Überladen des Makros für die Anzahl der Argumente

183

Ich habe zwei Makros FOO2und FOO3:

#define FOO2(x,y) ...
#define FOO3(x,y,z) ...

Ich möchte ein neues Makro FOOwie folgt definieren:

#define FOO(x,y) FOO2(x,y)
#define FOO(x,y,z) FOO3(x,y,z)

Dies funktioniert jedoch nicht, da Makros die Anzahl der Argumente nicht überladen.

Ohne Modifikation FOO2und FOO3ist es eine Möglichkeit , ein Makro zu definieren FOO(mit __VA_ARGS__oder auf andere Weise) , um die gleiche Wirkung des Versands erhalten FOO(x,y)zu FOO2, und FOO(x,y,z)zu FOO3?

Andrew Tomazos
quelle
1
Ich habe das starke Gefühl, dass dies schon mehrmals gefragt wurde ... [Update] zB hier .
Kerrek SB
1
@ KerrekSB: Das mag verwandt sein, muss es mit Sicherheit kein Betrüger sein.
Andrew Tomazos
Nein, vielleicht nicht das, aber so etwas
taucht
Gleiches gilt für C ++: stackoverflow.com/questions/3046889/… Sollte gleich sein, da die Präprozessoren grundsätzlich gleich sind: stackoverflow.com/questions/5085533/…
Ciro Santilli 20 冠状 病 六四 事件 法轮功
Siehe auch
Gabriel Staples

Antworten:

262

Einfach wie:

#define GET_MACRO(_1,_2,_3,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)

Wenn Sie also diese Makros haben:

FOO(World, !)         # expands to FOO2(World, !)
FOO(foo,bar,baz)      # expands to FOO3(foo,bar,baz)

Wenn Sie einen vierten wollen:

#define GET_MACRO(_1,_2,_3,_4,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO4, FOO3, FOO2)(__VA_ARGS__)

FOO(a,b,c,d)          # expeands to FOO4(a,b,c,d)

Natürlich, wenn Sie definieren FOO2, FOO3und FOO4wird die Ausgabe durch die der definierten Makros ersetzt werden.

Netcoder
quelle
5
@ Uroc327 Das Hinzufügen eines Makros mit 0 Argumenten zur Liste ist möglich, siehe meine Antwort.
Augurar
7
Funktioniert nicht unter Microsoft Visual Studio 2010, VA_ARGS scheint zu einem einzigen Makroargument erweitert zu sein.
Étienne
9
Fand diese Antwort , damit es unter MSVC 2010 funktioniert.
Étienne
8
Wenn jemand verwirrt ist, wie der EXPANDin @ Étiennes Link erwähnte verwendet werden soll, rufen Sie ihn im Grunde GET_MACROso auf #define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__))und er sollte auf die richtige Anzahl von Argumenten in msvc erweitert werden.
ärgerlich
3
Beachten Sie, dass unter C ++ 11 eine Warnung angezeigt wird : ISO C++11 requires at least one argument for the "..." in a variadic macro. Um dies zu beheben, fügen Sie nach dem letzten Parameter in der Definition von FOO (...) ein nicht verwendetes Argument (oder sogar nur ein Komma) hinzu: #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2, UNUSED)(__VA_ARGS__)( Siehe es läuft auf Coliru ).
Metall
49

Um die Antwort des Netzcodierers zu ergänzen, KÖNNEN Sie dies mit Hilfe der GCC- ##__VA_ARGS__Erweiterung mit einem Makro mit 0 Argumenten tun :

#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__)
Augurar
quelle
1
Ist es möglich, FOO1 und FOO2 zuzulassen, aber nicht FOO0, ohne dies zu tun #define FOO0 _Pragma("error FOO0 not allowed")?
noɥʇʎԀʎzɐɹƆ
FOO0Wenn der Anruf nicht in qt + mingw32 funktioniert, FOO0wird derFOO1
JustWe
Sehr vielversprechend und einfach. Funktioniert aber nicht für FOO0 mit -std = c ++ 11 ... :-(
leonp
1
Gleiches Problem, wenn Sie dies in C tun und versuchen, -std=c99oder zu verwenden -std=c11. Sie müssen verwenden -std=gnu99oder -std=gnu11stattdessen
Michael Mrozek
1
Es scheint, dass das Ersetzen _0, ##__VA_ARGS__durch _0 __VA_OPT__(,) __VA_ARGS__der neue Weg ist, dies zu tun.
Wrzlprmft
36

Hier ist eine allgemeinere Lösung:

// get number of arguments with __NARG__
#define __NARG__(...)  __NARG_I_(__VA_ARGS__,__RSEQ_N())
#define __NARG_I_(...) __ARG_N(__VA_ARGS__)
#define __ARG_N( \
      _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
     _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
     _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
     _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
     _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
     _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
     _61,_62,_63,N,...) N
#define __RSEQ_N() \
     63,62,61,60,                   \
     59,58,57,56,55,54,53,52,51,50, \
     49,48,47,46,45,44,43,42,41,40, \
     39,38,37,36,35,34,33,32,31,30, \
     29,28,27,26,25,24,23,22,21,20, \
     19,18,17,16,15,14,13,12,11,10, \
     9,8,7,6,5,4,3,2,1,0

// general definition for any function name
#define _VFUNC_(name, n) name##n
#define _VFUNC(name, n) _VFUNC_(name, n)
#define VFUNC(func, ...) _VFUNC(func, __NARG__(__VA_ARGS__)) (__VA_ARGS__)

// definition for FOO
#define FOO(...) VFUNC(FOO, __VA_ARGS__)

Definieren Sie Ihre Funktionen:

#define FOO2(x, y) ((x) + (y))
#define FOO3(x, y, z) ((x) + (y) + (z))

// it also works with C functions:
int FOO4(int a, int b, int c, int d) { return a + b + c + d; }

Jetzt können Sie FOOmit 2, 3 und 4 Argumenten verwenden:

FOO(42, 42) // will use makro function FOO2
FOO(42, 42, 42) // will use makro function FOO3
FOO(42, 42, 42, 42) // will call FOO4 function

Einschränkungen

  • Nur bis zu 63 Argumente (aber erweiterbar)
  • Funktion für kein Argument nur in GCC möglich

Ideen

Verwenden Sie es für Standardargumente:

#define func(...) VFUNC(func, __VA_ARGS__)
#define func2(a, b) func4(a, b, NULL, NULL)
#define func3(a, b, c) func4(a, b, c, NULL)

// real function:
int func4(int a, int b, void* c, void* d) { /* ... */ }

Verwenden Sie es für Funktionen mit einer möglichen unendlichen Anzahl von Argumenten:

#define SUM(...) VFUNC(SUM, __VA_ARGS__)
#define SUM2(a, b) ((a) + (b))
#define SUM3(a, b, c) ((a) + (b) + (c))
#define SUM4(a, b, c) ((a) + (b) + (c) + (d))
// ...

PS: __NARG__wird von Laurent Deniau & Roland Illig hier kopiert: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1

R1tschY
quelle
Siehe auch
Gabriel Staples
Auch dies: stackoverflow.com/questions/2124339/…
Gabriel Staples
Das Makro __NARG_I_erscheint völlig unnötig und überflüssig. Es fügt nur einen zusätzlichen Schritt und Verwirrung hinzu. Ich empfehle, es vollständig zu löschen und __NARG__stattdessen nur zu definieren als : #define __NARG__(...) __ARG_N(__VA_ARGS__,__RSEQ_N()).
Gabriel Staples
Oder wird das die Vorverarbeitung irgendwie unterbrechen? Vermisse ich etwas
Gabriel Staples
Gleiches gilt für _VFUNC_: einfach löschen. Definieren Sie dann _VFUNCals: #define _VFUNC(name, n) name##nanstelle von #define _VFUNC(name, n) _VFUNC_(name, n).
Gabriel Staples
15

Ich habe das nur selbst recherchiert und bin hier darauf gestoßen . Der Autor hat die Standardargumentunterstützung für C-Funktionen über Makros hinzugefügt.

Ich werde versuchen, den Artikel kurz zusammenzufassen. Grundsätzlich müssen Sie ein Makro definieren, das Argumente zählen kann. Dieses Makro gibt 2, 1, 0 oder einen beliebigen Bereich von Argumenten zurück, die es unterstützen kann. Z.B:

#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)

Dazu müssen Sie ein weiteres Makro erstellen, das eine variable Anzahl von Argumenten akzeptiert, die Argumente zählt und das entsprechende Makro aufruft. Ich habe Ihr Beispielmakro genommen und es mit dem Beispiel des Artikels kombiniert. Ich habe die FOO1-Aufruffunktion a () und die FOO2-Aufruffunktion a mit Argument b (natürlich gehe ich hier von C ++ aus, aber Sie können das Makro auf was auch immer ändern).

#define FOO1(a) a();
#define FOO2(a,b) a(b);

#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)

#define _ONE_OR_TWO_ARGS_1(a) FOO1(a)
#define _ONE_OR_TWO_ARGS_2(a, b) FOO2(a,b)

#define __ONE_OR_TWO_ARGS(N, ...) _ONE_OR_TWO_ARGS_ ## N (__VA_ARGS__)
#define _ONE_OR_TWO_ARGS(N, ...) __ONE_OR_TWO_ARGS(N, __VA_ARGS__)

#define FOO(...) _ONE_OR_TWO_ARGS(NARG2(__VA_ARGS__), __VA_ARGS__)

Also wenn du hast

FOO(a)
FOO(a,b)

Der Präprozessor erweitert das auf

a();
a(b);

Ich würde definitiv den Artikel lesen, den ich verlinkt habe. Es ist sehr informativ und er erwähnt, dass NARG2 nicht mit leeren Argumenten funktioniert. Er folgt dieser bis hier .

lhumongous
quelle
7

Hier ist eine kompaktere Version der obigen Antwort . Mit Beispiel.

#include <iostream>
using namespace std;

#define OVERLOADED_MACRO(M, ...) _OVR(M, _COUNT_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define _OVR(macroName, number_of_args)   _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args)    macroName##number_of_args

#define _COUNT_ARGS(...)  _ARG_PATTERN_MATCH(__VA_ARGS__, 9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9, N, ...)   N


//Example:
#define ff(...)     OVERLOADED_MACRO(ff, __VA_ARGS__)
#define ii(...)     OVERLOADED_MACRO(ii, __VA_ARGS__)

#define ff3(c, a, b) for (int c = int(a); c < int(b); ++c)
#define ff2(c, b)   ff3(c, 0, b)

#define ii2(a, b)   ff3(i, a, b)
#define ii1(n)      ii2(0, n)


int main() {
    ff (counter, 3, 5)
        cout << "counter = " << counter << endl;
    ff (abc, 4)
        cout << "abc = " << abc << endl;
    ii (3)
        cout << "i = " << i << endl;
    ii (100, 103)
        cout << "i = " << i << endl;


    return 0;
}

Lauf:

User@Table 13:06:16 /c/T
$ g++ test_overloaded_macros.cpp 

User@Table 13:16:26 /c/T
$ ./a.exe
counter = 3
counter = 4
abc = 0
abc = 1
abc = 2
abc = 3
i = 0
i = 1
i = 2
i = 100
i = 101
i = 102

Beachten Sie, dass beides vorhanden ist _OVRund _OVR_EXPANDmöglicherweise redundant aussieht. Der Präprozessor muss jedoch das _COUNT_ARGS(__VA_ARGS__)Teil erweitern, das ansonsten als Zeichenfolge behandelt wird.

Evgeni Sergeev
quelle
Ich mag diese Lösung. Kann es geändert werden, um ein überladenes Makro zu behandeln, das keine Argumente akzeptiert?
Andrew
3

Vielleicht können Sie dieses Makro verwenden, um die Anzahl der Argumente zu zählen .

#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5,4,3,2,1)
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...) N
Augen
quelle
2

Hier ist ein Spin-off von Evgeni Sergeevs Antwort. Dieser unterstützt auch keine Argumentüberladungen !

Ich habe dies mit GCC und MinGW getestet. Es sollte mit alten und neuen Versionen von C ++ funktionieren. Beachten Sie, dass ich es für MSVC nicht garantieren würde ... Aber mit einigen Optimierungen bin ich zuversichtlich, dass es auch damit funktionieren könnte.

Ich habe dies auch so formatiert, dass es in eine Header-Datei eingefügt wird (die ich macroutil.h genannt habe). Wenn Sie dies tun, können Sie diesen Header einfach einfügen, was auch immer Sie für die Funktion benötigen, und nicht auf die Unangenehmkeit achten, die mit der Implementierung verbunden ist.

#ifndef MACROUTIL_H
#define MACROUTIL_H

//-----------------------------------------------------------------------------
// OVERLOADED_MACRO
//
// used to create other macros with overloaded argument lists
//
// Example Use:
// #define myMacro(...) OVERLOADED_MACRO( myMacro, __VA_ARGS__ )
// #define myMacro0() someFunc()
// #define myMacro1( arg1 ) someFunc( arg1 )
// #define myMacro2( arg1, arg2 ) someFunc( arg1, arg2 )
//
// myMacro();
// myMacro(1);
// myMacro(1,2);
//
// Note the numerical suffix on the macro names,
// which indicates the number of arguments.
// That is the REQUIRED naming convention for your macros.
//
//-----------------------------------------------------------------------------

// OVERLOADED_MACRO
// derived from: /programming/11761703/overloading-macro-on-number-of-arguments
// replaced use of _COUNT_ARGS macro with VA_NUM_ARGS defined below
// to support of zero argument overloads
#define OVERLOADED_MACRO(M, ...) _OVR(M, VA_NUM_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define _OVR(macroName, number_of_args)   _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args)    macroName##number_of_args
//#define _COUNT_ARGS(...)  _ARG_PATTERN_MATCH(__VA_ARGS__, 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15, N, ...)   N

// VA_NUM_ARGS
// copied from comments section of:
// http://efesx.com/2010/07/17/variadic-macro-to-count-number-of-arguments/
// which itself was derived from:
// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define HAS_NO_COMMA(...) _ARG16(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
#define _TRIGGER_PARENTHESIS_(...) ,

#define HAS_ZERO_OR_ONE_ARGS(...) \
    _HAS_ZERO_OR_ONE_ARGS( \
    /* test if there is just one argument, eventually an empty one */ \
    HAS_COMMA(__VA_ARGS__), \
    /* test if _TRIGGER_PARENTHESIS_ together with the argument adds a comma */ \
    HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
    /* test if the argument together with a parenthesis adds a comma */ \
    HAS_COMMA(__VA_ARGS__ (~)), \
    /* test if placing it between _TRIGGER_PARENTHESIS_ and the parenthesis adds a comma */ \
    HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (~)) \
    )

#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _HAS_ZERO_OR_ONE_ARGS(_0, _1, _2, _3) HAS_NO_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,

#define _VA0(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA1(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA2(...) 2
#define _VA3(...) 3
#define _VA4(...) 4
#define _VA5(...) 5
#define _VA6(...) 6
#define _VA7(...) 7
#define _VA8(...) 8
#define _VA9(...) 9
#define _VA10(...) 10
#define _VA11(...) 11
#define _VA12(...) 12
#define _VA13(...) 13
#define _VA14(...) 14
#define _VA15(...) 15
#define _VA16(...) 16

#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, PP_RSEQ_N(__VA_ARGS__) )
#define VA_NUM_ARGS_IMPL(...) VA_NUM_ARGS_N(__VA_ARGS__)

#define VA_NUM_ARGS_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
    _11,_12,_13,_14,_15,_16,N,...) N

#define PP_RSEQ_N(...) \
    _VA16(__VA_ARGS__),_VA15(__VA_ARGS__),_VA14(__VA_ARGS__),_VA13(__VA_ARGS__), \
    _VA12(__VA_ARGS__),_VA11(__VA_ARGS__),_VA10(__VA_ARGS__), _VA9(__VA_ARGS__), \
    _VA8(__VA_ARGS__),_VA7(__VA_ARGS__),_VA6(__VA_ARGS__),_VA5(__VA_ARGS__), \
    _VA4(__VA_ARGS__),_VA3(__VA_ARGS__),_VA2(__VA_ARGS__),_VA1(__VA_ARGS__), \
    _VA0(__VA_ARGS__)

//-----------------------------------------------------------------------------

#endif // MACROUTIL_H
BuvinJ
quelle
2

Dies scheint bei GCC, Clang und MSVC gut zu funktionieren. Es ist eine bereinigte Version einiger der Antworten hier

#define _my_BUGFX(x) x

#define _my_NARG2(...) _my_BUGFX(_my_NARG1(__VA_ARGS__,_my_RSEQN()))
#define _my_NARG1(...) _my_BUGFX(_my_ARGSN(__VA_ARGS__))
#define _my_ARGSN(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
#define _my_RSEQN() 10,9,8,7,6,5,4,3,2,1,0

#define _my_FUNC2(name,n) name ## n
#define _my_FUNC1(name,n) _my_FUNC2(name,n)
#define GET_MACRO(func,...) _my_FUNC1(func,_my_BUGFX(_my_NARG2(__VA_ARGS__))) (__VA_ARGS__)

#define FOO(...) GET_MACRO(FOO,__VA_ARGS__)
Rian Quinn
quelle
1
@RianQuinn Wie kann man dieses Makro so anpassen, dass es mit dem Argument Null funktioniert #define func0() foo? Die aktuelle Version behandelt diesen Fall leider nicht.
Jerry Ma