可以轻松检测__VA_OPT__支持?

时间:2017-12-31 20:26:59

标签: c++ c-preprocessor c++20

在C ++ 20中,预处理器支持__VA_OPT__,以便在参数数量大于零时可选地扩展可变参数宏中的标记。 (这消除了##__VA_ARGS__ GCC扩展的需要,这是一个不可移植且丑陋的黑客。)

Clang SVN已实现此功能,但他们尚未为其添加功能测试宏。任何聪明的预处理器黑客能否找到一种方法来检测__VA_OPT__支持的存在与否,而不会导致硬错误或可移植性警告?

4 个答案:

答案 0 :(得分:28)

chris's answer的启发。

#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_OPT_SUPPORTED_I(?)会扩展为PP_THIRD_ARG(__VA_OPT__(,),true,false,),第三个参数为false

答案 1 :(得分:6)

以下内容应该可行,但您可以改进它:

#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主干上,在C ++ 2a模式下评估为1,在C ++ 17模式下评估为0。 GCC trunk实际上在C ++ 17中将其计算为1,但在该模式下也处理__VA_OPT__

这样做是使用BOOST_PP_OVERLOAD根据参数计数调用_1_2 _II版本的__VA_OPT__(,)。如果,扩展为__VA_OPT__,则会有2个空参数。如果没有,将有1个空参数。我们总是使用参数列表调用此宏,因此任何支持,的编译器都应始终将其扩展为OVERLOAD

当然,Boost.PP依赖关系不是强制性的。一个简单的1-or-2-arg #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__(,)) 宏应该很容易替换。失去一点普遍性使其更直接:

__cplusplus

Clang有一个可移植性警告:

  

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

如果没有C ++ 11可变参数宏支持,我不知道这种检测是否可行。您可以考虑假设不支持低于C ++ 11的{{1}}值,但即使包含在此类检查中,Clang仍会发出警告。

答案 2 :(得分:2)

如其他答案中所述,您可以编写自己的OVERLOAD宏。 BOOST_PP_OVERLOAD由两部分组成,BOOST_PP_CATBOOST_PP_VARIADIC_SIZE。但是,与Boost不同,你只关心2个args。所以:

#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

答案 3 :(得分:1)

在上面最流行的答案中指定的解决方案的问题是,如果在C ++ 20模式之外使用__VA_OPT__,则编译器可以自由发出警告甚至错误。是编译器保留字,因为它以双下划线开头和结尾。实际上,我发现gcc会根据所使用的编译器选项发出警告或错误,尽管在大多数编译情况下通常不会发出警告或错误。因此,围绕当前C ++ 20测试的任何解决方案,例如:

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

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