枚举到现代 C 11 / C 14 / C 17 和未来 C 20 中的字符串

新手上路,请多包涵

与所有其他类似问题相反,这个问题是关于使用新的 C++ 功能。

在阅读了许多答案后,我还没有找到任何答案:

  • 使用 C++11C++14C++17 新特性的优雅方式
  • 或者在 Boost 中可以使用的东西
  • C++20 计划的其他内容

例子

一个例子通常比一个冗长的解释更好。

您可以在 Coliru 上编译和运行此代码段。

(也可以使用 另一个以前的示例

 #include <map>
#include <iostream>

struct MyClass
{
    enum class MyEnum : char {
        AAA = -8,
        BBB = '8',
        CCC = AAA + BBB
    };
};

// Replace magic() by some faster compile-time generated code
// (you're allowed to replace the return type with std::string
// if that's easier for you)
const char* magic (MyClass::MyEnum e)
{
    const std::map<MyClass::MyEnum,const char*> MyEnumStrings {
        { MyClass::MyEnum::AAA, "MyClass::MyEnum::AAA" },
        { MyClass::MyEnum::BBB, "MyClass::MyEnum::BBB" },
        { MyClass::MyEnum::CCC, "MyClass::MyEnum::CCC" }
    };
    auto   it  = MyEnumStrings.find(e);
    return it == MyEnumStrings.end() ? "Out of range" : it->second;
}

int main()
{
   std::cout << magic(MyClass::MyEnum::AAA) <<'\n';
   std::cout << magic(MyClass::MyEnum::BBB) <<'\n';
   std::cout << magic(MyClass::MyEnum::CCC) <<'\n';
}

约束

  • 请不要无价值地重复 其他答案基本链接
  • 请避免臃肿的基于宏的答案,或者尽量减少 #define 开销。
  • 请不要手动 enum -> string 映射。

很高兴有

  • 支持 enum 从非零的数字开始的值
  • 支持负值 enum
  • 支持分片 enum
  • 支持 class enum (C++11)
  • 支持 class enum : <type> 有任何允许的 <type> (C++11)
  • 编译时(不是运行时)转换为字符串,

或者至少在运行时快速执行(例如 std::map 不是一个好主意……) - constexpr (C++11,然后在 C++14/17/20 中放宽) - noexcept (C++11) - C++17 / C++20 友好代码片段

一种可能的想法是使用 C++ 编译器功能在编译时使用基于 variadic template classconstexpr 函数的元编程技巧生成 C++ 代码…

原文由 oHo 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

Magic Enum 仅标头库为 C++17 的枚举(到字符串、从字符串、迭代)提供静态反射。

 #include <magic_enum.hpp>

enum Color { RED = 2, BLUE = 4, GREEN = 8 };

Color color = Color::RED;
auto color_name = magic_enum::enum_name(color);
// color_name -> "RED"

std::string color_name{"GREEN"};
auto color = magic_enum::enum_cast<Color>(color_name)
if (color.has_value()) {
  // color.value() -> Color::GREEN
};

有关更多示例,请查看主页存储库 https://github.com/Neargye/magic_enum

缺点在哪里?

该库使用特定于编译器的 hack(基于 __PRETTY_FUNCTION__ / __FUNCSIG__ ),适用于 Clang >= 5、MSVC >= 15.3 和 GCC >= 9。

枚举值必须在 [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX] 范围内。

  • 默认情况下 MAGIC_ENUM_RANGE_MIN = -128MAGIC_ENUM_RANGE_MAX = 128

  • 如果默认情况下需要所有枚举类型的另一个范围,请重新定义宏 MAGIC_ENUM_RANGE_MINMAGIC_ENUM_RANGE_MAX

  • MAGIC_ENUM_RANGE_MIN 必须小于或等于 0 并且必须大于 INT16_MIN

  • MAGIC_ENUM_RANGE_MAX 必须大于 0 并且必须小于 INT16_MAX

  • 如果需要特定枚举类型的另一个范围,请为必要的枚举类型添加专门化 enum_range。

   #include <magic_enum.hpp>

  enum number { one = 100, two = 200, three = 300 };

  namespace magic_enum {
  template <>
    struct enum_range<number> {
      static constexpr int min = 100;
      static constexpr int max = 300;
  };
  }

原文由 Neargye 发布,翻译遵循 CC BY-SA 4.0 许可协议

我对与此一起提出的所有花哨的框架(宏、模板和类)不太满意,因为我认为使用它们会使代码更难理解,并且会增加编译时间并隐藏错误。一般来说,我想要一个简单的解决方案来解决这个问题。添加额外的 100 行代码并不简单。

原始问题中给出的示例非常接近我在生产中实际使用的代码。相反,我只想对原始示例查找函数提出一些小的改进:

 const std::string& magic(MyClass::MyEnum e)
{
    static const std::string OUT_OF_RANGE = "Out of range";
    #define ENTRY(v) { MyClass::MyEnum::v, "MyClass::MyEnum::" #v }
    static const std::unordered_map<MyClass::MyEnum, std::string> LOOKUP {
        ENTRY(AAA),
        ENTRY(BBB),
        ENTRY(CCC),
    };
    #undef ENTRY
    auto it  = LOOKUP.find(e);
    return ((it != LOOKUP.end()) ? it->second : OUT_OF_RANGE);
}

具体来说:

  1. 内部数据结构现在是“静态”和“常量”。这些是不变的,所以没有必要在每次调用函数时都构造它们,这样做效率很低。相反,它们仅在第一次调用该函数时构建。
  2. 返回值现在是 ‘const std::string&‘。此函数将仅返回对已分配的具有“静态”生命周期的 std::string 对象的引用,因此在返回时无需复制它们。
  3. 对于 O(1) 访问,地图类型现在是 ‘std::unordered_map’,而不是 std::map 的 O(log(N)) 访问。
  4. 使用 ENTRY 宏可以使代码更加简洁,还可以避免在字符串文字中输入名称时出现的拼写错误的潜在问题。 (如果程序员输入了一个无效的名字,将会导致编译器错误。)

原文由 Some Guy 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题