带有 gcc 和 clang 的可变参数宏扩展的奇怪行为
Weird behavior of variadic macro expansion with gcc and clang
我正在用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 个参数调用它将导致调用需要 1 个参数的 NAMEDARGS1,依此类推(我不提供各种 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 代码,与 gcc 不同,使用 -std==c++14?或者问题出在别处?感谢您的帮助。
GCC默认-std
为gnu++14
(见这里(,这是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在这两个调用中都是正确的:
如果宏定义中的标识符列表不以省略号结尾,则调用类似函数的宏中的参数数(包括那些不包含预处理标记的参数(应等于宏定义中的参数数。否则,调用中的参数应多于宏定义中的参数(不包括...(。应存在终止调用的 ( 预处理令牌。
。
如果有...紧接在类似函数的宏定义中的 ( 之前,尾随参数(包括任何分隔逗号预处理标记(将合并为单个项目:变量参数。如此组合的参数数使得在合并后,参数数比宏定义中的参数数多 1(不包括 ...(。
将空__VA_ARGS__
扩展为单个空参数似乎是正确的。
我找不到叮叮当在这里的行为是否是有意的。
- std::具有相同基类的类的变体
- 访问者访问变体并返回不同类型时出错
- 是否可以通过C++扩展强制多个python进程共享同一内存
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 我应该使用什么来代替void作为变体中的替代类型之一
- 将shared_ptr移动到<StructA>shared_ptr<变体<结构A、结构 B>>
- 如何将这个C++哈希表转换为动态扩展和收缩,而不是使用硬设置的最大值
- 扩展光电二极管探测器以支持多个传感器
- 如何比较自定义类的std::变体
- 通过网络、跨平台传递std::变体是否安全
- 我可以在C++中扩展变体吗?
- 聚合初始化的 C++17 扩展是否使大括号初始化变得危险?
- 从C 中的PHP扩展中获取函数参数值
- C++11:变分模板函数参数包扩展执行顺序
- 如何使用基于 IExecuteCommand 的 shell 扩展将某些上下文菜单选项变灰
- 使用 MPL 列表扩展提升变体
- 扩展'isalnum'以识别 UTF-8 变音符号
- 扩展非类型形参包来定义带有非类型形参的内部类模板是否合法?
- 编译器的区别:扩展x3::变体需要用gcc定义复制构造函数、复制赋值运算符和默认构造函数,但不需要clang
- 嵌入的铬 - 在不扩展类C++的情况下修改虚拟方法参数值