为什么宏展开失败而必须使用一层间接寻址?

Why the macro expansion fails and one level of indirection must be used?

当我在玩宏参数计数器时,我发现如果删除一级间接它就无法工作了。

原代码:

#define COUNT_ARGS(...) \
     COUNT_INDIR(__VA_ARGS__,COUNT_DOWN())
#define COUNT_INDIR(...) \
     COUNT_HELPER(__VA_ARGS__)
#define COUNT_HELPER( \
     _1, _2, _3, _4, _5, N,...) N
#define COUNT_DOWN() \
     5, 4, 3, 2, 1, 0

COUNT_ARGS(1,2,3);

我编辑的版本:

#define COUNT_ARGS(...) \
     COUNT_HELPER(__VA_ARGS__,COUNT_DOWN())
#define COUNT_HELPER( \
     _1, _2, _3, _4, _5, N,...) N
#define COUNT_DOWN() \
     5, 4, 3, 2, 1, 0

COUNT_ARGS(1,2,3);

我收到了以下消息

error: macro "COUNT_HELPER" requires 7 arguments, but only 4 given

我的猜测是 COUNT_DOWN() 被传递给 COUNT_HELPER 而没有被展开。但是我从 Argument Prescan 中读到,宏参数在被替换为宏体之前已经完全展开了。自相矛盾的行为让我很困惑。

类函数宏的扩展在离散阶段运行:

  1. 参数是从参数列表中识别出来的。宏的参数列表中的每个命名参数必须有一个参数,如果宏是可变的,则至少还有一个参数。

  2. 每个参数都是完全宏展开的。

  3. 宏调用被宏的替换文本替换,扩展参数替换相应的参数名称。

  4. 重新扫描扩展以进行额外的宏扩展。

(这忽略了 ### 运算符的影响,它们在这里没有发挥作用。)

我怀疑您的困惑是由于没有意识到步骤 (2) 与步骤 (1) 完全分开并在步骤 (1) 之后发生的。一旦确定了宏参数,就设置了它们的编号以及它们与宏参数的对应关系。扩展它们不会修改它,无论扩展文本的性质如何。或者,您可能不理解宏替换文本中的宏调用不会在宏定义本身中展开,而是在展开宏过程的第 (4) 步展开。

因此,给定这些(原始)定义...

#define COUNT_ARGS(...) \
     COUNT_INDIR(__VA_ARGS__,COUNT_DOWN())
#define COUNT_INDIR(...) \
     COUNT_HELPER(__VA_ARGS__)
#define COUNT_HELPER( \
     _1, _2, _3, _4, _5, N,...) N
#define COUNT_DOWN() \
     5, 4, 3, 2, 1, 0

...这是展开的顺序:

COUNT_ARGS(1,2,3);
COUNT_INDIR(1,2,3,COUNT_DOWN());
COUNT_HELPER(1,2,3,5,4,3,2,1,0);
3;

另一方面,根据您修改后的定义...

#define COUNT_ARGS(...) \
     COUNT_HELPER(__VA_ARGS__,COUNT_DOWN())
#define COUNT_HELPER( \
     _1, _2, _3, _4, _5, N,...) N
#define COUNT_DOWN() \
     5, 4, 3, 2, 1, 0

...扩展是这样的:

COUNT_ARGS(1,2,3);
COUNT_HELPER(1,2,3,COUNT_DOWN());

错误

正如错误消息告诉您的那样,提供给 COUNT_HELPER() 的参数太少。 COUNT_HELPER() 的扩展会产生一个逗号分隔的列表在这一点上是无关紧要的。

使 COUNT_DOWN() 扩展为 COUNT_HELPER() 的多个参数是原始 COUNT_INDIR() 宏的全部目的。