我正在用C ++编写一个可变参数调度程序宏,根据提供给调度程序的参数数量(从无到5)调用另一个宏。我提出了这个解决方案:
#define GETOVERRIDE(_ignored, _1, _2, _3, _4, _5, NAME, ...) NAME
#define NAMEDARGS(...) GETOVERRIDE(ignored, ##__VA_ARGS__, NAMEDARGS5, NAMEDARGS4, NAMEDARGS3, NAMEDARGS2, NAMEDARGS1, NAMEDARGS0)(__VA_ARGS__)
NAMEDARGS是调度员宏;使用1参数调用它将导致对NAMEDARGS1的调用,该调用需要1个参数,依此类推(我不提供各种NAMEDARGS#的实现,因为它们在此上下文中无关紧要)。
我测试了代码gcc 7.1.1,并且在使用-std = c ++ 14标志时发现了gcc扩展的奇怪行为。使用此测试代码:
NAMEDARGS()
NAMEDARGS(int)
NAMEDARGS(int, float)
我得到了这些扩展:
$ gcc -E testMacro.cpp
NAMEDARGS0()
NAMEDARGS1(int)
NAMEDARGS2(int, float)
$ gcc -E -std=c++14 testMacro.cpp
NAMEDARGS1()
NAMEDARGS1(int)
NAMEDARGS2(int, float)
似乎使用-std = c ++ 14标志,零参数调用的替换失败,导致调用单参数宏。我认为这可能是因为## __ VA_ARGS__语法是GNU扩展,因此无法使用ISO C ++预处理器;但是,当尝试使用clang 4.0.1时,我获得了所需的扩展:
$ clang -E -std=c++14 testMacro.cpp
NAMEDARGS0()
NAMEDARGS1(int)
NAMEDARGS2(int, float)
所以我不明白这里发生了什么。 clang是否实现了这个gnu扩展,接受非ISO代码也使用-std == c ++ 14与gcc不同?或者问题可能在其他地方?谢谢你的帮助。
答案 0 :(得分:2)
GCC默认-std
到gnu++14
(参见here),这是带有GNU扩展的C ++ 14。
将两者仅与NAMEDARGS(...)
定义进行比较,显示扩展的不同之处:
<强>代码强>
#define NAMEDARGS(...) GETOVERRIDE(ignored, ##__VA_ARGS__, NAMEDARGS5, NAMEDARGS4, NAMEDARGS3, NAMEDARGS2, NAMEDARGS1, NAMEDARGS0)(__VA_ARGS__)
NAMEDARGS()
-std = gnu ++ 14 -E
GETOVERRIDE(ignored, NAMEDARGS5, NAMEDARGS4, NAMEDARGS3, NAMEDARGS2, NAMEDARGS1, NAMEDARGS0)()
-------------------^
-std = c ++ 14 -E
GETOVERRIDE(ignored,, NAMEDARGS5, NAMEDARGS4, NAMEDARGS3, NAMEDARGS2, NAMEDARGS1, NAMEDARGS0)()
-------------------^^
我不是一位经验丰富的标准读者,但我在 [cpp.replace] 中找到以下两段,表明GCC在两次调用中都是正确的:
如果宏定义中的标识符列表没有以省略号结尾,则在调用类函数宏时,参数的数量(包括那些不包含预处理标记的参数)应等于参数的数量。宏定义。 否则,调用中的参数应该多于宏定义中的参数(不包括......)。应该存在一个终止调用的预处理标记。
...
如果在类似函数的宏定义中有一个......紧接在...之前,那么尾随参数(包括任何分隔逗号预处理标记)将合并为一个单独的项:变量参数。 如此组合的参数数量使得在合并之后,参数的数量比宏定义中的参数数量多一个(不包括......)。
似乎正确的是,空__VA_ARGS__
被扩展为一个空参数。
我无法确定此处是否有铿锵行为。