Overloading Macro on Number of Arguments
我有两个宏:
1 2 | #define FOO2(x,y) ... #define FOO3(x,y,z) ... |
我想定义一个新的宏
1 2 | #define FOO(x,y) FOO2(x,y) #define FOO(x,y,z) FOO3(x,y,z) |
但这不起作用,因为宏不会重载参数的数量。
在不修改
简单为:
1 2 | #define GET_MACRO(_1,_2,_3,NAME,...) NAME #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__) |
因此,如果您有这些宏:
1 2 | FOO(World, !) # expands to FOO2(World, !) FOO(foo,bar,baz) # expands to FOO3(foo,bar,baz) |
如果你想要第四个:
1 2 3 4 | #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) |
当然,如果定义
为了补充Netcoder的答案,实际上,您可以使用0参数宏,在gcc
1 2 | #define GET_MACRO(_0, _1, _2, NAME, ...) NAME #define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__) |
下面是一个更通用的解决方案:
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 | // 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__) |
定义函数:
1 2 3 4 5 | #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; } |
现在您可以将
1 2 3 | 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中的无参数
思想
将其用于默认参数:
1 2 3 4 5 6 | #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) { /* ... */ } |
对于参数可能无穷多的函数使用它:
1 2 3 4 5 | #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:
我只是自己研究这个,我在这里遇到了这个。作者通过宏为C函数添加了默认参数支持。
我将试着简单地总结一下这篇文章。基本上,您需要定义一个可以计算参数的宏。此宏将返回2、1、0或它可以支持的任何参数范围。如:
1 2 | #define _ARG2(_0, _1, _2, ...) _2 #define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0) |
使用此方法,您需要创建另一个宏,该宏接受可变数量的参数,对参数进行计数,并调用相应的宏。我取了您的示例宏,并将其与本文的示例结合起来。我有Foo1调用函数A()和FoO2调用函数A带有参数B(很明显,我假设这里是C++,但是你可以把宏变成任意的)。
1 2 3 4 5 6 7 8 9 10 11 12 13 | #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__) |
如果你有
1 2 | FOO(a) FOO(a,b) |
预处理器将其扩展为
1 2 | a(); a(b); |
我肯定会读我链接的文章。这是非常有信息性的,他提到NARG2不会处理空论点。他在这里跟踪这件事。
下面是上面答案的一个更紧凑的版本。举个例子。
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 | #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; } |
运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
注意,拥有
也许您可以使用这个宏来计算参数的数目。
1 2 | #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)中。如果这样做,您只需包含这个头部,无论您需要什么特性,而不必考虑实现中涉及到的麻烦。
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | #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上似乎很管用。这是这里一些答案的清理版本
1 2 3 4 5 6 7 8 9 10 | #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__) |