如何使用 range-for 循环迭代枚举

问题描述 投票:0回答:1
#define DEFINE_ENUM_CLASS(Name, ...) \
enum class Name { __VA_ARGS__ }; \
constexpr const char* Name##Strings[] = { #__VA_ARGS__ };

int main()
{
    DEFINE_ENUM_CLASS(Color, Red, Green, Blue);
    DEFINE_ENUM_CLASS(Bolor, Blue, Cyan, Green);

    for (const char* colorName : ColorStrings)
        std::cout << colorName << std::endl;

    for (const char* bolorName : BolorStrings)
        std::cout << bolorName << std::endl;
}

现在打印不在函数中,我正在努力弄清楚如何使打印这些 for 循环之一的行为发生在函数中。

c++ enums macros c++17
1个回答
1
投票

我们需要一个功能性的“map”宏,用于将枚举标记转换为字符串,或者在它们前面添加枚举名称前缀。该宏是this SO post的适配器,占据了程序的大部分长度。

#include <iostream>

// 2-argument functional map with a fixed first argument

#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL(...)  EVAL4(EVAL4(EVAL4(__VA_ARGS__)))

#define MAP_END(...)
#define MAP_OUT
#define MAP_COMMA ,

#define MAP_GET_END2() 0, MAP_END
#define MAP_GET_END1(...) MAP_GET_END2
#define MAP_GET_END(...) MAP_GET_END1
#define MAP_NEXT0(test, next, ...) next MAP_OUT
#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0)
#define MAP_NEXT(test, next)  MAP_NEXT1(MAP_GET_END test, next)

#define MAP0(f, a, x, peek, ...) f(a,x) MAP_NEXT(peek, MAP1)(f, a, peek, __VA_ARGS__)
#define MAP1(f, a, x, peek, ...) f(a,x) MAP_NEXT(peek, MAP0)(f, a, peek, __VA_ARGS__)

#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
#define MAP_LIST_NEXT(test, next)  MAP_LIST_NEXT1(MAP_GET_END test, next)

#define MAP_LIST0(f, a, x, peek, ...) f(a, x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, a, peek, __VA_ARGS__)
#define MAP_LIST1(f, a, x, peek, ...) f(a, x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, a, peek, __VA_ARGS__)

/**
 * Applies the function macro `f` to first argument `a` and each of the remaining parameters.
 */
#define MAP_TWOARG(f, firstarg, ...) EVAL(MAP1(f, firstarg, __VA_ARGS__, ()()(), ()()(), ()()(), 0))

/**
 * Applies the function macro `f` to first argument `a` and each of the remaining parameters,
 * and inserts commas between the results.
 */
#define MAP_TWOARG_LIST(f, firstarg, ...) EVAL(MAP_LIST1(f, firstarg, __VA_ARGS__, ()()(), ()()(), ()()(), 0))

/**
 * Applies the function macro `f` to first argument `a` and each of the remaining parameters.
 */
#define MAP_ONEARG(f, ...) MAP_TWOARG(f, dummy, __VA_ARGS__)

/**
 * Applies the function macro `f` to first argument `a` and each of the remaining parameters,
 * and inserts commas between the results.
 */
#define MAP_ONEARG_LIST(f, ...) MAP_TWOARG_LIST(f, dummy, __VA_ARGS__)


#define PREFIX_AS_ENUM_CLASS(EnumName, EnumValue) EnumName::EnumValue
#define STR(a,token) #token

template <typename Enum>
constexpr std::underlying_type_t<Enum> to_underlying(Enum x)
{
    return static_cast<std::underlying_type_t<Enum>>(x);
}

#define DEFINE_ENUM_CLASS(Name, ...) \
enum class Name { __VA_ARGS__ }; \
constexpr const Name Name##Values[] = {  \
     MAP_TWOARG_LIST(PREFIX_AS_ENUM_CLASS, Name, __VA_ARGS__)  \
}; \
constexpr char const* Name##Strings[] = { MAP_ONEARG_LIST(STR, __VA_ARGS__) }; \
constexpr char const* as_string(Name x, ...) \
{ \
    return Name##Strings[to_underlying(x)]; \
}

DEFINE_ENUM_CLASS(Color, Red, Green, Blue);
DEFINE_ENUM_CLASS(Bolor, Blue, Cyan, Green);


int main()
{
    std::cout << "Colors:\n";
    for (auto color : ColorValues)
        std::cout << as_string(color) << std::endl;
    std::cout << "-----\nBolors:\n";
    for (auto bolor : BolorValues)
        std::cout << as_string(bolor) << std::endl;
}

而且它有效(GodBolt.org)。


备注:

  • 与其使用所有值的数组,不如只定义一个最大值并从最小值迭代到最大值。毕竟,这不支持跳过或重复任何值。
  • 类似这样的东西 - 事实上更好 - 已经在各种增强的枚举库中可用,例如@heapunderrun 提到的“更好的枚举”“神奇的枚举”。也许最好不要重新发明轮子。
  • 代码是有效的 C++14。
© www.soinside.com 2019 - 2024. All rights reserved.