Перегрузка макроса по количеству аргументов

У меня есть два макроса FOO2 и FOO3 :

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

Я хочу определить новый макрос FOO следующим образом:

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

Но это не работает, потому что macros не перегружают количество аргументов.

Без изменения FOO2 и FOO3 существует ли способ определить макрос FOO (используя __VA_ARGS__ или иначе), чтобы получить тот же эффект от отправки FOO(x,y) в FOO2 и FOO(x,y,z) в FOO3 ?

Просто:

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

Итак, если у вас есть эти macros:

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

Если вы хотите четвертый:

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

Естественно, если вы определите FOO2 , FOO3 и FOO4 , то результат будет заменен на те из определенных макросов.

Чтобы добавить ответ netcoder , вы можете сделать это с помощью макроса с 0 аргументами с помощью расширения GCC ##__VA_ARGS__ :

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

Вот более общее решение:

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

Определите свои функции:

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

Теперь вы можете использовать FOO с аргументами 2, 3 и 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 

Ограничения

  • Только до 63 аргументов (но расширяемых)
  • Функция без аргументов только в GCC возможна

идеи

Используйте его для аргументов по умолчанию:

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

Используйте его для функций с возможным бесконечным числом аргументов:

 #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__ копируется с: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1

Я просто изучал это сам, и я наткнулся на это здесь . Автор добавил поддержку аргументов по умолчанию для функций C через macros.

Я попытаюсь кратко обобщить статью. В принципе, вам нужно определить макрос, который может подсчитывать аргументы. Этот макрос вернет 2, 1, 0 или любой диапазон аргументов, которые он может поддерживать. Например:

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

При этом вам нужно создать еще один макрос, который принимает переменное количество аргументов, подсчитывает аргументы и вызывает соответствующий макрос. Я взял ваш пример макроса и объединил его с примером статьи. У меня есть функция вызова FOO1 a () и функция вызова FOO2 a с аргументом b (очевидно, я предполагаю C ++ здесь, но вы можете изменить макрос на что угодно).

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

Поэтому, если у вас есть

 FOO(a) FOO(a,b) 

Препроцессор расширяет это значение до

 a(); a(b); 

Я бы определенно прочитал статью, которую я связал. Это очень информативно, и он упоминает, что NARG2 не будет работать над пустым аргументом. Он следует за этим здесь .

Вот более компактная версия ответа выше . Пример.

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

Бег:

 [email protected] 13:06:16 /c/T $ g++ test_overloaded_macros.cpp [email protected] 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 

Обратите внимание, что наличие как _OVR и _OVR_EXPAND может выглядеть избыточным, но для препроцессора необходимо расширить часть _COUNT_ARGS(__VA_ARGS__) , которая в противном случае обрабатывается как строка.

Возможно, вы можете использовать этот макрос для подсчета количества аргументов .

 #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 

Вот от ответа Евгения Сергеева. Это также поддерживает перегрузку нулевого аргумента !

Я тестировал это с помощью GCC и MinGW. Он должен работать со старыми и новыми версиями C ++. Заметьте, что я бы не гарантировал это для MSVC … Но с некоторой настройкой, я уверен, что это может быть сделано для работы с этим тоже.

Я также отформатировал это для вставки в файл заголовка (который я назвал macroutil.h). Если вы это сделаете, вы можете просто включить этот заголовок, что бы вам ни понадобилось, и не смотреть на гадость, вовлеченную в реализацию.

 #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 

Это, похоже, отлично работает на GCC, Clang и MSVC. Некоторые из ответов здесь вычищены.

 #define __BUGFX(x) x #define __NARG2(...) __BUGFX(__NARG1(__VA_ARGS__,__RSEQN())) #define __NARG1(...) __BUGFX(__ARGSN(__VA_ARGS__)) #define __ARGSN(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N #define __RSEQN() 10,9,8,7,6,5,4,3,2,1,0 #define __FUNC2(name,n) name ## n #define __FUNC1(name,n) __FUNC2(name,n) #define GET_MACRO(func,...) __FUNC1(func,__BUGFX(__NARG2(__VA_ARGS__))) (__VA_ARGS__) 
  • Как я могу генерировать уникальные значения в препроцессоре C?
  • Использование X-Macros в реальном мире
  • Как большой может быть malloc в C?
  • Несовместимые типы указателей, проходящие в макрос _Generic
  • Макрос формата C ++ / встроенный ostringstream
  • Можем ли мы иметь рекурсивные macros?
  • Давайте будем гением компьютера.