Przeciążenie makra na liczbę argumentów

183

Mam dwa makra FOO2i FOO3:

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

Chcę zdefiniować nowe makro FOOw następujący sposób:

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

Ale to nie działa, ponieważ makra nie przeciążają wielu argumentów.

Bez modyfikacji FOO2i FOO3czy istnieje jakiś sposób zdefiniowania makra FOO(przy użyciu __VA_ARGS__lub w inny sposób), aby uzyskać taki sam efekt wysyłania FOO(x,y)do FOO2i FOO(x,y,z)do FOO3?

Andrew Tomazos
źródło
1
Mam bardzo silne przeczucie, że pytano o to kilka razy przed ... [aktualizacja] np . Tutaj .
Kerrek SB
1
@KerrekSB: To może być powiązane, z pewnością nie jest to dupek.
Andrew Tomazos,
Nie, może nie ten, ale coś takiego pojawia się raz w miesiącu ...
Kerrek SB
To samo dla C ++: stackoverflow.com/questions/3046889/... Powinny być takie same, ponieważ preprocesory są w zasadzie takie same: stackoverflow.com/questions/5085533/...
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Powiązane: stackoverflow.com/questions/11317474/…
Gabriel Staples

Odpowiedzi:

264

Proste jak:

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

Więc jeśli masz te makra:

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

Jeśli chcesz czwarty:

#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)

Oczywiście, jeśli zdefiniujesz FOO2, FOO3a FOO4dane wyjściowe zostaną zastąpione danymi z zdefiniowanych makr.

netcoder
źródło
5
@ Uroc327 Możliwe jest dodanie makra 0-argumentowego do listy, patrz moja odpowiedź.
sierpień
7
Nie działa w Microsoft Visual Studio 2010, VA_ARGS wydaje się być rozwinięty do pojedynczego argumentu makra.
Étienne
9
Znalazłem tę odpowiedź, aby działała pod MSVC 2010.
Étienne
8
Jeśli ktoś jest zdezorientowany, jak używać EXPANDwspomnianego w linku @ Étienne, w zasadzie wywołujesz go w ten GET_MACROsposób #define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__))i powinien on rozwinąć się do odpowiedniej liczby argumentów w msvc.
vexe
3
Zauważ, że na C ++ 11, dostaniesz ostrzeżenie: ISO C++11 requires at least one argument for the "..." in a variadic macro. Aby to naprawić, dodaj nieużywany argument (lub nawet przecinek) po ostatnim parametrze w definicji FOO (...): #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2, UNUSED)(__VA_ARGS__)( Zobacz, jak działa na Coliru ).
metal
49

Aby dodać odpowiedź netcodera , MOŻESZ to zrobić za pomocą makra 0-argumentowego za pomocą ##__VA_ARGS__rozszerzenia GCC :

#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__)
augurar
źródło
1
czy możliwe jest zezwolenie FOO1 i FOO2, ale nie FOO0 bez robienia tego #define FOO0 _Pragma("error FOO0 not allowed")?
noɥʇʎԀʎzɐɹƆ
FOO0nie działa w qt + mingw32, wywołanie FOO0wywołaFOO1
JustWe
Bardzo obiecujące i proste. Ale nie działa dla FOO0 z -std = c ++ 11 ... :-(
leonp
1
Ten sam problem, jeśli robisz to w C i próbujesz użyć -std=c99lub -std=c11. Musisz użyć -std=gnu99lub -std=gnu11zamiast
Michael Mrozek
1
Wydaje się, że zastąpienie _0, ##__VA_ARGS__go _0 __VA_OPT__(,) __VA_ARGS__to nowy sposób na zrobienie tego.
Wrzlprmft
36

Oto bardziej ogólne rozwiązanie:

// 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__)

Zdefiniuj swoje funkcje:

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

Teraz możesz używać FOOz argumentami 2, 3 i 4:

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

Ograniczenia

  • Tylko do 63 argumentów (ale z możliwością rozszerzenia)
  • Możliwa funkcja bez argumentu tylko w GCC

Pomysły

Użyj go jako domyślnych argumentów:

#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) { /* ... */ }

Użyj go do funkcji z możliwą nieskończoną liczbą argumentów:

#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__jest kopiowany z Laurenta Deniau i Rolanda Illiga tutaj: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1

R1tschY
źródło
Powiązane: stackoverflow.com/questions/11317474/…
Gabriel Staples
Ten też: stackoverflow.com/questions/2124339/...
Gabriel Staples
Makro __NARG_I_wydaje się całkowicie niepotrzebne i zbędne. To tylko dodatkowy krok i zamieszanie. Polecam usuwania go całkowicie i po prostu definiowanie __NARG__zamiast jak: #define __NARG__(...) __ARG_N(__VA_ARGS__,__RSEQ_N()).
Gabriel Staples
A może w jakiś sposób przerwie to wstępne przetwarzanie? Czy coś brakuje?
Gabriel Staples
To samo z _VFUNC_: po prostu go usuń. Następnie zdefiniuj _VFUNCjako: #define _VFUNC(name, n) name##nzamiast #define _VFUNC(name, n) _VFUNC_(name, n).
Gabriel Staples
15

Właśnie to badałem i natknąłem się na to tutaj . Autor dodał domyślną obsługę argumentów funkcji C za pomocą makr.

Spróbuję krótko streścić artykuł. Zasadniczo musisz zdefiniować makro, które może liczyć argumenty. To makro zwróci 2, 1, 0 lub dowolny zakres obsługiwanych argumentów. Na przykład:

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

Dzięki temu musisz utworzyć kolejne makro, które pobierze zmienną liczbę argumentów, zlicza argumenty i wywołuje odpowiednie makro. Wziąłem twoje makro makra i połączyłem je z przykładem artykułu. Mam funkcję wywołania FOO1 a () i funkcję wywołania FOO2 a z argumentem b (oczywiście zakładam, że C ++ tutaj, ale możesz zmienić makro na cokolwiek).

#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__)

Więc jeśli masz

FOO(a)
FOO(a,b)

Preprocesor rozszerza to do

a();
a(b);

Na pewno przeczytałbym link do tego artykułu. Jest to bardzo pouczające i wspomina, że ​​NARG2 nie będzie działać na pustych argumentach. Śledzi to tutaj .

hhongongous
źródło
7

Oto bardziej zwarta wersja powyższej odpowiedzi . Z przykładem

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

Biegać:

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

Zauważ, że posiadanie obu _OVRi _OVR_EXPANDmoże wyglądać na zbędne, ale konieczne jest, aby preprocesor rozwinął _COUNT_ARGS(__VA_ARGS__)część, która w przeciwnym razie jest traktowana jako ciąg.

Jewgienij Siergiejew
źródło
Podoba mi się to rozwiązanie. Czy można go zmodyfikować, aby obsługiwał przeciążone makro, które przyjmuje zerowe argumenty?
Andrew
3

Być może możesz użyć tego makra, aby policzyć liczbę argumentów .

#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
eyalm
źródło
2

Oto część odpowiedzi Evgeni Siergiejewa. Ten obsługuje również zerowe przeciążenia argumentów !

Testowałem to z GCC i MinGW. Powinien współpracować ze starymi i nowymi wersjami C ++. Zauważ, że nie gwarantowałbym tego w przypadku MSVC ... Ale z pewnymi poprawkami, jestem pewien, że można by do tego nadawać.

Sformatowałem to również, aby wkleić do pliku nagłówka (który nazwałem macroutil.h). Jeśli to zrobisz, możesz po prostu dołączyć ten nagłówek, czego potrzebujesz, i nie patrzeć na nieprzyjemności związane z implementacją.

#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
źródło
2

Wydaje się, że działa to dobrze na GCC, Clang i MSVC. To oczyszczona wersja niektórych odpowiedzi tutaj

#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
źródło
1
@RianQuinn Jak dostosować to makro, aby działało z zerowym argumentem #define func0() foo? Obecna wersja niestety nie obsługuje tego przypadku.
Jerry Ma