Eri*_*ler 25 c++ c-preprocessor c++20
在C++ 20中,__VA_OPT__如果参数的数量大于零,则预处理器支持作为可选地扩展可变参数宏中的标记的方法.(这消除了对##__VA_ARGS__GCC扩展的需求,这是一个不可移植且丑陋的黑客.)
Clang SVN已实现此功能,但尚未为其添加功能测试宏.任何聪明的预处理器黑客能否找到一种方法来检测__VA_OPT__支持的存在与否,而不会导致硬错误或可移植性警告?
cpp*_*ner 31
灵感来自克里斯的回答.
#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(?)
Run Code Online (Sandbox Code Playgroud)
如果__VA_OPT__支持,则VA_OPT_SUPPORTED_I(?)扩展为PP_THIRD_ARG(,,true,false,),所以第三个参数是true; 否则,VA_OPT_SUPPORTED_I(?)扩展为PP_THIRD_ARG(__VA_OPT__(,),true,false,),第三个参数是false.
类似下面的内容应该可行,但您可以改进它:
#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(?)
Run Code Online (Sandbox Code Playgroud)
在Clang主干上,在C++ 2a模式下评估为1,在C++ 17模式下评估为0.GCC trunk实际上在C++ 17中将其计算为1,但也在__VA_OPT__该模式下处理.
这样做用于根据参数计数BOOST_PP_OVERLOAD调用_1或者_2版本_II.如果__VA_OPT__(,)扩展为,,则会有2个空参数.如果没有,将有1个空参数.我们总是使用参数列表调用此宏,因此任何支持的编译器__VA_OPT__都应始终将其扩展为,.
当然,Boost.PP依赖关系不是强制性的.一个简单的1或2-arg 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__(,))
Run Code Online (Sandbox Code Playgroud)
Clang有一个可移植性警告:
警告:可变参数宏与C++ 98不兼容[-Wc ++ 98-compat-pedantic]
如果没有C++ 11可变参数宏支持,我不知道这种检测是否可行.您可以考虑假设不支持__cplusplus低于C++ 11的值,但Clang仍然会在包含此类检查时发出警告.
小智 6
上面最流行的答案中指定的解决方案的问题是,如果 __VA_OPT__ 在其 C++20 模式之外使用,则编译器可以自由地发出警告甚至错误,因为该词是编译器保留字,因为它以双下划线开头和结尾。事实上,我发现 gcc 会根据所使用的编译器选项发出警告或错误,尽管在大多数编译情况下它通常不会这样做。因此,围绕 C++20 的当前测试的任何解决方案,例如:
# if defined(__cplusplus) && __cplusplus > 201703L
// Solution
#endif
Run Code Online (Sandbox Code Playgroud)
是一个更保守的解决方案,尽管它将测试限制为 C++20 或更高版本。