计算__VA_ARGS__ MSVC会产生意外结果

时间:2017-10-24 21:33:50

标签: c++ macros variadic-macros

我正在尝试计算调用正确宏的参数数量。连接和参数的数量似乎给出了预期的结果,但由于某种原因,在MSVC上,参数的数量不起作用。我尝试了EXPAND(x) x and EXPAND(...) __VA_ARGS__CALL(x,y) x y等已知修复,但没有任何效果。我还硬编码了一个我知道可以工作的数字,在编译后它给出了正确的结果,所以我把问题缩小到了计数MACRO。

编译VS后警告没有足够的参数,因为它调用了不正确的_#_DERIVED(...) MACRO。

#define CONCAT_DETAIL(l, r) l##r
#define CONCAT(l, r) CONCAT_DETAIL(l, r)
#define _COUNT_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define _COUNT(...) _COUNT_N("ignore", ## __VA_ARGS__, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _0)
#define _EXPAND(...) __VA_ARGS__
#define CLASS_BODY(...) CONCAT(_EXPAND(_COUNT(__VA_ARGS__)),_DERIVED)(__VA_ARGS__)

示例用法

CLASS_BODY(Renderer)
CLASS_BODY(Object, XMLParser)

MACRO编译后的期望结果

_0_DERIVED()
_1_DERIVED(arg1)
_2_DERIVED(arg1, arg2)
.
.
.

1 个答案:

答案 0 :(得分:0)

MSalters是对的;具有单个下划线的标识符是保留的。如果您的宏在实现中包含任何内容时处于活动状态,则可能会在您最不期望的时候发生冲突。

编译后VS警告...

我怀疑基于这个引用你是通过尝试编译它们来调试你的宏。更好的方法是仅使用预处理器。在Microsoft中,您可以通过从开发控制台在命令行中运行cl /EP foo.h(或在为特定实现运行VCVARSALL之后)来执行此操作。

现在到宏:

#define _COUNT_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define _COUNT(...) _COUNT_N("ignore", ## __VA_ARGS__, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _0)
#define _EXPAND(...) __VA_ARGS__

_COUNT需要一个扩展步骤,否则它就没用了。这样更好:

#define COUNT_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define COUNT(...) EXPAND(COUNT_N( , __VA_ARGS__, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _0))
#define EXPAND(...) __VA_ARGS__

现在:

COUNT() COUNT(a) COUNT(a,b)

...扩展为:

_1 _1 _2

技术上正确。 COUNT()有一个参数(它是空的)。这不会在Microsoft中返回_0的原因是因为扩展步骤......允许COUNT宏首先工作的事情。如果您希望COUNT()返回0,则需要添加另一级别的间接使用CALL(因为参数列表需要展开一次以触发逗号省略):

#define COUNT_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define COUNT_M(...) EXPAND(COUNT_N( __VA_ARGS__, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _0))
#define COUNT(...) CALL(COUNT_M,(, __VA_ARGS__))
#define CALL(X,Y) X Y
#define EXPAND(...) __VA_ARGS__

...现在COUNT()返回_0;请注意这是微软特有的。

把它放在一起

#define CONCAT_DETAIL(l, r) l##r
#define CONCAT(l, r) CONCAT_DETAIL(l, r)
#define COUNT_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define COUNT_M(...) EXPAND(COUNT_N( __VA_ARGS__, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _0))
#define COUNT(...) CALL(COUNT_M,(, __VA_ARGS__))
#define CALL(X,Y) X Y
#define EXPAND(...) __VA_ARGS__
#define CLASS_BODY(...) CONCAT(EXPAND(COUNT(__VA_ARGS__)),_DERIVED)(__VA_ARGS__)

CLASS_BODY()
CLASS_BODY(arg1)
CLASS_BODY(arg1,arg2)

...扩展为:

_0_DERIVED()
_1_DERIVED(arg1)
_2_DERIVED(arg1,arg2)

当然,您现在只是生成以_开头的标识符。