在使用VS 2008和GCC时,关于对__VA_ARGS__进行不同处理的问题 [英] the problem about different treatment to __VA_ARGS__ when using VS 2008 and GCC

查看:195
本文介绍了在使用VS 2008和GCC时,关于对__VA_ARGS__进行不同处理的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图找出一个问题,因为使用了各种各样的宏。这里是假设的宏:

I am trying to identify a problem because of an unusual usage of variadic macros. Here is the hypothetic macro:

#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,预处理器将输出

For gcc, the preprocessor will output

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

,但对于VS 2008,输出是

but for VS 2008, the output is

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

我怀疑这种差异是由于 VA_ARGS ,对于gcc,它首先将表达式扩展为va(printf,%d%d%d \ n,1,2,3),并将1,2,3作为 VA_ARGS <对于宏va。但对于VS 2008,它首先将b视为 VA_ARGS ,然后进行扩展。

I suspect the difference is caused by the different treatment to VA_ARGS, for gcc, it will first expand the expression to va(printf, "%d %d %d\n", 1, 2, 3), and treat 1, 2, 3 as the VA_ARGS for macro va. But for VS 2008, it will first treat b as VA_ARGS for macro va, and then do the expansion.

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

Which one is correct interpretation for C99 variadic macro? or my usage falls into an undefined behavior?

推荐答案

请参阅ISO / IEC 9899:1999,章节6.10.3.1。
它声明:

Look into the ISO/IEC 9899:1999, chapter 6.10.3.1. It states that:


之后,调用函数式宏的参数被识别,参数替换发生。替换列表中的参数除非在#或##预处理标记之前或之后是##预处理标记(参见下文),否则在中包含的所有宏已被扩大。 被替换之前,每个参数的预处理标记完全被宏替换,就好像它们构成了预处理文件的其余部分一样;

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

因此,在 va 中,第一个参数 c >有一个预处理标记是 __ VA_ARGS __ ,根据这一段,它必须被宏替代,然后替换为 c (它仍然没有给出答案,哪个编译器是正确的)

So in va the first argument c has one preprocessing token being __VA_ARGS__, which, according to this paragraph, must be macro replaced before being substituted to c (which still does not give the answer as to which compiler is right)

但后来:

But later:


替换列表
中出现的标识符 __ VA_ARGS __ 应视为参数,并且变量
参数将形成用于
的预处理标记来代替它。

An identifier __VA_ARGS__ that occurs in the replacement list shall be treated as if it were a parameter, and the variable arguments shall form the preprocessing tokens used to replace it.

根据第一个片段,首先识别 var 的参数。它们是: 2 3 printf %d%d%d \ n 1 。现在进行参数替换。这意味着来自 var 的替换列表中的参数被取代并被替换。但是,第二个片段指出,标识符 __ VA_ARGS __ 将被视为参数,因此它必须替换为 printf,%d%d %d \\\
,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_ARGS __ ,它如何识别 va 的参数c $ c>来自 var ,并且可以包含许多参数?那么,它将 __ VA_ARGS __ 作为 va 的参数,这在我的选择中是错误的,因为根据片段1,参数替换仅发生 在调用的参数已被识别。

According to the first fragment, first the arguments to var are identified. They are: 2, 3, printf, "%d %d %d\n" and 1. Now argument substitution takes place. This means that parameters from var's replacement list are taken and replaced. However, the second fragment states, that identifier __VA_ARGS__ is to be treated as a parameter, so it must be substituted to printf, "%d %d %d\n", 1. Now there are no macros inside parameters, so no further substitution takes place, resulting in the expansion of var(2, 3, printf, "%d %d %d\n", 1); into va(printf, "%d %d %d\n", 1, 2, 3);. Because va is a macro, it is also expanded, giving the result of printf("%d %d %d\n", 1, 2, 3);. Now if you take VS 2008's reasoning, how can it identify the arguments to va, if one of them is __VA_ARGS__ from var, and can contain many arguments? Well, it treats __VA_ARGS__ as an argument to va, which in my opition is wrong, as, according to fragment one, argument substitution takes place only after the arguments for invocation have been identified.

因此,在我看来,预处理器在 var 首先确定调用宏 va 的参数,然后开始展开 va 。这意味着gcc可能就在这里。

So it seems to me, that in var the preprocessor has to first identify the arguments to the invocation of the macro va, and then start expanding va. And this means that probably gcc is right here.

它也显示在

It is also shown in C preprocessor and concatenation how processing tokens are macro-replaced in order, until there are no identifiers which can be further expanded as macros, or the preprocessor spots a recursion and terminates expansion.

这篇关于在使用VS 2008和GCC时,关于对__VA_ARGS__进行不同处理的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆