可变参数宏警告

Variadic macro warning

本文关键字:警告 参数 变参      更新时间:2023-10-16

在C++中,可变参数宏require at least one argument for the '...'。考虑以下函数:FOO(a, b, ...);如果我希望这两个调用都是正确且无警告的,我该怎么办?FOO(5, 4, "gamma"); FOO(5, 4);我使用的是--pedantic标志,因此仅禁止显示警告不是一种选择。

第二个给出了上面提到的编译时警告。我考虑过这个:

将定义更改为FOO(a, ...);并将__VA_ARGS__变量(代表...)拆分为b,如果存在,则拆分为其余变量。所以函数调用看起来像:FOO(5, "4gamma");FOO(5, "4");我认为这不是一个好的选择,因为拆分效率不高,函数声明不需要b参数,即使它是强制性的。

有没有更好的方法来获得无警告的编译?

尽管我完全同意,如果可能的话,应该使用可变参数函数或函数模板,但这个问题也显示出对宏可以做什么和不能做什么的一些误解,所以在这个答案中,我将假装函数不是一种选择。

将定义更改为FOO(a, ...);并将__VA_ARGS__变量(代表...)拆分为b变量,如果存在,则拆分为其余变量。

是的。

因此,函数调用如下所示:FOO(5, "4gamma");FOO(5, "4");

不。继续调用FOO(5, 4, "gamma");FOO(5, 4);。在第一种情况下,__VA_ARGS__4, "gamma".在第二种情况下,__VA_ARGS__4.

如果需要从前者中提取, "gamma",则可以由预处理器完成。它需要参数数量的上限,但您可以将其增加到几乎任何您喜欢的数字。虽然很丑。

如果__VA_ARGS__不包含逗号,则提取是微不足道的:

#define COMMA_TRAILING_ARGS_0(a)

如果您知道__VA_ARGS__至少包含一个逗号,则可以使用

#define COMMA_TRAILING_ARGS_1(a, ...) , __VA_ARGS__

您可以检测使用其中的哪一个,直到宏参数的某个上限:

#define ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _16
#define HAS_COMMA(...) ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, )

组合的:

#define COMMA_TRAILING_ARGS_0(a)
#define COMMA_TRAILING_ARGS_1(a, ...) , __VA_ARGS__
#define ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _16
#define HAS_COMMA(...) ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,)
#define CONCAT(a, b) a ## b
#define CONCAT_(a, b) CONCAT(a, b)
#define FOO(a, ...) BAR(a CONCAT_(COMMA_TRAILING_ARGS_, HAS_COMMA(__VA_ARGS__)) (__VA_ARGS__))
FOO(5, 4, x, q);    // expands to BAR(5, x, q);
FOO(5, 4, "gamma"); // expands to BAR(5, "gamma");
FOO(5, 4);          // expands to BAR(5);
  • 你可以摆脱这个宏,因为宏是邪恶的,并用模板或其他东西替换它
  • 您可以定义两个宏:FOOs- 具有许多参数和FOO- 仅使用两个参数
  • 您可以使用 GCC##__VA_ARGS__扩展(假设您使用的是 GCC 或兼容的编译器)

我不明白你的具体问题,但从你展示的内容来看,使用两个重载函数要容易得多。

void foo(int, int);
void foo(int, int, const std::string&);
foo(3, 4); // calls first
foo(3, 4, "hello"); // calls second

甚至可能:

void foo(int, int, const std::string& = "");

另一方面,如果您可以使用 C++11:

template<typename... Args>
void foo(int, int, const Args&... args); // google variadic templates

一个参数包(args)可以由零个或多个参数组成。