使用gcc和clang进行可变宏扩展的奇怪行为

时间:2017-08-02 09:28:46

标签: c++ gcc clang variadic-macros

我正在用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不同?或者问题可能在其他地方?谢谢你的帮助。

1 个答案:

答案 0 :(得分:2)

GCC默认-stdgnu++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__被扩展为一个空参数。

我无法确定此处是否有铿锵行为。