C 在参数数量上重载宏
我有两个宏C 在参数数量上重载宏,c,macros,c-preprocessor,C,Macros,C Preprocessor,我有两个宏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) 但这不起作用,因为宏不会在参数数量上过载 在不修改FOO2和FOO3的情况下,是否有办法定义一个宏FOO(使用\uuuuu-VA\u-ARGS\uuuuuu或其他方式)以获得与将FOO(x,y)分派到FOO2和FOO(
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)
但这不起作用,因为宏不会在参数数量上过载
在不修改
FOO2
和FOO3
的情况下,是否有办法定义一个宏FOO
(使用\uuuuu-VA\u-ARGS\uuuuuu
或其他方式)以获得与将FOO(x,y)
分派到FOO2
和FOO(x,y,z)
分派到FOO3
相同的效果
我自己也在研究这个,我发现了这个。作者通过宏为C函数添加了默认参数支持 我将简要地总结一下这篇文章。基本上,您需要定义一个可以计算参数的宏。此宏将返回2、1、0或它可以支持的任何参数范围。例如:
#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)
这样,您需要创建另一个宏,该宏接受可变数量的参数,对参数进行计数,并调用相应的宏。我已经将您的示例宏与本文的示例结合起来。我有Foo1调用函数A(),FoO2调用函数A带有参数B(很明显,我假设这里是C++,但是你可以把宏变成任意的)。
所以如果你有
FOO(a)
FOO(a,b)
预处理器将其扩展为
a();
a(b);
我肯定会读我链接的文章。这是一篇非常翔实的文章,他提到NARG2不会处理空参数。他接着说。简单如下:
#define GET_MACRO(_1,_2,_3,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)
因此,如果您有这些宏:
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
,则输出将被定义的宏的输出所取代。要添加到,您实际上可以在GCC扩展名的帮助下,使用0参数宏来执行此操作:
#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; }
现在,您可以对2、3和4个参数使用FOO
:
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
#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))
// ...
局限性
- 最多63个参数(但可扩展)
- 函数仅在GCC中不允许有参数
思想
将其用于默认参数:
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
#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))
// ...
将其用于可能具有无限多个参数的函数:
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
#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:\uuuu NARG\uuuuu
是从Laurent Deniau&Roland Illig这里复制的:这里是一个更紧凑的版本。举个例子
#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;
}
请注意,同时拥有\u OVR
和\u OVR\u EXPAND
可能看起来是多余的,但预处理器有必要扩展\u COUNT\u ARGS(\uuu VA\u ARGS\uuuu)
部分,否则将其视为字符串。这在GCC、Clang和MSVC上似乎可以正常工作。这是一个清理版的一些答案在这里
#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__)
这是从叶甫根尼·谢尔盖夫的回答中衍生出来的。这个函数也支持零参数重载
我用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
我有一个非常强烈的感觉,这已经被问了好几次了。。。[更新]例如:@KerrekSB:这可能是相关的,肯定不是重复。不,可能不是那个,但这样的事情大约每月出现一次……对于C++:应该是一样的,因为预处理器基本相同:相关:@Uroc327向列表中添加一个0参数宏是可能的,查看我的答案。在Microsoft Visual Studio 2010上不起作用,VA_ARGS似乎被扩展为一个宏参数。在MSVC 2010下可以使用。如果有人对如何使用@ea tienne链接中提到的EXPAND
感到困惑,基本上你可以在GET_macro
上调用它,就像\define FOO(…)EXPAND一样(GET_MACRO(uu VA_ARGS_uu,FOO3,FOO2,FOO1)(u VA_ARGS_uu))
并且它应该在msvc中扩展到正确的参数数量。注意,在C++11上,您会得到一个警告:ISO C++11要求变量宏中的“…”至少有一个参数。
若要解决此问题,请添加一个未使用的参数(甚至只添加一个逗号)在FOO(…)定义中的最后一个参数之后:#define FOO(…)GET_MACRO(u VA_ARGS,FOO3,FOO2,UNUSED)(u VA_ARGS_uu)
()。不执行#define FOO0(“错误FOO0不允许”)是否可以允许FOO1和FOO2而不允许FOO0
?FOO0
不在qt+mingw32中工作,调用FOO0
将调用FOO1
非常有前途且简单。但是在-std=c++11的情况下,对FOO0不起作用…:-(如果您在C中执行此操作,并且尝试使用-std=c99
或-std=c11
,则会出现相同的问题。您需要使用-std=gnu99
或-std=gnu11
代替标题,将\u0、\u35; uva_u参数
替换为\u0\uva_u选项(,)__VA_ARGS__
是实现这一点的新方法。我喜欢这个解决方案。可以修改它来处理采用零参数的重载宏吗?@rianquin如何调整此宏,使其能够与零参数一起工作#define func0()foo
?不幸的是,当前版本没有处理这种情况。相关:这一个:宏\uuNarg\uI
似乎完全不必要和多余。它只是增加了一个额外的步骤和混乱。我建议将其完全删除,只定义\uNarg\uuUg
,而不是:\define\uNarg\uUu(…)\uArg\N(uu VA_ARGS_uu,u RSEQ_N())
。或者这会以某种方式破坏预处理吗?我是否遗漏了什么?与VFUNC
相同:只需删除它。然后,将VFUNC
定义为:#定义VFUNC(名称,N)名称#N
,而不是#定义(名称,N)(。