代码之家  ›  专栏  ›  技术社区  ›  Eric Niebler

是否可移植检测\uu VA\u OPT\uuuuu支持?

  •  36
  • Eric Niebler  · 技术社区  · 7 年前

    在C++20中,预处理器支持 __VA_OPT__ 如果参数的数量大于零,可以选择在变量宏中扩展标记。(这样就不需要 ##__VA_ARGS__ GCC扩展,这是一种不可移植且丑陋的黑客行为。)

    Clang SVN已经实现了这个特性,但他们还没有为它添加特性测试宏。任何聪明的预处理器黑客都能想出一种方法来检测 __VA\U选项__ 支持而不会导致硬错误或可移植性警告?

    4 回复  |  直到 7 年前
        1
  •  48
  •   cpplearner    4 年前

    灵感来自 chris's answer . 1.

    #define PP_THIRD_ARG(a,b,c,...) c
    #define VA_OPT_SUPPORTED_I(...) PP_THIRD_ARG(__VA_OPT__(,),true,false,)
    #define VA_OPT_SUPPORTED VA_OPT_SUPPORTED_I(?)
    

    如果 __VA_OPT__ 支持, VA_OPT_SUPPORTED_I(?) 扩展到 PP_THIRD_ARG(,,true,false,) ,所以第三个参数是 true ; 否则 VA\U OPT\U SUPPORED\U I(?) 扩展到 PP_THIRD_ARG(__VA_OPT__(,),true,false,) ,第三个参数是 false .


    编辑:作为 Edward Diener's answer 注释,GCC>=8在看到时发出警告或错误 __VA\U选项__ 如果 -pedantic 模式打开,并且 __VA\U选项__ 未启用(例如,在 -std=c++17 ). 这是 GCC bug 98859 . 可能必须使用特殊情况的GCC来避免这种诊断。

    #if __cplusplus <= 201703 && defined __GNUC__ \
      && !defined __clang__ && !defined __EDG__ // These compilers pretend to be GCC
    #  define VA_OPT_SUPPORTED false
    #endif
    

    正如chris提到的,如果 __VA_OPT__(,) 扩展到 , ,将有2个空参数,否则将有1个参数。所以可以测试 PP_NARG(__VA_OPT__(,)) == 2 哪里 PP_NARG is a macro to count the number of arguments . 为了适应此测试,定义 PP\U NARG公司 可以简化和内联。

        2
  •  10
  •   chris    7 年前

    虽然您可能能够改进,但以下内容应该可以奏效:

    #include <boost/preprocessor.hpp>
    
    #define VA_OPT_SUPPORTED_II_1(_) 0
    #define VA_OPT_SUPPORTED_II_2(_1, _2) 1
    
    #define VA_OPT_SUPPORTED_I(...) BOOST_PP_OVERLOAD(VA_OPT_SUPPORTED_II_, __VA_OPT__(,))(__VA_OPT__(,))
    
    #define VA_OPT_SUPPORTED VA_OPT_SUPPORTED_I(?)
    

    在Clang trunk上,在C++2a模式下计算为1,在C++17模式下计算为0。在C++17中,GCC trunk实际上将其计算为1,但也处理 __VA_OPT__ 在该模式下。

    它的用途是 BOOST_PP_OVERLOAD 调用 _1 _2 版本 _II 基于参数计数。如果 __VA_OPT__(,) 扩展到 , ,将有2个空参数。否则,将有1个空参数。我们总是使用参数列表调用此宏,因此任何支持 __VA\U选项__ 应始终将其扩展到 , .

    当然,这是一种激励。PP依赖项不是强制性的。一个简单的1或2参数 OVERLOAD 宏应该很容易替换。失去一点通用性,使其更简单:

    #define OVERLOAD2_I(_1, _2, NAME, ...) NAME
    #define OVERLOAD2(NAME1, NAME2, ...) OVERLOAD2_I(__VA_ARGS__, NAME2, NAME1)
    
    #define VA_OPT_SUPPORTED_I(...) OVERLOAD2(VA_OPT_SUPPORTED_II_1, VA_OPT_SUPPORTED_II_2, __VA_OPT__(,))(__VA_OPT__(,))
    

    Clang有一个便携性警告:

    警告:可变宏与C++98[-Wc++98 compat-pedantic]不兼容

    我不知道如果没有C++11可变宏支持,这种检测是否可能实现。你可以考虑不支持 __cplusplus 值低于C++11,但即使包含在这样的检查中,Clang仍会发出警告。

        3
  •  6
  •   Edward Diener    5 年前

    上面最流行的答案中指定的解决方案的问题是,如果在C++20模式之外使用\uu VA\u OPT\uuuu,编译器可以自由发出警告,甚至错误,因为该字是编译器保留字,因为它以双下划线开始和结束。事实上,我发现gcc会根据所使用的编译器选项发出警告或错误,尽管在大多数编译情况下通常不会发出警告或错误。由于这一点,任何具有当前C++20测试的解决方案,例如:

    # if defined(__cplusplus) && __cplusplus > 201703L
    // Solution
    #endif
    

    是一种更保守的解决方案,尽管它将测试限制在C++20或更高版本。

        4
  •  1
  •   OwO    7 年前

    如另一个答案所述,你可以自己写 OVERLOAD 宏。 BOOST_PP_OVERLOAD 由两部分组成, BOOST_PP_CAT BOOST_PP_VARIADIC_SIZE . 然而,与Boost不同,您只关心2个参数。因此:

    #define OVERLOAD(prefix, ...) CAT(prefix, VARIADIC(__VA_ARGS__))
    

    CAT 将看起来像:

    #define CAT(a, b) KITTY((a, b))
    #define KITTY(par) MEOW ## par
    #define MEOW(a, b) a ## b
    

    VARIADIC :

    #define VARIADIC(...) _VARIADIC_(__VA_ARGS__, 2, 1,)
    #define _VARIADIC_(e0, e1, size, ...) size