【问题标题】:Variadic macros with zero arguments零参数的可变参数宏
【发布时间】:2011-08-18 23:20:35
【问题描述】:

我正在处理调用宏,

#define CALL(f,...) FN(f)->call((ref(new LinkedList()), __VA_ARGS__))

调用时,

CALL(print,2,3,4,5);

将 2 3 4 5 添加到链表(为此已重载)并调用 print 期望链表按预期工作但是有一些调用不需要参数,

CALL(HeapSize);

它仍然需要一个链接列表,但是一个空列表,上面不起作用,我正在尝试提出一个可以与任何一种样式一起使用的宏?

编辑:通过 gcc 文档挖掘我发现在 VA_ARGS 之前添加 ## 会删除 ,当没有参数但我无法嵌套宏时,

CALL(print,CALL(HeadSize));

这会导致 CALL 未定义错误,但是如果我将调用分开,它会起作用

【问题讨论】:

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


【解决方案1】:

很遗憾,这是无法做到的。您将需要定义一个单独的宏来执行此调用。

VA_ARGS 被替换为空时,你会得到无效的参数,你最终会得到一个浮动的,

#define CALL0(f) FN(f)->call((ref(new LinkedList())))

【讨论】:

    【解决方案2】:

    如果您使用的是 GCC,它有一个扩展名来吞噬__VA_ARGS__ 之前的逗号。见:http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

    【讨论】:

    • 只因为你的更彻底。 :)
    • MSVC,虽然没有在他们的 MSDN 页面上提到,但支持相同的扩展,让每个人的生活都更轻松。
    • @vanza:实际上,MSVC 会“自动”为你做这件事——如果你在宏中有序列 , __VA_ARGS____VA_ARGS__ 是空的,它会自动吞下(删除)@ 987654325@。如果 ## 位于两个无法合并的标记之间,它也会忽略它,因此 gcc 扩展通过偶然性工作。
    【解决方案3】:

    如果你使用 gcc/g++ 有一种方法:

    #define CALL(f,...) FN(f)->call((ref(new LinkedList()), ## __VA_ARGS__))
    

    来自fine manual

    [...] 如果变量参数被省略或为空,`##' 运算符会导致预处理器删除它前面的逗号。

    所以 gcc 有一个专门针对您面临的问题的扩展/破解。

    【讨论】:

    • 我已经编辑了这个问题,我在文档中找到了同样的东西,但是我不能嵌套宏
    • 不仅 gcc/g++,还可以与 IAR 的编译器一起使用!
    【解决方案4】:

    只需将f 变成... 的一部分,然后使用单独的宏在您需要f 的位置提取第一个参数。

    【讨论】:

      【解决方案5】:

      至于更新问题,通过使用辅助宏VA_ARGSlike 以下,参数将按预期展开。

      #define VA_ARGS(...) , ##__VA_ARGS__
      #define CALL(f,...) FN(f)->call((ref(new LinkedList()) VA_ARGS(__VA_ARGS__)))
      

      【讨论】:

      • P.S.这在我需要在末尾添加 NULL 的情况下效果很好:#define blah(x,...) actual(a,b,c,## VA_ARGS_,NULL)
      • 我一直在使用这个技巧,直到我发现在 gcc 的迂腐模式下编译 C99 甚至 C11 时,这会产生警告。好吧,不完全是这个宏定义本身,而是调用具有零可变参数部分的可变参数宏。为什么,哦,为什么他们不允许在 C11 标准中使用它?!
      • __VA_OPT__ 将成为你的朋友@AlexanderAmelkin
      【解决方案6】:

      这些答案的一个共同主题是我们需要一个 GCC 特定的 hack。一种方法是使用标记粘贴##__VAR_ARGS__,但粘贴的参数不是宏扩展的,这意味着宏不能嵌套。但是,如果您无论如何都要做特定于 GCC 的事情,那么为什么不使用老式的 GCC 扩展:

       #define VARARG_FOO(ZeroOrMoreArgs...) \
           printf("VARARG_FOO: " ZeroOrMoreArgs)
      

      ZeroOrMoreArgs 简单地被所有参数(如果有的话)、逗号和所有替换。这包括递归宏扩展。

      • 然后VARARG_FOO() 扩展为printf("VARARG_FOO: ")
      • VARARG_FOO("I iz %d", 42) 扩展为 printf("VARARGFOO: " "I iz %d", 42)

      终于

       #define NEST_ME "I tawt I taw a puddy tat"
       VARARG_FOO("The evil one says %s", NEST_ME); 
      

      将扩展到

       printf("VARARG_FOO: " "The evil one says %s", "I tawt I taw a puddy tat");
      

      优点:

      • 您可以嵌套宏调用,同时拥有零个或多个 aguments。

      缺点:

      • ##__VA_ARGS__ hack 可能在标准 C 程序中至少有一个逗号的情况下是无害的。 (我还没想过这到底是真是假)。
      • 根据@ScootMoonen 的说法,##__VA_ARGS__ hack 是对 MSVC 的无证扩展。

      【讨论】:

        【解决方案7】:

        __VA_OPT__ (c++2a) 应该更可靠,例如: 来自http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0306r2.html

        VA_OPT 的规范用例是用于可选分隔符:

        #define LOG(msg, ...) printf(msg __VA_OPT__(,) __VA_ARGS__)

        【讨论】:

          猜你喜欢
          • 2011-04-03
          • 2013-05-21
          • 1970-01-01
          • 1970-01-01
          • 2011-09-11
          • 1970-01-01
          • 1970-01-01
          • 2015-11-09
          • 1970-01-01
          相关资源
          最近更新 更多