手动代码中带有 C++20 __VA_OPT__ 错误的类似函数的宏
Function-like macros with C++20 __VA_OPT__ error in manual code
我正在根据伟大的递归宏和 C++20 手册构建代码 __VA_OPT__
:
https://www.scs.stanford.edu/~dm/blog/va-opt.html
密码是
#include <iostream>
#define PARENS ()
// Rescan macro tokens 256 times
#define EXPAND(arg) EXPAND1(EXPAND1(EXPAND1(EXPAND1(arg))))
#define EXPAND1(arg) EXPAND2(EXPAND2(EXPAND2(EXPAND2(arg))))
#define EXPAND2(arg) EXPAND3(EXPAND3(EXPAND3(EXPAND3(arg))))
#define EXPAND3(arg) EXPAND4(EXPAND4(EXPAND4(EXPAND4(arg))))
#define EXPAND4(arg) arg
#define FOR_EACH(macro, ...) \
__VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, __VA_ARGS__)))
#define FOR_EACH_HELPER(macro, a1, ...) \
macro(a1) \
__VA_OPT__(FOR_EACH_AGAIN PARENS (macro, __VA_ARGS__))
#define FOR_EACH_AGAIN() FOR_EACH_HELPER
#define ENUM_CASE(name) case name: return #name;
#define MAKE_ENUM(type, ...) \
enum type { \
__VA_ARGS__ \
}; \
constexpr const char * \
to_cstring(type _e) \
{ \
using enum type; \
switch (_e) { \
FOR_EACH(ENUM_CASE, __VA_ARGS__) \
default: \
return "unknown"; \
} \
}
MAKE_ENUM(MyType, ZERO, ONE, TWO, THREE);
void
test(MyType e)
{
std::cout << to_cstring(e) << " = " << e << std::endl;
}
int
main()
{
test(ZERO);
test(ONE);
test(TWO);
test(THREE);
}
根据 https://en.cppreference.com/w/cpp/compiler_support#C.2B.2B20_features ,MSVC 和 Clang 支持 __VA_OPT__
,GCC 仅部分支持。
但令我惊讶的是,手册中的代码仅适用于 GCC,在其他编译器中会产生错误:
<source>(36): warning C4003: not enough arguments for function-like macro invocation 'FOR_EACH_HELPER'
<source>(36): warning C4003: not enough arguments for function-like macro invocation 'ENUM_CASE'
<source>(36): error C2059: syntax error: 'case'
<source>(36): error C2065: 'ENUM_CASE': undeclared identifier
<source>(36): error C3861: 'FOR_EACH_HELPER': identifier not found
<source>(36): error C2059: syntax error: ')'
<source>(36): error C2146: syntax error: missing ';' before identifier 'default'
https://gcc.godbolt.org/z/PreYcbz7f
这是否意味着手动代码的格式不符合 C++20,需要一些特定于 GCC 的语言扩展?
If you look here,我添加了一个__VA_OPT__
支持检测器。
#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(?)
static_assert(VA_OPT_SUPPORTED);
gcc 和 clang 通过它; MSVC 版本没有。
然后我查看了 clang 的警告;这是一个警告,告诉您是否将 0 个参数传递给 ...
.
这似乎是虚假的。我用 -Wno-gnu-zero-variadic-macro-arguments
禁用了它,你的代码在 clang 中工作。
因此,godbolt 上的任何版本的 MSVC 似乎都不支持 __VA_OPT__
,而 gcc 和 clang 版本支持。该 clang 版本在您选择的警告集中有一个虚假警告。
To fix MSVC, pass /Zc:preprocessor
。默认情况下,它使用传统的预处理器。
我正在根据伟大的递归宏和 C++20 手册构建代码 __VA_OPT__
:
https://www.scs.stanford.edu/~dm/blog/va-opt.html
密码是
#include <iostream>
#define PARENS ()
// Rescan macro tokens 256 times
#define EXPAND(arg) EXPAND1(EXPAND1(EXPAND1(EXPAND1(arg))))
#define EXPAND1(arg) EXPAND2(EXPAND2(EXPAND2(EXPAND2(arg))))
#define EXPAND2(arg) EXPAND3(EXPAND3(EXPAND3(EXPAND3(arg))))
#define EXPAND3(arg) EXPAND4(EXPAND4(EXPAND4(EXPAND4(arg))))
#define EXPAND4(arg) arg
#define FOR_EACH(macro, ...) \
__VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, __VA_ARGS__)))
#define FOR_EACH_HELPER(macro, a1, ...) \
macro(a1) \
__VA_OPT__(FOR_EACH_AGAIN PARENS (macro, __VA_ARGS__))
#define FOR_EACH_AGAIN() FOR_EACH_HELPER
#define ENUM_CASE(name) case name: return #name;
#define MAKE_ENUM(type, ...) \
enum type { \
__VA_ARGS__ \
}; \
constexpr const char * \
to_cstring(type _e) \
{ \
using enum type; \
switch (_e) { \
FOR_EACH(ENUM_CASE, __VA_ARGS__) \
default: \
return "unknown"; \
} \
}
MAKE_ENUM(MyType, ZERO, ONE, TWO, THREE);
void
test(MyType e)
{
std::cout << to_cstring(e) << " = " << e << std::endl;
}
int
main()
{
test(ZERO);
test(ONE);
test(TWO);
test(THREE);
}
根据 https://en.cppreference.com/w/cpp/compiler_support#C.2B.2B20_features ,MSVC 和 Clang 支持 __VA_OPT__
,GCC 仅部分支持。
但令我惊讶的是,手册中的代码仅适用于 GCC,在其他编译器中会产生错误:
<source>(36): warning C4003: not enough arguments for function-like macro invocation 'FOR_EACH_HELPER'
<source>(36): warning C4003: not enough arguments for function-like macro invocation 'ENUM_CASE'
<source>(36): error C2059: syntax error: 'case'
<source>(36): error C2065: 'ENUM_CASE': undeclared identifier
<source>(36): error C3861: 'FOR_EACH_HELPER': identifier not found
<source>(36): error C2059: syntax error: ')'
<source>(36): error C2146: syntax error: missing ';' before identifier 'default'
https://gcc.godbolt.org/z/PreYcbz7f
这是否意味着手动代码的格式不符合 C++20,需要一些特定于 GCC 的语言扩展?
If you look here,我添加了一个__VA_OPT__
支持检测器。
#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(?)
static_assert(VA_OPT_SUPPORTED);
gcc 和 clang 通过它; MSVC 版本没有。
然后我查看了 clang 的警告;这是一个警告,告诉您是否将 0 个参数传递给 ...
.
这似乎是虚假的。我用 -Wno-gnu-zero-variadic-macro-arguments
禁用了它,你的代码在 clang 中工作。
因此,godbolt 上的任何版本的 MSVC 似乎都不支持 __VA_OPT__
,而 gcc 和 clang 版本支持。该 clang 版本在您选择的警告集中有一个虚假警告。
To fix MSVC, pass /Zc:preprocessor
。默认情况下,它使用传统的预处理器。