C++ 具有零参数的可变宏

C++ 具有零参数的可变宏,c++,gcc,c-preprocessor,variadic-macros,C++,Gcc,C Preprocessor,Variadic Macros,我正在处理一个呼叫宏 #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文档,

我正在处理一个呼叫宏

#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 not defined(调用未定义)错误。但是,如果我将调用与它工作的调用分开,很遗憾,这无法完成。您需要定义一个单独的宏来执行此调用

VA_ARGS被零替换时,您将得到无效的参数,因此您将得到一个浮动的

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

如果您使用的是GCC,那么它有一个扩展名,可以吞掉
\uuva\u ARGS\uu
前面的逗号。请参阅:。

如果您使用的是gcc/g++,有一种方法:

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

[…]如果变量参数被省略或为空,“##”运算符将使预处理器删除其前面的逗号


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

只需将
f
作为
..
的一部分,并使用一个单独的宏在需要
f

的地方提取第一个参数,对于更新的问题,使用辅助宏
VA_ARGS
等 下面,参数将按预期展开

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

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

 #define VARARG_FOO(ZeroOrMoreArgs...) \
     printf("VARARG_FOO: " ZeroOrMoreArgs)
ZeroOrMoreArgs
仅由所有参数(如果有)、逗号和all替换。这包括递归宏扩展

  • 然后
    VARARG\u FOO()
    扩展到
    printf(“VARARG\u FOO:”)
  • VARARGFOO(“iz%d”,42)
    扩展到
    printf(“VARARGFOO:“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");
优点:

  • 您可以嵌套宏调用,同时拥有零个或多个AGUMENT
缺点:

  • 如果标准C程序总是至少有一个逗号,那么
    ##uu VA_uargs_uu
    黑客攻击在标准C程序中可能是无害的。(我没想过这是不是真的)
  • 根据@ScootMoonen的说法,
    ###uu VA_uargs_u uu
    hack是MSVC的一个未记录的扩展
    • \uuu VA\u OPT\uuu
      (c++2a)应该更可靠,例如: 从

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

      #定义日志(msg,…)printf(msg u VA u OPT u(,)u VA u ARGS u)


      只是因为你的更彻底。:)而MSVC,即使在他们的MSDN页面上没有提到,也支持相同的扩展,使每个人的生活都更轻松。@vanza:事实上,MSVC是“自动”为你做的——如果你在宏中有序列
      ,\uu VA_ARGS\uuuuu
      ,并且
      \uu VA_ARGS\uuuu
      为空,它会自动吞并(删除)
      。如果它位于两个无法合并的令牌之间,那么它也会忽略
      ##
      ,因此gcc扩展的工作是偶然的。我编辑了这个问题,我在文档中发现了同样的问题,但我不能用它嵌套宏不仅gcc/g++,还可以与IAR的编译器一起工作!根据传递的参数数量,可以将
      CALL
      委托给不同的宏。我演示了如何做到这一点。这不需要任何特定于实现的hack。另请参阅相关的blogpost:.P.S。在我需要在末尾添加NULL的情况下,这很有效:#define blah(x,…)actual(a,b,c,###VA#u ARGS,NULL)我一直在使用这个技巧,直到我发现在为C99甚至C11编译时,在gcc的迂腐模式下,这会产生一个警告。嗯,不完全是这个宏定义本身,而是用零变量部分调用变量宏。为什么,哦,为什么他们不允许在C11标准中使用它<代码>\uuu VA\u OPT\uuu将成为你的朋友@AlexanderAmelkin