35

在 C++20 中,__VA_OPT__如果参数的数量大于零,预处理器支持作为可选扩展可变参数宏中的标记的一种方式。(这消除了对##__VA_ARGS__GCC 扩展的需要,这是一种不可移植且丑陋的 hack。)

Clang SVN 已经实现了这个特性,但是他们没有为它添加一个特性测试宏。任何聪明的预处理器黑客能否找到一种方法来检测是否存在__VA_OPT__支持,而不会导致硬错误或可移植性警告?

4

4 回答 4

47

受到克里斯回答的启发。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_OPT_SUPPORTED_I(?)展开为PP_THIRD_ARG(__VA_OPT__(,),true,false,),第三个参数为false


编辑:正如Edward Diener 的回答__VA_OPT__所指出的,如果-pedantic模式已打开__VA_OPT__且未启用(例如在 中),则GCC >= 8 会在看到 时发出警告或错误-std=c++17。这是GCC 错误 98859。可能必须使用特殊情况的 GCC 来避免这种诊断。

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

1. 正如 chris 所说,如果__VA_OPT__(,)展开为,,将有 2 个空参数,否则将有 1 个参数。所以可以测试一下PP_NARG(__VA_OPT__(,)) == 2,哪里PP_NARG有一个宏来计算参数个数。为了适应这种测试,PP_NARG可以简化和内联的定义。

于 2017-12-31T21:07:21.630 回答
10

尽管您可以改进它,但以下内容应该可以工作:

#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 主干实际上在 C++17 中将此评估为 1,但也在__VA_OPT__该模式下处理。

它的作用是根据参数的数量BOOST_PP_OVERLOAD调用_1or_2版本。_II如果__VA_OPT__(,)展开为,,将有 2 个空参数。如果没有,将有 1 个空参数。我们总是用参数列表调用这个宏,所以任何支持的编译器__VA_OPT__都应该将它扩展为,.

当然,Boost.PP 依赖不是强制性的。一个简单的 1-or-2-argOVERLOAD宏应该很容易替换。失去一些普遍性以使其更简单:

#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 仍会发出警告。

于 2017-12-31T20:51:43.740 回答
6

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

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

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

于 2019-09-30T13:31:39.003 回答
1

如另一个答案中所述,您可以编写自己的OVERLOAD宏。BOOST_PP_OVERLOAD由两部分组成,BOOST_PP_CATBOOST_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
于 2017-12-31T21:08:00.733 回答