使用VS 2008和GCC时对__VA_ARGS__的不同处理问题

时间:2010-04-04 21:01:23

标签: visual-studio-2008 gcc

我正在尝试识别问题,因为使用了可变参数宏。这是假设的宏观:

#define va(c, d, ...) c(d, __VA_ARGS__)
#define var(a, b, ...)  va(__VA_ARGS__, a, b)

var(2, 3, printf, “%d %d %d\n”, 1);

对于gcc,预处理器将输出

printf("%d %d %d\n", 1, 2, 3)

但是对于VS 2008,输出是

printf, “%d %d %d\n”, 1(2, 3);

我怀疑差异是由对 VA_ARGS 的不同处理引起的,对于gcc,它首先会将表达式扩展为va(printf,“%d%d%d \ n”,1, 2,3),并将1,2,3视为宏va的 VA_ARGS 。但是对于VS 2008,它首先将b视为宏观va的 VA_ARGS ,然后进行扩展。

哪一个是C99可变参数宏的正确解释?或者我的用法属于未定义的行为?

2 个答案:

答案 0 :(得分:6)

有一种简单的方法可以解决这个问题:

#define exp(...) __VA_ARGS__
#define va(c, d, ...) c(d, __VA_ARGS__)
#define var(a, b, ...)  exp(va(__VA_ARGS__, a, b))

var(2, 3, printf, “%d %d %d\n”, 1);

这将在VS 2008上发挥作用,它不会影响gcc

答案 1 :(得分:4)

查看ISO / IEC 9899:1999,第6.10.3.1章。 它声明:

  

后,调用类似函数的宏的参数已经识别,发生了参数替换。替换列表中的参数除非前面带有#或##预处理标记或后跟##预处理标记(见下文),否则将替换为相应的参数 后面包含的所有宏扩大。 替换之前,每个参数的预处理标记都被完全宏替换,好像它们形成了预处理文件的其余部分;没有其他预处理令牌可供使用。

所以在 va 中,第一个参数 c 有一个预处理标记为__VA_ARGS__,根据此段,必须将宏替换为< / em>在被替换为 c 之前(仍然没有给出关于哪个编译器是正确的答案)

但后来:

  

替换列表中出现的标识符__VA_ARGS__   应被视为参数和变量   参数应构成用于的预处理标记   替换它。

根据第一个片段,首先识别 var 的参数。它们是:23printf“%d %d %d\n”1。现在进行参数替换。这意味着将获取并替换 var 的替换列表中的参数。但是,第二个片段指出,标识符__VA_ARGS__将被视为参数,因此必须将其替换为printf, “%d %d %d\n”, 1。现在参数中没有宏,因此不再进行替换,导致var(2, 3, printf, “%d %d %d\n”, 1);扩展为va(printf, “%d %d %d\n”, 1, 2, 3);。因为va是一个宏,它也会被扩展,得到printf(“%d %d %d\n”, 1, 2, 3);的结果。 现在,如果您采用VS 2008的推理,如果 va 的参数 ,并且可以包含许多参数?好吧,它将__VA_ARGS__视为 va 的参数,这在我的选择中是错误的,因为根据片段1,参数替换只发生在之后已经确定了调用的参数。

所以在我看来,在 var 中,预处理器必须首先识别调用宏 va 的参数,然后开始扩展 va 。这意味着gcc可能就在这里。

还在C preprocessor and concatenation中显示了处理令牌是如何按顺序进行宏替换的,直到没有可以进一步扩展为宏的标识符,或者预处理器发现递归并终止扩展。