在C ++ 20中,预处理器支持__VA_OPT__
,以便在参数数量大于零时可选地扩展可变参数宏中的标记。 (这消除了##__VA_ARGS__
GCC扩展的需要,这是一个不可移植且丑陋的黑客。)
Clang SVN已实现此功能,但他们尚未为其添加功能测试宏。任何聪明的预处理器黑客能否找到一种方法来检测__VA_OPT__
支持的存在与否,而不会导致硬错误或可移植性警告?
答案 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_CAT
和BOOST_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或更高版本。