命名宏参数与__VA_ARGS__的区别



我已经写了一些实用程序宏来将enum值转换为std::string:

#include <map>
#include <string>
#define MY_map_entry_(name, value) {#value, name::value}
#define MY_map_2(name, v1, v2) MY_map_entry_(name, v1), MY_map_entry_(name, v2)
#define MY_map_3(name, v1, v2, v3) MY_map_entry_(name, v1), MY_map_2(name, v2, v3)

这完全按预期工作:

enum class rgb { red, green, blue };
static const std::map<std::string, rgb> string_to_rgb{ MY_map_3(rgb, red, green, blue) };
// { {"red", rgb::red}, {"green", rgb::green}, {"blue", rgb::blue} }

然而,它开始变得有点乏味的编写宏;所以我想试试__VA_ARGS__

#define MY_map_4(name, v1, ...) MY_map_entry_(name, v1), MY_map_3(name, __VA_ARGS__)

用例为

enum class cymk { cyan, yellow, magenta, black };
static const std::map<std::string, cymk> string_to_cymk{ MY_map_4(cymk, cyan, yellow, magenta, black) };

这不起作用,没有__VA_ARGS__的类似宏可以

#define MY_map_4(name, v1, v2, v3, v4) MY_map_entry_(name, v1), MY_map_3(name, v2, v3, v4)

有没有办法用__VA_ARGS__来写MY_map_4?

(当然,在不使用任何宏的情况下这样做会很好,或者至少接近;但我认为那是不可能的。

您实际需要的是x -宏而不是可变宏:

#define ENUM_VAL(X) X
#define ENUM_TO_STRING(E, X) {E::X, #X}
#define STRING_TO_ENUM(E, X) {#X, E::X}
enum class rgb
{
#define RGB_VALS E(red), E(green), E(blue)
#define E(X) ENUM_VAL(X)
RGB_VALS
#undef E
};
static const std::map<rgb, std::string> rgb_to_string
({
#define E(X) ENUM_TO_STRING(rgb, X)
RGB_VALS
#undef E
});
static const std::map<std::string, rgb> string_to_rgb
({
#define E(X) STRING_TO_ENUM(rgb, X)
RGB_VALS
#undef E
});
#undef RGB_VALS

背后的思想是:在从属宏中定义枚举值,该从属宏使用helper宏实际生成代码。

使用辅助宏,您可以交换从属宏的行为,并获得不同上下文所需的结果。

你的宏没有任何问题。GCC和Clang接受它的开箱即用,而MSVC需要/Zc:preprocessor(它支持标准一致性的预处理器;默认预处理器有bug)。


宏可以改进为支持无限枚举大小(不需要显式指定),并声明枚举和数组。这里有一个例子。(需要c++ 20, Clang额外需要-Wno-gnu-zero-variadic-macro-arguments。MSVC要求/Zc:preprocessor.)

#define ENUM(name, seq) 
enum class name { END(ENUM_DECL_A seq) }; 
std::pair<std::string, name> CAT(name, _values)[] = { END(ENUM_ARR_A seq) };
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
#define CAT(x, y) CAT_(x, y)
#define CAT_(x, y) x##y
#define STR(...) STR_(__VA_ARGS__)
#define STR_(...) #__VA_ARGS__
#define ENUM_DECL_A(...) ENUM_DECL_BODY(__VA_ARGS__) ENUM_DECL_B
#define ENUM_DECL_B(...) ENUM_DECL_BODY(__VA_ARGS__) ENUM_DECL_A
#define ENUM_DECL_A_END
#define ENUM_DECL_B_END
#define ENUM_DECL_BODY(name, ...) name __VA_OPT__(= __VA_ARGS__),
#define ENUM_ARR_A(...) ENUM_ARR_BODY(__VA_ARGS__) ENUM_ARR_B
#define ENUM_ARR_B(...) ENUM_ARR_BODY(__VA_ARGS__) ENUM_ARR_A
#define ENUM_ARR_A_END
#define ENUM_ARR_B_END
#define ENUM_ARR_BODY(name, ...) STR(name),

然后,ENUM(A, (x)(y,42)(z))展开为:

enum class A
{
x,
y = 42,
z,
};
std::pair<std::string, A> A_values[] = { "x", "y", "z", };

相关内容

  • 没有找到相关文章

最新更新