Überladen des Makros bei der Anzahl der Argumente

Lesezeit: 11 Minuten

Benutzeravatar von Andrew Tomazos
Andreas Tomassos

Ich habe zwei Makros FOO2 und FOO3:

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

Ich möchte ein neues Makro definieren FOO folgendermaßen:

#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 zu modifizieren FOO2 und FOO3gibt es eine Möglichkeit, ein Makro zu definieren FOO (unter Verwendung __VA_ARGS__ oder auf andere Weise), um den gleichen Versandeffekt zu erzielen FOO(x,y) zu FOO2und FOO(x,y,z) zu FOO3?

  • Ich habe das sehr starke Gefühl, dass dies schon mehrmals gefragt wurde … [update] zB hier.

    – Kerrek SB

    1. August 2012 um 14:45 Uhr


  • @KerrekSB: Das mag zusammenhängen, muss es ganz sicher kein Betrüger sein.

    – Andreas Tomazos

    1. August 2012 um 15:16 Uhr

  • Nein, das vielleicht nicht, aber so etwas kommt ungefähr einmal im Monat vor…

    – Kerrek SB

    1. August 2012 um 15:18 Uhr

  • Gleiches für C++: stackoverflow.com/questions/3046889/… Sollte gleich sein, da die Präprozessoren im Grunde gleich sind: stackoverflow.com/questions/5085533/…

    – Ciro Santilli OurBigBook.com

    20. Juni 2015 um 22:03 Uhr

  • Siehe auch: stackoverflow.com/questions/11317474/…

    – Gabriel Staples

    5. März 2020 um 20:32 Uhr

Benutzeravatar von netcoder
Netcoder

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, werden sie wie beschrieben erweitert:

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)          // expands to FOO4(a,b,c,d)

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

  • @ Uroc327 Das Hinzufügen eines Makros mit 0 Argumenten zur Liste ist möglich, siehe meine Antwort.

    – Augurar

    27. Januar 2014 um 0:59 Uhr

  • Funktioniert nicht auf Microsoft Visual Studio 2010, VA_ARGS scheint zu einem einzigen Makroargument erweitert zu werden.

    – Etienne

    28. Oktober 2014 um 11:32 Uhr

  • Habe diese Antwort gefunden, damit es unter MSVC 2010 funktioniert.

    – Etienne

    28. Oktober 2014 um 14:14 Uhr

  • Wenn jemand verwirrt ist, wie man das benutzt EXPAND in @Étiennes Link erwähnt, rufen Sie ihn im Grunde auf GET_MACRO wie so #define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__)) und es sollte auf die richtige Anzahl von Argumenten in msvc erweitert werden.

    – Ärger

    18. September 2015 um 16:01 Uhr

  • Beachten Sie, dass Sie unter C++11 eine Warnung erhalten: ISO C++11 requires at least one argument for the "..." in a variadic macro. Um dies zu beheben, fügen Sie ein unbenutztes Argument (oder auch nur ein Komma) nach dem letzten Parameter in der Definition von FOO(…) hinzu: #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2, UNUSED)(__VA_ARGS__) (Sehen Sie es auf Coliru laufen).

    – Metall

    12. April 2017 um 12:26 Uhr


Benutzeravatar von augurar
Augurar

Um die Antwort von netcoder zu ergänzen, KÖNNEN Sie dies tatsächlich mit einem 0-Argument-Makro mit Hilfe des GCC tun ##__VA_ARGS__ Verlängerung:

#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__)

  • ist es möglich, FOO1 und FOO2 zuzulassen, aber nicht FOO0, ohne dies zu tun #define FOO0 _Pragma("error FOO0 not allowed")?

    – noɥʇʎʀʎzɐɹƆ

    24. Juli 2017 um 22:24 Uhr

  • FOO0 funktioniert nicht in qt + mingw32, rufen Sie an FOO0 wird die aufrufen FOO1

    – Nur wir

    8. März 2018 um 6:35 Uhr


  • Sehr vielversprechend und einfach. Funktioniert aber nicht für FOO0 mit -std=c++11…:-(

    – leonp

    9. August 2019 um 18:53 Uhr

  • Dasselbe Problem, wenn Sie dies in C tun und versuchen, es zu verwenden -std=c99 oder -std=c11. Sie müssen verwenden -std=gnu99 oder -std=gnu11 stattdessen

    – Michael Mrozek

    11. Februar 2020 um 4:45 Uhr

  • Es scheint, dass das Ersetzen _0, ##__VA_ARGS__ mit _0 __VA_OPT__(,) __VA_ARGS__ ist der neue Weg, dies zu tun.

    – Wrzlprmft

    1. April 2020 um 8:01 Uhr

Benutzeravatar von R1tschY
R1tschY

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 verwenden FOO mit 2, 3 und 4 Argumenten:

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 möglicherweise unendlich vielen 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__ ist hier von Laurent Deniau & Roland Illig kopiert: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1

  • Siehe auch: stackoverflow.com/questions/11317474/…

    – Gabriel Staples

    5. März 2020 um 20:32 Uhr

  • Auch dieses hier: stackoverflow.com/questions/2124339/…

    – Gabriel Staples

    5. März 2020 um 20:40 Uhr

  • 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 nur zu definieren __NARG__ stattdessen als: #define __NARG__(...) __ARG_N(__VA_ARGS__,__RSEQ_N()).

    – Gabriel Staples

    26. Mai 2020 um 22:51 Uhr


  • Oder wird das irgendwie die Vorverarbeitung unterbrechen? Übersehe ich etwas?

    – Gabriel Staples

    26. Mai 2020 um 22:55 Uhr

  • Das gleiche mit _VFUNC_: einfach löschen. Dann definiere _VFUNC wie: #define _VFUNC(name, n) name##n Anstatt von #define _VFUNC(name, n) _VFUNC_(name, n).

    – Gabriel Staples

    26. Mai 2020 um 22:58 Uhr

Ich habe gerade selbst recherchiert und bin darauf gestoßen hier. Der Autor hat Unterstützung für Standardargumente für C-Funktionen über Makros hinzugefügt.

Ich versuche 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, den 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 FOO1-Aufruffunktion a() und FOO2-Aufruffunktion a mit Argument b (offensichtlich gehe ich hier von C++ aus, aber Sie können das Makro beliebig ä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 Sie haben

FOO(a)
FOO(a,b)

Der Präprozessor erweitert das zu

a();
a(b);

Den von mir verlinkten Artikel würde ich auf jeden Fall lesen. Es ist sehr informativ und er erwähnt, dass NARG2 nicht mit leeren Argumenten funktioniert. Dem geht er nach hier.

Benutzeravatar von Evgeni Sergeev
Jewgeni Sergejew

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;
}

Laufen:

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 Sie beides haben _OVR und _OVR_EXPAND mag überflüssig aussehen, aber es ist notwendig, dass der Präprozessor die erweitert _COUNT_ARGS(__VA_ARGS__) Teil, der ansonsten als String behandelt wird.

  • Ich mag diese Lösung. Kann es geändert werden, um ein überladenes Makro zu handhaben, das null Argumente akzeptiert?

    – Andreas

    21. September 2018 um 1:08 Uhr

Benutzeravatar von BuvinJ
BuvinJ

Hier ist ein Ableger von Evgeni Sergeevs Antwort. Dieser unterstützt null Argumentüberladungen auch!

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 das tun, können Sie einfach diesen Header einfügen, was auch immer Sie für die Funktion benötigen, und müssen sich nicht um die mit der Implementierung verbundenen Unannehmlichkeiten kümmern.

#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: https://stackoverflow.com/questions/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

  • Ich mag diese Lösung. Kann es geändert werden, um ein überladenes Makro zu handhaben, das null Argumente akzeptiert?

    – Andreas

    21. September 2018 um 1:08 Uhr

Benutzeravatar von eyalm
eyalm

Vielleicht kannst du dieses Makro dazu verwenden zählen Sie die Anzahl der Argumente.

#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

1426080cookie-checkÜberladen des Makros bei der Anzahl der Argumente

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy