为啥我的可变参数宏不能正确接受任何参数?

Posted

技术标签:

【中文标题】为啥我的可变参数宏不能正确接受任何参数?【英文标题】:Why won't my variadic macro accept no arguments correctly?为什么我的可变参数宏不能正确接受任何参数? 【发布时间】:2017-10-17 15:43:21 【问题描述】:

Overloading Macro on Number of Arguments

https://codecraft.co/2014/11/25/variadic-macros-tricks/

我一直在查看上面的两个链接,试图让以下代码工作:

#define _GET_NUMBER(_0, _1, _2, _3, _4, _5, NAME, ...) NAME
#define OUTPUT_ARGS_COUNT(...) _GET_NUMBER(_0, ##__VA_ARGS__, 5, 4, 3, 2, 1, 0)

...

cout << OUTPUT_ARGS_COUNT("HelloWorld", 1.2) << endl;
cout << OUTPUT_ARGS_COUNT("HelloWorld") << endl;
cout << OUTPUT_ARGS_COUNT() << endl;

这会编译、运行并给出以下输出:

2
1
1

我一生都无法弄清楚为什么调用 OUTPUT_ARGS_COUNT() 给我的是 1 而不是 0。我对我尝试使用的代码有很好的理解,但它对我来说还是有点希腊语所以我想我可能没有正确应用某些东西,尽管我实际上是从堆栈溢出链接中复制并粘贴了示例代码。

我正在使用 g++ 5.4.0 20160609 进行编译。

您可以向我指出任何想法或其他资源,我们将不胜感激。

【问题讨论】:

你可以在gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html看到:"上面的解释对于只有一个宏参数是可变参数参数的情况是模棱两可的,因为试图区分是否没有参数是没有意义的at all 是一个空参数或缺少参数。当符合特定 C 标准时,CPP 保留逗号。否则,逗号作为标准的扩展被删除。". 检查这个***.com/questions/2308243/…,那里的解决方案在零参数上也失败了 【参考方案1】:

你可以在http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html看到:

其次,“##”标记粘贴运算符放在逗号和变量参数之间时具有特殊含义。如果你写

#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)

在使用eprintf宏时省略了变量参数,那么'##'之前的逗号将被删除。如果您传递一个空参数,则不会发生这种情况,如果“##”之前的标记不是逗号,也不会发生这种情况。

eprintf ("success!\n")
 → fprintf(stderr, "success!\n");

上面的解释对于唯一的宏参数是可变参数参数的情况是模棱两可的,因为试图区分根本没有参数是空参数还是缺少参数是没有意义的。当符合特定的 C 标准时,CPP 保留逗号。否则,逗号作为标准的扩展被删除。

因此,(除非使用适当的扩展名)OUTPUT_ARGS_COUNT() 被视为 1 个空参数(逗号与 ##__VA_ARGS__ 保持一致)。

【讨论】:

我在您对我的原始帖子发表评论后看到了该文档,但我不知道要根据它更改什么来修复它,所以当我调用 OUTPUT_ARGS_COUNT() 时我可以得到 0。你是说不可能吗?或者我只是不明白你指的是什么合适的扩展名。 如您所见,Demo、-std=gnu++XX 给出了0,而-std=c++XX 给出了1。你甚至得到了关于那个结构的明确警告。 @Brian:如引用中所述,这取决于您正在编译的标准版本。如果您使用例如-std=c99 进行编译,您将获得所显示的输出。另一方面,如果你使用-std=gnu99,你会得到2 1 0 我正在使用 -std=c++11 进行编译,所以我很确定这就是我想要的。我害怕切换到 gnu11,因为事实上已经进行了很多测试,我不想让其中任何一个失效。嗯嗯嗯。 . .决定决定! @Brian 如果您正在使用如上所示的值,只需使用可变参数模板即可避免大量头痛【参考方案2】:

C 标准规定

如果宏定义中的标识符列表不以省略号结尾,[...]。否则,调用中的参数应该比宏定义中的参数多(不包括...)

(C2011 6.10.3/4;强调)

C++11 包含与第 16.3/4 段相同效果的语言。

那么,在这两种情况下,如果您的宏调用被解释为具有零参数,那么您的程序将是不合格的。另一方面,预处理器确实识别并支持空宏参数——即由零个预处理标记组成的参数。那么,原则上,这里在没有参数和单个空参数之间存在歧义,但在实践中,只有后一种解释会导致程序一致。

g++ 选择后一种解释(另一个答案引用了它的文档)因此是合理和适当的,但是如果您希望您的代码可移植,则依赖它是不安全的。采用替代解释的编译器会表现出不同的行为,可能是通过提供您期望的行为,但也可能是通过拒绝代码。

【讨论】:

以上是关于为啥我的可变参数宏不能正确接受任何参数?的主要内容,如果未能解决你的问题,请参考以下文章

gnu printf可变参数宏

为啥视图修饰符不能接受不可变变量?

可变参数列表

是否可以迭代可变参数宏中的参数?

当 Row 接受可变参数时,为啥 Scala 编译器会失败并显示“此处不允许 ': _*' 注释”?

为啥编译器不能通过逗号运算符扩展可变参数模板的参数?