Search code examples
c++stringenumsc++17c++20

How to convert an enum to a string in modern C++


Contrary to all other similar questions, this question is about using the new C++ features.

After reading many answers, I did not yet find any:

Example

An example is often better than a long explanation.
You can compile and run this snippet on Coliru.
(Another former example is also available)

#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';
}

Constraints

  • Please no valueless duplication of other answers or basic link.
  • Please avoid bloat macro-based answer, or try to reduce the #define overhead as minimum as possible.
  • Please no manual enum -> string mapping.

Nice to have

  • Support enum values starting from a number different from zero
  • Support negative enum values
  • Support fragmented enum values
  • Support class enum (C++11)
  • Support class enum : <type> having any allowed <type> (C++11)
  • Compile-time (not run-time) conversions to a string,
    or at least fast execution at run-time (e.g. std::map is not a great idea...)
  • constexpr (C++11, then relaxed in C++14/17/20)
  • noexcept (C++11)
  • C++17/C++20 friendly snippet

One possible idea could be using the C++ compiler capabilities to generate C++ code at compilation-time using meta-programming tricks based on variadic template class and constexpr functions...


Solution

  • Magic Enum header-only library provides static reflection for enums (to string, from string, iteration) for C++17.

    (Disclosure: I'm the author of the library.)

    #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
    };
    

    For more examples check home repository https://github.com/Neargye/magic_enum.

    Where is the drawback?

    This library uses a compiler-specific hack (based on __PRETTY_FUNCTION__ / __FUNCSIG__), which works on Clang >= 5, MSVC >= 15.3 and GCC >= 9.

    Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX].

    • By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128.

    • If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX.

    • MAGIC_ENUM_RANGE_MIN must be less or equals than 0 and must be greater than INT16_MIN.

    • MAGIC_ENUM_RANGE_MAX must be greater than 0 and must be less than INT16_MAX.

    • If need another range for specific enum type, add specialization enum_range for necessary enum type.

      #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;
      };
      }