具有零参数的可变参数宏

38

我正在开发一个调用宏的项目。

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

当调用它时,

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

将 2、3、4 和 5 添加到链表中(逗号重载为添加操作),并调用 print 方法。print 方法期望传入一个链表,这个过程也如预期般正常工作。但是有一些调用不需要参数。

CALL(HeapSize);

仍需要一个链表,但是是空的。上面的代码不起作用,我试图想出一个宏可以与任何风格一起工作?

编辑:在研究gcc文档时,我发现在VA_ARGS之前添加##会在没有参数时删除逗号,但是这样我就无法嵌套宏了。

CALL(print,CALL(HeadSize));

这会导致“CALL未定义”错误,但是如果我将调用分开,它就可以工作。


1
您可以使CALL委托给不同的宏,具体取决于传递给它的参数数量。我在另一个问题的答案中演示了如何实现这一点。这不需要任何特定于实现的技巧。 - James McNellis
请参见https://dev59.com/jW035IYBdhLWcg3wKsqa。 - Richard Hansen
相关博客文章:"检测空宏参数" - Luiz Martins
7个回答

31

关于更新后的问题,通过使用辅助宏VA_ARGS,如下所示,参数将按预期进行扩展。

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

这段代码在我的情况下很有效,我需要在末尾添加NULL: #define blah(x,...) actual(a,b,c,## VA_ARGS_,NULL) - Brad
13
直到我发现,当使用gcc以C99甚至C11编译并启用严谨模式时,这个技巧会导致编译器警告。不是这个宏定义本身,而是使用零变参部分调用可变参数宏。为什么C11标准没有允许这样做呢? - Alexander Amelkin
3
__VA_OPT__将成为您的好朋友 @AlexanderAmelkin - Richard

22

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

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

精细的手册中得知:

如果可变参数被省略或为空, `##' 运算符会导致预处理器在其前面删除逗号。

因此,gcc针对你所遇到的问题有一个特定的扩展/技巧。


1
我已经编辑了问题,我在文档中找到了相同的内容,但是我无法嵌套宏。 - Hamza Yerlikaya
2
不仅适用于gcc/g++编译器,也适用于IAR的编译器! - Lukas Kalinski

8

6

仅仅是因为你的更加彻底。 :) - Scott Moonen
1
而且,即使在他们的MSDN页面上没有提到,MSVC也支持相同的扩展,使每个人的生活变得更加轻松。 - vanza
6
@vanza: 实际上,MSVC会为您“自动”完成此操作——如果宏中有序列“,__VA_ARGS__”,并且“__VA_ARGS__”为空,则它将自动吞掉(删除)“,”。它还会忽略“##”,如果它位于无法合并的两个标记之间,则gcc扩展功能就是偶然发生的。 - Chris Dodd

2
只需将f作为...的一部分,并使用单独的宏在需要f的第一个参数时提取它。

2
这些答案中的一个共同主题是我们需要特定于GCC的技巧。一种方法是使用令牌粘贴##__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");

优点:

  • 您可以嵌套宏调用,同时具有零个或多个参数。

缺点:

  • ##__VA_ARGS__技巧在标准C程序中可能是无害的,前提是它们始终至少有一个逗号。(我没有考虑过这是否正确)。
  • 根据@ScootMoonen的说法,##__VA_ARGS__技巧是MSVC的一种未记录的扩展。

0

很遗憾,这是不可能的。您需要定义一个单独的宏来进行此调用。

VA_ARGS被替换为空时,您最终会得到无效的参数,从而导致浮动,

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接