在 `=` 处拆分宏参数以实现智能枚举

Split macro parameters at `=` for smart enum implementation

有没有办法写一个宏 ENUM 使得 ENUM(Animal, Dog, Cat = 5, Horse = 2) 扩展为

enum class Animal { Dog, Cat = 5, Horse = 2 }; 

template<typename EnumT> 
constexpr std::array<std::pair<std::string_view, Animal>, 3> get_enum_map() { 
    return {{ {"Dog", Animal::Dog} , {"Cat", Animal::Cat} , {"Horse", Animal::Horse} }};
}

注意,难点在于宏参数NAME = VAL需要在=处拆分得到NAME,没有VAL,这样我们就可以定义get_enum_map()干净利落。 可以这样做吗?


作为参考,下面的代码实现了 ENUM(Animal, Dog, Cat, Horse)(类似于 wise_enum),但无法为枚举指定值。我见过诸如 ENUM(Animal, Dog, (Cat, 5), (Horse , 2)) 之类的示例,其中各个参数被打包为 (NAME, VAL)。但我很好奇是否有办法通过 NAME = VAL.

#include <string_view>
#include <array>

#define ENUM_EXPAND(x) x
#define ENUM_COMMA() ,
#define ENUM_STR_CONCAT(a, ...) ENUM_STR_CONCAT_(a, __VA_ARGS__)
#define ENUM_STR_CONCAT_(a, ...) a##__VA_ARGS__
#define ENUM_ARG_COUNT_(_1, _2, _3, _4, _5, X,...) X
#define ENUM_ARG_COUNT(...) ENUM_EXPAND(ENUM_ARG_COUNT_(__VA_ARGS__, 5, 4, 3, 2, 1))
#define ENUM_LOOP_1(f, a, b, c)       f(a, c)
#define ENUM_LOOP_2(f, a, b, c, ...)  f(a, c) b() ENUM_EXPAND(ENUM_LOOP_1(f, a, b, __VA_ARGS__))
#define ENUM_LOOP_3(f, a, b, c, ...)  f(a, c) b() ENUM_EXPAND(ENUM_LOOP_2(f, a, b, __VA_ARGS__))
#define ENUM_LOOP_4(f, a, b, c, ...)  f(a, c) b() ENUM_EXPAND(ENUM_LOOP_3(f, a, b, __VA_ARGS__))
#define ENUM_LOOP_5(f, a, b, c, ...)  f(a, c) b() ENUM_EXPAND(ENUM_LOOP_4(f, a, b, __VA_ARGS__))
#define ENUM_PAIR(name, x) { #x, name::x}
#define ENUM_IMPL(name, loop_func_name, ...) \
    enum class name { __VA_ARGS__ }; template<typename EnumT> \
    constexpr std::array<std::pair<std::string_view, name>, ENUM_ARG_COUNT(__VA_ARGS__)> get_enum_map() { \
        return {{ ENUM_EXPAND(loop_func_name(ENUM_PAIR, name, ENUM_COMMA, __VA_ARGS__)) }}; }
#define ENUM(name, ...) ENUM_IMPL(name, ENUM_STR_CONCAT(ENUM_LOOP_, ENUM_ARG_COUNT(__VA_ARGS__)), __VA_ARGS__)

ENUM(Animal, Dog, Cat, Horse)

int main() {
    constexpr auto arr = get_enum_map<Animal>();

    constexpr auto el0 = std::get<0>(arr[1]);
    constexpr auto el1 = std::get<1>(arr[1]);

    static_assert(el0 == "Cat");
    static_assert(el1 == Animal::Cat);

    return 0;
}

正如评论中指出的那样,无法在某个字符处拆分宏输入,例如 =

然而,better-enums 库通过 _eat_assign 帮助程序 class 吸收(或“吃掉”)分配来使用巧妙的技巧。例如

(EnumType)((better_enums::_eat_assign<EnumType>)EnumType::Name = EnumValue)

减少到 EnumType::Name,有效地删除了分配。参见 _eat_assign definition