【问题标题】:Variadic macro warning可变宏警告
【发布时间】:2017-04-16 08:55:23
【问题描述】:

在 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 参数,即使它是强制性的。

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

【问题讨论】:

  • 你为什么使用宏?您要解决的实际问题是什么?
  • 可变参数函数模板是您的解决方案。
  • @DeiDei 能否详细说明一下函数模板?

标签: c++ macros variadic


【解决方案1】:

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

将定义更改为 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);

【讨论】:

    【解决方案2】:
    • 你可以去掉这个宏,因为宏是邪恶的,用模板或其他东西代替它
    • 您可以定义两个宏:FOOs - 有很多参数,FOO - 只有两个参数
    • 你可以使用gcc ##__VA_ARGS__ extension(假设你使用的是gcc或者兼容的编译器)

    【讨论】:

    • 但这意味着我必须FOO(5, 4); FOOs(5, 4, "gamma");,这与我想要的略有不同。
    【解决方案3】:

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

    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) 可以包含 零个或多个 个参数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-14
      • 1970-01-01
      相关资源
      最近更新 更多