【问题标题】:MSVC doesn't expand __VA_ARGS__ correctlyMSVC 未正确扩展 __VA_ARGS__
【发布时间】:2011-07-05 07:21:15
【问题描述】:

考虑这段代码:

#define F(x, ...) X = x and VA_ARGS = __VA_ARGS__
#define G(...) F(__VA_ARGS__)
F(1, 2, 3)
G(1, 2, 3)

这两个宏的预期输出都是X = 1 and VA_ARGS = 2, 3,这就是我使用 GCC 得到的结果,但是,MSVC 将其扩展为:

X = 1 and VA_ARGS = 2, 3
X = 1, 2, 3 and VA_ARGS =

也就是说,__VA_ARGS__ 被扩展为单个参数,而不是被分解为多个参数。

有什么办法吗?

【问题讨论】:

    标签: visual-c++ c-preprocessor variadic-macros


    【解决方案1】:

    编辑: 这个问题可以通过使用来解决 最近的 MSVC 中的 /Zc:preprocessor/experimental:preprocessor 选项。 详情请看 here.

    MSVC 的预处理器的行为似乎与标准完全不同 规格。
    以下解决方法可能会有所帮助:

    #define EXPAND( x ) x
    #define F(x, ...) X = x and VA_ARGS = __VA_ARGS__
    #define G(...) EXPAND( F(__VA_ARGS__) )
    

    【讨论】:

    • __VA_ARGS__ 还不是标准 C++ 的一部分。标准草案是否真的指定了这种情况下的行为?
    • @bk1e:对不起,由于我没有能力,这里无法详细解释即将发布的C++标准中的预处理,但它与C99不太可能有很大的不同。跨度>
    • 有人能解释一下吗? “and VA_ARGS = VA_ARGS” 是一段有效的 C 代码,还是只是作为注释的人类可读文本?如果这是有效代码,“and VA_ARGS = VA_ARGS”在做什么?谢谢。
    • 哦,我想我明白了……让我们说__VA_ARGS__1, 2,而不是为了简单起见。 EXPAND(x) x 接受 F(__VA_ARGS__)) 并将其替换为字面上的 F(1, 2) 以便处理 F(1, 2) 而不是 F(__VA_ARGS__) 其中 __VA_ARGS__ 被视为一个单一的实体,如果这有意义的话......
    • @IseWisteria,@rbrich 已经在下面提到它现在有编译器开关/Zc:preprocessor 请参阅here,它将正确解压缩__VA_ARGS__。除了原始解决方案之外,也许值得编辑您的答案以包含此内容?
    【解决方案2】:

    我发了the following Microsoft support issue

    以下程序给出编译错误,因为预编译器 错误地扩展__VA_ARGS__

    #include <stdio.h>
    
    #define A2(a1, a2) ((a1)+(a2))
    
    #define A_VA(...) A2(__VA_ARGS__)
    
    int main(int argc, char *argv[])
    {
        printf("%d\n", A_VA(1, 2));
        return 0;
    }
    

    预处理器将 printf 扩展为: printf("%d\n", ((1, 2)+()));

    而不是 printf("%d\n", ((1)+(2)));

    我收到了来自 Microsoft 编译器团队开发人员的以下令人不满意的回答:

    嗨:在这种情况下,Visual C++ 编译器的行为是正确的。如果您将在初始宏调用中匹配“...”的标记组合以形成单个实体 (16.3/p12) 的规则与在参数替换之前扩展子宏的规则 (16.3.1/p1 ) 那么在这种情况下,编译器认为 A2 是使用单个参数调用的:因此会出现错误消息。

    【讨论】:

    • 感谢您传递 MS 的基本原理。似乎他们将 16.3.1/p12 中的“组合成一个项目”解释为“组合成一个单一的、永久不可分割的预处理器令牌”,这似乎不太有用。我希望至少在 16.3.4 中给出的重新扫描步骤中被替换的标记被重新分离,这似乎是其他编译器正在做的事情。
    • 我非常同意,但我显然被 GCC 和 Clang 宠坏了。你们中的任何一个都可以想到 MSVC 行为的用例,还是只是为了一致性而保持一致性的问题,尽管具有可表达性?我已经完全脱离了我的元素,但是“......在这种情况下,编译器相信......”听起来不是很有说服力,更不用说对任何试图编写不可知代码的人有用了。我有一些解决方法的想法,但我的 Windows 分区已无法使用。我希望看到有人在任何情况下尝试它。抱歉,您的咆哮和/或 necropost。
    • FWIW,这里是 another bug 处理同一个问题,团队承认这是一个错误,但表示它的优先级不够高,无法修复(7 年前)。
    • 现在有/experimental:preprocessor/Zc:preprocessor 编译器开关可以使预处理器正常运行。它为我解决了这个问题。
    【解决方案3】:

    您使用的是哪个版本的 MSVC?您将需要 Visual C++ 2010。

    __VA_ARGS__ 最早由 C99 引入。 MSVC 从未尝试支持 C99,因此未添加支持。

    不过,现在__VA_ARGS__ 包含在新的 C++ 标准 C++2011(以前称为 C++0x)中,Microsoft 显然计划支持该标准,因此最近版本的 MSVC 已支持它。

    顺便说一句,您需要在源文件中使用.cpp 后缀才能获得此支持。 MSVC 很久没有更新它的 C 前端了。

    【讨论】:

      猜你喜欢
      • 2015-11-30
      • 2012-08-01
      • 1970-01-01
      • 2020-09-10
      • 2020-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多