enum to string in modern C++11 / C++14 / C++17 and future C++20

后端 未结 28 1963
逝去的感伤
逝去的感伤 2020-11-22 16:57

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

  • 2008 c Is there a simple way to convert C++ enum to string?
  • <
相关标签:
28条回答
  • 2020-11-22 17:28

    Very simple solution with one big constraint: you can't assign custom values to enum values, but with the right regex, you could. you could also add a map to translate them back to enum values without much more effort:

    #include <vector>
    #include <string>
    #include <regex>
    #include <iterator>
    
    std::vector<std::string> split(const std::string& s, 
                                   const std::regex& delim = std::regex(",\\s*"))
    {
        using namespace std;
        vector<string> cont;
        copy(regex_token_iterator<string::const_iterator>(s.begin(), s.end(), delim, -1), 
             regex_token_iterator<string::const_iterator>(),
             back_inserter(cont));
        return cont;
    }
    
    #define EnumType(Type, ...)     enum class Type { __VA_ARGS__ }
    
    #define EnumStrings(Type, ...)  static const std::vector<std::string> \
                                    Type##Strings = split(#__VA_ARGS__);
    
    #define EnumToString(Type, ...) EnumType(Type, __VA_ARGS__); \
                                    EnumStrings(Type, __VA_ARGS__)
    

    Usage example:

    EnumToString(MyEnum, Red, Green, Blue);
    
    0 讨论(0)
  • 2020-11-22 17:28

    What about a simple streaming overload? You still have to maintain the mapping if you don't want to do some macro magic, but I find it cleaner than your original solution.

    #include <cstdint>  // for std::uint_fast8_t
    #include <array>
    #include <string>
    #include <iostream>
    
    enum class MyEnum : std::uint_fast8_t {
       AAA,
       BBB,
       CCC,
    };
    
    std::ostream& operator<<(std::ostream& str, MyEnum type)
    {
        switch(type)
        {
        case MyEnum::AAA: str << "AAA"; break;
        case MyEnum::BBB: str << "BBB"; break;
        case MyEnum::CCC: str << "CCC"; break;
        default: break;
        }
        return str;
    }
    
    int main()
    {
       std::cout << MyEnum::AAA <<'\n';
    }
    
    0 讨论(0)
  • 2020-11-22 17:29

    For C++17 C++20, you will be interested in the work of the Reflection Study Group (SG7). There is a parallel series of papers covering wording (P0194) and rationale, design and evolution (P0385). (Links resolve to the latest paper in each series.)

    As of P0194r2 (2016-10-15), the syntax would use the proposed reflexpr keyword:

    meta::get_base_name_v<
      meta::get_element_m<
        meta::get_enumerators_m<reflexpr(MyEnum)>,
        0>
      >
    

    For example (adapted from Matus Choclik's reflexpr branch of clang):

    #include <reflexpr>
    #include <iostream>
    
    enum MyEnum { AAA = 1, BBB, CCC = 99 };
    
    int main()
    {
      auto name_of_MyEnum_0 = 
        std::meta::get_base_name_v<
          std::meta::get_element_m<
            std::meta::get_enumerators_m<reflexpr(MyEnum)>,
            0>
        >;
    
      // prints "AAA"
      std::cout << name_of_MyEnum_0 << std::endl;
    }
    

    Static reflection failed to make it into C++17 (rather, into the probably-final draft presented at the November 2016 standards meeting in Issaquah) but there is confidence that it will make it into C++20; from Herb Sutter's trip report:

    In particular, the Reflection study group reviewed the latest merged static reflection proposal and found it ready to enter the main Evolution groups at our next meeting to start considering the unified static reflection proposal for a TS or for the next standard.

    0 讨论(0)
  • 2020-11-22 17:30

    As per request from the OP, here a stripped down version of the ugly macro solution based on Boost Preprosessor and Variadic Macros.

    It allows for a simple list like syntax of the enumerator elements along with setting values for specific elements so that

    XXX_ENUM(foo,(a,b,(c,42)));
    

    expands to

    enum foo {
        a,
        b,
        c=42
    };
    

    Alongside with the necessary functions to output and do some conversion back. This macro has been around here for ages, and I am not totally sure that its the most efficient way, or that it is a conforming way, but it has ever since been working

    The complete code can be seen in action at both Ideone and Coliru.

    Its gargantuan ugliness is above; I would have put it behind spoilers to protect your eyes, if I knew how, but markdown doesn't like me.

    The library (merged within one single header file)

    #include <boost/preprocessor.hpp>
    #include <string>
    #include <unordered_map>
    
    namespace xxx
    {
    
    template<class T>
    struct enum_cast_adl_helper { };
    
    template<class E>
    E enum_cast( const std::string& s )
    {
        return do_enum_cast(s,enum_cast_adl_helper<E>());
    }
    
    template<class E>
    E enum_cast( const char* cs )
    {
        std::string s(cs);
        return enum_cast<E>(s);
    }
    
    } // namespace xxx
    
    #define XXX_PP_ARG_N(                             \
              _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
             _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
             _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
             _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
             _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
             _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
             _61,_62,_63,N,...) N
    
    #define XXX_PP_RSEQ_N()                 \
             63,62,61,60,                   \
             59,58,57,56,55,54,53,52,51,50, \
             49,48,47,46,45,44,43,42,41,40, \
             39,38,37,36,35,34,33,32,31,30, \
             29,28,27,26,25,24,23,22,21,20, \
             19,18,17,16,15,14,13,12,11,10, \
             9,8,7,6,5,4,3,2,1,0 
    
    #define XXX_PP_NARG_(...) XXX_PP_ARG_N(__VA_ARGS__)
    #define XXX_PP_NARG(...)  XXX_PP_NARG_(__VA_ARGS__,XXX_PP_RSEQ_N())
    #define XXX_TUPLE_SIZE_INTERNAL(TUPLE) XXX_PP_NARG TUPLE
    
    #define XXX_TUPLE_CHOICE(i)                            \
      BOOST_PP_APPLY(                                      \
        BOOST_PP_TUPLE_ELEM(                               \
          25, i, (                                         \
            (0), (1), (2), (3), (4), (5), (6), (7), (8),   \
            (9), (10), (11), (12), (13), (14), (15), (16), \
            (17), (18), (19), (20), (21), (22), (23), (24) \
      ) ) )
    
    #define BOOST_PP_BOOL_00  BOOST_PP_BOOL_0
    #define BOOST_PP_BOOL_01  BOOST_PP_BOOL_1
    #define BOOST_PP_BOOL_02  BOOST_PP_BOOL_2
    #define BOOST_PP_BOOL_03  BOOST_PP_BOOL_3
    #define BOOST_PP_BOOL_04  BOOST_PP_BOOL_4
    #define BOOST_PP_BOOL_05  BOOST_PP_BOOL_5
    #define BOOST_PP_BOOL_06  BOOST_PP_BOOL_6
    #define BOOST_PP_BOOL_07  BOOST_PP_BOOL_7
    #define BOOST_PP_BOOL_08  BOOST_PP_BOOL_8
    #define BOOST_PP_BOOL_09  BOOST_PP_BOOL_9
    #define BOOST_PP_BOOL_010 BOOST_PP_BOOL_10
    #define BOOST_PP_BOOL_011 BOOST_PP_BOOL_11
    #define BOOST_PP_BOOL_012 BOOST_PP_BOOL_12
    #define BOOST_PP_BOOL_013 BOOST_PP_BOOL_13
    #define BOOST_PP_BOOL_014 BOOST_PP_BOOL_14
    #define BOOST_PP_BOOL_015 BOOST_PP_BOOL_15
    #define BOOST_PP_BOOL_016 BOOST_PP_BOOL_16
    #define BOOST_PP_BOOL_017 BOOST_PP_BOOL_17
    #define BOOST_PP_BOOL_018 BOOST_PP_BOOL_18
    #define BOOST_PP_BOOL_019 BOOST_PP_BOOL_19
    #define BOOST_PP_BOOL_020 BOOST_PP_BOOL_20
    #define BOOST_PP_BOOL_021 BOOST_PP_BOOL_21
    #define BOOST_PP_BOOL_022 BOOST_PP_BOOL_22
    #define BOOST_PP_BOOL_023 BOOST_PP_BOOL_23
    #define BOOST_PP_BOOL_024 BOOST_PP_BOOL_24
    #define BOOST_PP_BOOL_025 BOOST_PP_BOOL_25
    #define BOOST_PP_BOOL_026 BOOST_PP_BOOL_26
    #define BOOST_PP_BOOL_027 BOOST_PP_BOOL_27
    #define BOOST_PP_BOOL_028 BOOST_PP_BOOL_28
    #define BOOST_PP_BOOL_029 BOOST_PP_BOOL_29
    #define BOOST_PP_BOOL_030 BOOST_PP_BOOL_30
    #define BOOST_PP_BOOL_031 BOOST_PP_BOOL_31
    #define BOOST_PP_BOOL_032 BOOST_PP_BOOL_32
    #define BOOST_PP_BOOL_033 BOOST_PP_BOOL_33
    #define BOOST_PP_BOOL_034 BOOST_PP_BOOL_34
    #define BOOST_PP_BOOL_035 BOOST_PP_BOOL_35
    #define BOOST_PP_BOOL_036 BOOST_PP_BOOL_36
    #define BOOST_PP_BOOL_037 BOOST_PP_BOOL_37
    #define BOOST_PP_BOOL_038 BOOST_PP_BOOL_38
    #define BOOST_PP_BOOL_039 BOOST_PP_BOOL_39
    #define BOOST_PP_BOOL_040 BOOST_PP_BOOL_40
    #define BOOST_PP_BOOL_041 BOOST_PP_BOOL_41
    #define BOOST_PP_BOOL_042 BOOST_PP_BOOL_42
    #define BOOST_PP_BOOL_043 BOOST_PP_BOOL_43
    #define BOOST_PP_BOOL_044 BOOST_PP_BOOL_44
    #define BOOST_PP_BOOL_045 BOOST_PP_BOOL_45
    #define BOOST_PP_BOOL_046 BOOST_PP_BOOL_46
    #define BOOST_PP_BOOL_047 BOOST_PP_BOOL_47
    #define BOOST_PP_BOOL_048 BOOST_PP_BOOL_48
    #define BOOST_PP_BOOL_049 BOOST_PP_BOOL_49
    #define BOOST_PP_BOOL_050 BOOST_PP_BOOL_50
    #define BOOST_PP_BOOL_051 BOOST_PP_BOOL_51
    #define BOOST_PP_BOOL_052 BOOST_PP_BOOL_52
    #define BOOST_PP_BOOL_053 BOOST_PP_BOOL_53
    #define BOOST_PP_BOOL_054 BOOST_PP_BOOL_54
    #define BOOST_PP_BOOL_055 BOOST_PP_BOOL_55
    #define BOOST_PP_BOOL_056 BOOST_PP_BOOL_56
    #define BOOST_PP_BOOL_057 BOOST_PP_BOOL_57
    #define BOOST_PP_BOOL_058 BOOST_PP_BOOL_58
    #define BOOST_PP_BOOL_059 BOOST_PP_BOOL_59
    #define BOOST_PP_BOOL_060 BOOST_PP_BOOL_60
    #define BOOST_PP_BOOL_061 BOOST_PP_BOOL_61
    #define BOOST_PP_BOOL_062 BOOST_PP_BOOL_62
    #define BOOST_PP_BOOL_063 BOOST_PP_BOOL_63
    
    #define BOOST_PP_DEC_00  BOOST_PP_DEC_0
    #define BOOST_PP_DEC_01  BOOST_PP_DEC_1
    #define BOOST_PP_DEC_02  BOOST_PP_DEC_2
    #define BOOST_PP_DEC_03  BOOST_PP_DEC_3
    #define BOOST_PP_DEC_04  BOOST_PP_DEC_4
    #define BOOST_PP_DEC_05  BOOST_PP_DEC_5
    #define BOOST_PP_DEC_06  BOOST_PP_DEC_6
    #define BOOST_PP_DEC_07  BOOST_PP_DEC_7
    #define BOOST_PP_DEC_08  BOOST_PP_DEC_8
    #define BOOST_PP_DEC_09  BOOST_PP_DEC_9
    #define BOOST_PP_DEC_010 BOOST_PP_DEC_10
    #define BOOST_PP_DEC_011 BOOST_PP_DEC_11
    #define BOOST_PP_DEC_012 BOOST_PP_DEC_12
    #define BOOST_PP_DEC_013 BOOST_PP_DEC_13
    #define BOOST_PP_DEC_014 BOOST_PP_DEC_14
    #define BOOST_PP_DEC_015 BOOST_PP_DEC_15
    #define BOOST_PP_DEC_016 BOOST_PP_DEC_16
    #define BOOST_PP_DEC_017 BOOST_PP_DEC_17
    #define BOOST_PP_DEC_018 BOOST_PP_DEC_18
    #define BOOST_PP_DEC_019 BOOST_PP_DEC_19
    #define BOOST_PP_DEC_020 BOOST_PP_DEC_20
    #define BOOST_PP_DEC_021 BOOST_PP_DEC_21
    #define BOOST_PP_DEC_022 BOOST_PP_DEC_22
    #define BOOST_PP_DEC_023 BOOST_PP_DEC_23
    #define BOOST_PP_DEC_024 BOOST_PP_DEC_24
    #define BOOST_PP_DEC_025 BOOST_PP_DEC_25
    #define BOOST_PP_DEC_026 BOOST_PP_DEC_26
    #define BOOST_PP_DEC_027 BOOST_PP_DEC_27
    #define BOOST_PP_DEC_028 BOOST_PP_DEC_28
    #define BOOST_PP_DEC_029 BOOST_PP_DEC_29
    #define BOOST_PP_DEC_030 BOOST_PP_DEC_30
    #define BOOST_PP_DEC_031 BOOST_PP_DEC_31
    #define BOOST_PP_DEC_032 BOOST_PP_DEC_32
    #define BOOST_PP_DEC_033 BOOST_PP_DEC_33
    #define BOOST_PP_DEC_034 BOOST_PP_DEC_34
    #define BOOST_PP_DEC_035 BOOST_PP_DEC_35
    #define BOOST_PP_DEC_036 BOOST_PP_DEC_36
    #define BOOST_PP_DEC_037 BOOST_PP_DEC_37
    #define BOOST_PP_DEC_038 BOOST_PP_DEC_38
    #define BOOST_PP_DEC_039 BOOST_PP_DEC_39
    #define BOOST_PP_DEC_040 BOOST_PP_DEC_40
    #define BOOST_PP_DEC_041 BOOST_PP_DEC_41
    #define BOOST_PP_DEC_042 BOOST_PP_DEC_42
    #define BOOST_PP_DEC_043 BOOST_PP_DEC_43
    #define BOOST_PP_DEC_044 BOOST_PP_DEC_44
    #define BOOST_PP_DEC_045 BOOST_PP_DEC_45
    #define BOOST_PP_DEC_046 BOOST_PP_DEC_46
    #define BOOST_PP_DEC_047 BOOST_PP_DEC_47
    #define BOOST_PP_DEC_048 BOOST_PP_DEC_48
    #define BOOST_PP_DEC_049 BOOST_PP_DEC_49
    #define BOOST_PP_DEC_050 BOOST_PP_DEC_50
    #define BOOST_PP_DEC_051 BOOST_PP_DEC_51
    #define BOOST_PP_DEC_052 BOOST_PP_DEC_52
    #define BOOST_PP_DEC_053 BOOST_PP_DEC_53
    #define BOOST_PP_DEC_054 BOOST_PP_DEC_54
    #define BOOST_PP_DEC_055 BOOST_PP_DEC_55
    #define BOOST_PP_DEC_056 BOOST_PP_DEC_56
    #define BOOST_PP_DEC_057 BOOST_PP_DEC_57
    #define BOOST_PP_DEC_058 BOOST_PP_DEC_58
    #define BOOST_PP_DEC_059 BOOST_PP_DEC_59
    #define BOOST_PP_DEC_060 BOOST_PP_DEC_60
    #define BOOST_PP_DEC_061 BOOST_PP_DEC_61
    #define BOOST_PP_DEC_062 BOOST_PP_DEC_62
    #define BOOST_PP_DEC_063 BOOST_PP_DEC_63
    
    #define XXX_TO_NUMx(x) 0 ## x
    #define XXX_TO_NUM(x) BOOST_PP_ADD(0,XXX_TO_NUMx(x))
    #define XXX_STRINGIZEX(x) # x
    #define XXX_VSTRINGIZE_SINGLE(a,b,x) XXX_STRINGIZE(x)
    #define XXX_VSTRINGIZE_TUPLE(tpl) XXX_TUPLE_FOR_EACH(XXX_VSTRINGIZE_SINGLE,,tpl)
    #define XXX_TUPLE_SIZE(TUPLE) XXX_TO_NUM(XXX_TUPLE_CHOICE(XXX_TUPLE_SIZE_INTERNAL(TUPLE)))
    #define XXX_TUPLE_FOR_EACH(MACRO,DATA,TUPLE) BOOST_PP_LIST_FOR_EACH(MACRO,DATA,BOOST_PP_TUPLE_TO_LIST(XXX_TUPLE_SIZE(TUPLE),TUPLE))
    #define XXX_STRINGIZE(x) XXX_STRINGIZEX(x)
    #define XXX_VSTRINGIZE(...) XXX_VSTRINGIZE_TUPLE((__VA_ARGS__))
    #define XXX_CAST_TO_VOID_ELEMENT(r,data,elem) (void)(elem);
    #define XXX_CAST_TO_VOID_INTERNAL(TUPLE) XXX_TUPLE_FOR_EACH(XXX_CAST_TO_VOID_ELEMENT,,TUPLE)    
    #define XXX_CAST_TO_VOID(...) XXX_CAST_TO_VOID_INTERNAL((__VA_ARGS__))
    #define XXX_ENUM_EXTRACT_SP(en) BOOST_PP_TUPLE_ELEM(XXX_TUPLE_SIZE(en),0,en) = BOOST_PP_TUPLE_ELEM(XXX_TUPLE_SIZE(en),1,en)
    #define XXX_ENUM_ELEMENT(r,data,elem) BOOST_PP_IF( XXX_TUPLE_SIZE(elem), XXX_ENUM_EXTRACT_SP(elem), elem) ,
    #define XXX_ENUM_EXTRACT_ELEMENT(en) BOOST_PP_TUPLE_ELEM(XXX_TUPLE_SIZE(en),0,en)
    #define XXX_ENUM_CASE_ELEMENT(en) BOOST_PP_IF( XXX_TUPLE_SIZE(en), XXX_ENUM_EXTRACT_ELEMENT(en), en )
    #define XXX_ENUM_CASE(r,data,elem) case data :: XXX_ENUM_CASE_ELEMENT(elem) : return #data "::" XXX_STRINGIZE(XXX_ENUM_CASE_ELEMENT(elem));
    #define XXX_ENUM_IFELSE(r,data,elem) else if( en == data :: XXX_ENUM_CASE_ELEMENT(elem)) { return #data "::" XXX_STRINGIZE(XXX_ENUM_CASE_ELEMENT(elem)); }
    #define XXX_ENUM_CASTLIST(r,data,elem) { XXX_STRINGIZE(XXX_ENUM_CASE_ELEMENT(elem)), data :: XXX_ENUM_CASE_ELEMENT(elem) },
    #define XXX_ENUM_QUALIFIED_CASTLIST(r,data,elem) { #data "::" XXX_STRINGIZE(XXX_ENUM_CASE_ELEMENT(elem)), data :: XXX_ENUM_CASE_ELEMENT(elem) },
    
    #define XXX_ENUM_INTERNAL(TYPE,NAME,TUPLE)                       \
    enum TYPE                                                        \
    {                                                                \
       XXX_TUPLE_FOR_EACH(XXX_ENUM_ELEMENT,,TUPLE)                   \
       BOOST_PP_CAT(last_enum_,NAME)                                 \
    };                                                               \
                                                                     \
    inline                                                           \
    const char* to_string( NAME en )                                 \
    {                                                                \
       if(false)                                                     \
       {                                                             \
       }                                                             \
       XXX_TUPLE_FOR_EACH(XXX_ENUM_IFELSE,NAME,TUPLE)                \
       else if( en == NAME :: BOOST_PP_CAT(last_enum_,NAME) )        \
       {                                                             \
         return XXX_VSTRINGIZE(NAME,::,BOOST_PP_CAT(last_enum_,NAME));  \
       }                                                             \
       else                                                          \
       {                                                             \
         return "Invalid enum value specified for " # NAME;          \
       }                                                             \
    }                                                                \
                                                                     \
    inline                                                           \
    std::ostream& operator<<( std::ostream& os, const NAME& en )     \
    {                                                                \
       os << to_string(en);                                          \
       return os;                                                    \
    }                                                                \
                                                                     \
    inline                                                           \
    NAME do_enum_cast( const std::string& s, const ::xxx::enum_cast_adl_helper<NAME>& ) \
    {                                                                \
      static const std::unordered_map<std::string,NAME> map =        \
      {                                                              \
        XXX_TUPLE_FOR_EACH(XXX_ENUM_CASTLIST,NAME,TUPLE)             \
        XXX_TUPLE_FOR_EACH(XXX_ENUM_QUALIFIED_CASTLIST,NAME,TUPLE)   \
      };                                                             \
                                                                     \
      auto cit = map.find(s);                                        \
      if( cit == map.end() )                                         \
      {                                                              \
        throw std::runtime_error("Invalid value to cast to enum");   \
      }                                                              \
      return cit->second;                                            \
    }
    
    #define XXX_ENUM(NAME,TUPLE) XXX_ENUM_INTERNAL(NAME,NAME,TUPLE)
    #define XXX_ENUM_CLASS(NAME,TUPLE) XXX_ENUM_INTERNAL(class NAME,NAME,TUPLE)
    #define XXX_ENUM_CLASS_TYPE(NAME,TYPE,TUPLE) XXX_ENUM_INTERNAL(class NAME : TYPE,NAME,TUPLE)
    #define XXX_ENUM_TYPE(NAME,TYPE,TUPLE) XXX_ENUM_INTERNAL(NAME : TYPE,NAME,TUPLE)
    

    Usage

    #include "xxx_enum.h"  // the above lib
    #include <iostream>
    
    XXX_ENUM(foo,(a,b,(c,42)));
    
    int main()
    {
      std::cout << "foo::a = "            << foo::a            <<'\n';
      std::cout << "(int)foo::c = "       << (int)foo::c       <<'\n';
      std::cout << "to_string(foo::b) = " << to_string(foo::b) <<'\n';
      std::cout << "xxx::enum_cast<foo>(\"b\") = " << xxx::enum_cast<foo>("b") <<'\n';
    }
    

    Compilation (copy paste header within main.cpp)

    > g++ --version | sed 1q
    g++ (GCC) 4.9.2
    
    > g++ -std=c++14 -pedantic -Wall -Wextra main.cpp
    main.cpp:268:31: warning: extra ';' [-Wpedantic]
         XXX_ENUM(foo,(a,b,(c,42)));
                                   ^
    

    Output

    foo::a = foo::a
    (int)foo::c = 42
    to_string(foo::b) = foo::b
    xxx::enum_cast<foo>("b") = foo::b
    
    0 讨论(0)
  • 2020-11-22 17:30

    This gist provides a simple mapping based on C++ variadic templates.

    This is a C++17-simplified version of the type-based map from the gist:

    #include <cstring> // http://stackoverflow.com/q/24520781
    
    template<typename KeyValue, typename ... RestOfKeyValues>
    struct map {
      static constexpr typename KeyValue::key_t get(const char* val) noexcept {
        if constexpr (sizeof...(RestOfKeyValues)==0)  // C++17 if constexpr
          return KeyValue::key; // Returns last element
        else {
          static_assert(KeyValue::val != nullptr,
                      "Only last element may have null name");
          return strcmp(val, KeyValue::val()) 
                ? map<RestOfKeyValues...>::get(val) : KeyValue::key;
        }
      }
      static constexpr const char* get(typename KeyValue::key_t key) noexcept {
        if constexpr (sizeof...(RestOfKeyValues)==0)
          return (KeyValue::val != nullptr) && (key == KeyValue::key)
                ? KeyValue::val() : "";
        else
          return (key == KeyValue::key) 
                ? KeyValue::val() : map<RestOfKeyValues...>::get(key);
      }
    };
    
    template<typename Enum, typename ... KeyValues>
    class names {
      typedef map<KeyValues...> Map;
    public:
      static constexpr Enum get(const char* nam) noexcept {
        return Map::get(nam);
      }
      static constexpr const char* get(Enum key) noexcept {
        return Map::get(key);
      }
    };
    

    An example usage:

    enum class fasion {
        fancy,
        classic,
        sporty,
        emo,
        __last__ = emo,
        __unknown__ = -1
    };
    
    #define NAME(s) static inline constexpr const char* s() noexcept {return #s;}
    namespace name {
        NAME(fancy)
        NAME(classic)
        NAME(sporty)
        NAME(emo)
    }
    
    template<auto K, const char* (*V)()>  // C++17 template<auto>
    struct _ {
        typedef decltype(K) key_t;
        typedef decltype(V) name_t;
        static constexpr key_t  key = K; // enum id value
        static constexpr name_t val = V; // enum id name
    };
    
    typedef names<fasion,
        _<fasion::fancy, name::fancy>,
        _<fasion::classic, name::classic>,
        _<fasion::sporty, name::sporty>,
        _<fasion::emo, name::emo>,
        _<fasion::__unknown__, nullptr>
    > fasion_names;
    

    The map<KeyValues...> can be used in both directions:

    • fasion_names::get(fasion::emo)
    • fasion_names::get("emo")

    This example is available on godbolt.org

    int main ()
    {
      constexpr auto str = fasion_names::get(fasion::emo);
      constexpr auto fsn = fasion_names::get(str);
      return (int) fsn;
    }
    

    Result from gcc-7 -std=c++1z -Ofast -S

    main:
            mov     eax, 3
            ret
    
    0 讨论(0)
  • 2020-11-22 17:32

    The following solution is based on a std::array<std::string,N> for a given enum.

    For enum to std::string conversion we can just cast the enum to size_t and lookup the string from the array. The operation is O(1) and requires no heap allocation.

    #include <boost/preprocessor/seq/transform.hpp>
    #include <boost/preprocessor/seq/enum.hpp>
    #include <boost/preprocessor/stringize.hpp>
    
    #include <string>
    #include <array>
    #include <iostream>
    
    #define STRINGIZE(s, data, elem) BOOST_PP_STRINGIZE(elem)
    
    // ENUM
    // ============================================================================
    #define ENUM(X, SEQ) \
    struct X {   \
        enum Enum {BOOST_PP_SEQ_ENUM(SEQ)}; \
        static const std::array<std::string,BOOST_PP_SEQ_SIZE(SEQ)> array_of_strings() { \
            return {{BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(STRINGIZE, 0, SEQ))}}; \
        } \
        static std::string to_string(Enum e) { \
            auto a = array_of_strings(); \
            return a[static_cast<size_t>(e)]; \
        } \
    }
    

    For std::string to enum conversion we would have to make a linear search over the array and cast the array index to enum.

    Try it here with usage examples: http://coliru.stacked-crooked.com/a/e4212f93bee65076

    Edit: Reworked my solution so the custom Enum can be used inside a class.

    0 讨论(0)
提交回复
热议问题