代码之家  ›  专栏  ›  技术社区  ›  zxcv

在C中使用枚举类型的变量作为字符串的简单方法?

  •  83
  • zxcv  · 技术社区  · 16 年前

    我想做的是:

    typedef enum { ONE, TWO, THREE } Numbers;
    

    我正在尝试编写一个函数,该函数将执行类似于以下内容的切换案例:

    char num_str[10];
    int process_numbers_str(Numbers num) {
      switch(num) {
        case ONE:
        case TWO:
        case THREE:
        {
          strcpy(num_str, num); //some way to get the symbolic constant name in here?
        } break;
        default:
          return 0; //no match
      return 1;
    }
    

    不是在每种情况下都定义,有没有一种方法可以像我在上面所做的那样使用枚举变量来设置它?

    18 回复  |  直到 7 年前
        1
  •  13
  •   roottraveller    7 年前

    没有内置的解决方案。最简单的方法是使用 char* 其中枚举的int值索引到包含该枚举的描述性名称的字符串。如果你有稀疏的 enum (一个不从0开始或在编号上有间隙的)其中 int 映射的高度足以使基于数组的映射不切实际,然后您可以改用哈希表。

        2
  •  61
  •   Bill Forster    16 年前
    // Define your enumeration like this (in say numbers.h);
    ENUM_BEGIN( Numbers )
        ENUM(ONE),
        ENUM(TWO),
        ENUM(FOUR)
    ENUM_END( Numbers )
    
    // The macros are defined in a more fundamental .h file (say defs.h);
    #define ENUM_BEGIN(typ) enum typ {
    #define ENUM(nam) nam
    #define ENUM_END(typ) };
    
    // Now in one and only one .c file, redefine the ENUM macros and reinclude
    //  the numbers.h file to build a string table
    #undef ENUM_BEGIN
    #undef ENUM
    #undef ENUM_END
    #define ENUM_BEGIN(typ) const char * typ ## _name_table [] = {
    #define ENUM(nam) #nam
    #define ENUM_END(typ) };
    #undef NUMBERS_H_INCLUDED   // whatever you need to do to enable reinclusion
    #include "numbers.h"
    
    // Now you can do exactly what you want to do, with no retyping, and for any
    //  number of enumerated types defined with the ENUM macro family
    //  Your code follows;
    char num_str[10];
    int process_numbers_str(Numbers num) {
      switch(num) {
        case ONE:
        case TWO:
        case THREE:
        {
          strcpy(num_str, Numbers_name_table[num]); // eg TWO -> "TWO"
        } break;
        default:
          return 0; //no match
      return 1;
    }
    
    // Sweet no ? After being frustrated by this for years, I finally came up
    //  with this solution for my most recent project and plan to reuse the idea
    //  forever
    
        3
  •  61
  •   Community CDub    7 年前

    技术来源 Making something both a C identifier and a string? 可以在这里使用。

    与通常的预处理器一样,编写和理解预处理器部分可能很困难,包括将宏传递给其他宏,并涉及使用和运算符,但使用它确实很容易。我发现这种样式对于长枚举非常有用,在长枚举中维护同一列表两次确实很麻烦。

    工厂代码-仅键入一次,通常隐藏在标题中:

    枚举:h:

    // expansion macro for enum value definition
    #define ENUM_VALUE(name,assign) name assign,
    
    // expansion macro for enum to string conversion
    #define ENUM_CASE(name,assign) case name: return #name;
    
    // expansion macro for string to enum conversion
    #define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name;
    
    /// declare the access function and define enum values
    #define DECLARE_ENUM(EnumType,ENUM_DEF) \
      enum EnumType { \
        ENUM_DEF(ENUM_VALUE) \
      }; \
      const char *GetString(EnumType dummy); \
      EnumType Get##EnumType##Value(const char *string); \
    
    /// define the access function names
    #define DEFINE_ENUM(EnumType,ENUM_DEF) \
      const char *GetString(EnumType value) \
      { \
        switch(value) \
        { \
          ENUM_DEF(ENUM_CASE) \
          default: return ""; /* handle input error */ \
        } \
      } \
      EnumType Get##EnumType##Value(const char *str) \
      { \
        ENUM_DEF(ENUM_STRCMP) \
        return (EnumType)0; /* handle input error */ \
      } \
    

    工厂使用

    H:

    #include "enumFactory.h"
    #define SOME_ENUM(XX) \
        XX(FirstValue,) \
        XX(SecondValue,) \
        XX(SomeOtherValue,=50) \
        XX(OneMoreValue,=100) \
    
    DECLARE_ENUM(SomeEnum,SOME_ENUM)
    

    CPP:

    #include "someEnum.h"
    DEFINE_ENUM(SomeEnum,SOME_ENUM)
    

    这种技术可以很容易地扩展,以便xx宏接受更多的参数,您还可以准备更多的宏来代替xx以满足不同的需要,类似于我在这个示例中提供的三个。

    使用include/define/undef与X宏进行比较

    虽然这类似于其他人提到的X宏,但我认为该解决方案更优雅,因为它不需要取消任何标记,这允许您隐藏更多复杂的内容在工厂中头文件-头文件是您在需要定义新枚举时根本不接触的内容,因此新枚举澄清要短得多,而且要干净得多。

        4
  •  13
  •   JayG    16 年前

    确实有一种方法可以做到这一点——使用 X() macros . 这些宏使用C预处理器从源数据列表中构造枚举、数组和代码块。您只需要向包含x()宏的定义中添加新项。switch语句将自动展开。

    您的示例可以写如下:

     // Source data -- Enum, String
     #define X_NUMBERS \
        X(ONE,   "one") \
        X(TWO,   "two") \
        X(THREE, "three")
    
     ...
    
     // Use preprocessor to create the Enum
     typedef enum {
      #define X(Enum, String)       Enum,
       X_NUMBERS
      #undef X
     } Numbers;
    
     ...
    
     // Use Preprocessor to expand data into switch statement cases
     switch(num)
     {
     #define X(Enum, String) \
         case Enum:  strcpy(num_str, String); break;
     X_NUMBERS
     #undef X
    
         default: return 0; break;
     }
     return 1;
    

    有更有效的方法(即使用x宏创建字符串数组和枚举索引),但这是最简单的演示。

        5
  •  8
  •   Peter Hosey    13 年前

    我知道你有几个很可靠的答案,但是你知道C预处理器中的运算符吗?

    它允许您这样做:

    #define MACROSTR(k) #k
    
    typedef enum {
        kZero,
        kOne,
        kTwo,
        kThree
    } kConst;
    
    static char *kConstStr[] = {
        MACROSTR(kZero),
        MACROSTR(kOne),
        MACROSTR(kTwo),
        MACROSTR(kThree)
    };
    
    static void kConstPrinter(kConst k)
    {
        printf("%s", kConstStr[k]);
    }
    
        6
  •  6
  •   Samuel Danielson    14 年前

    吻。您将对枚举执行各种其他切换/大小写操作,那么为什么打印会有所不同呢?当你认为有大约100个地方你可以忘记一个案例时,在你的打印程序中忘记一个案例并不是一件大事。只需编译墙,它将警告非详尽的案例匹配。不要使用“默认值”,因为这样会使开关耗尽,并且不会收到警告。相反,让开关退出并像这样处理默认情况…

    const char *myenum_str(myenum e)
    {
        switch(e) {
        case ONE: return "one";
        case TWO: return "two";
        }
        return "invalid";
    }
    
        7
  •  5
  •   paxdiablo    16 年前

    C或C++不提供这个功能,虽然我经常需要它。

    下面的代码可以工作,尽管它最适合非稀疏枚举。

    typedef enum { ONE, TWO, THREE } Numbers;
    char *strNumbers[] = {"one","two","three"};
    printf ("Value for TWO is %s\n",strNumbers[TWO]);
    

    我指的是不稀疏的形状

    typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;
    

    因为这其中有巨大的差距。

    这个方法的优点是,它将枚举和字符串的定义放在一起;在函数中有一个switch语句可以将它们放在一起。这意味着你不太可能改变其中一个。

        8
  •  4
  •   Bob Nadler    16 年前

    尝试 Converting C++ enums to strings . 这个 comments 当枚举项具有任意值时,具有解决问题的改进。

        9
  •  4
  •   Giacomo M.    13 年前

    使用 boost::preprocessor 提供如下优雅的解决方案:

    步骤1:包括头文件:

    #include "EnumUtilities.h"
    

    步骤2:用以下语法声明枚举对象:

    MakeEnum( TestData,
             (x)
             (y)
             (z)
             );
    

    第3步:使用数据:

    获取元素数:

    td::cout << "Number of Elements: " << TestDataCount << std::endl;
    

    获取关联字符串:

    std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl;
    std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl;
    std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;
    

    从关联字符串获取枚举值:

    std::cout << "Value of x is " << TestData2Enum("x") << std::endl;
    std::cout << "Value of y is " << TestData2Enum("y") << std::endl;
    std::cout << "Value of z is " << TestData2Enum("z") << std::endl;
    

    这看起来干净紧凑,没有额外的文件。 我在遗孀中写的代码。h如下:

    #include <boost/preprocessor/seq/for_each.hpp>
    #include <string>
    
    #define REALLY_MAKE_STRING(x) #x
    #define MAKE_STRING(x) REALLY_MAKE_STRING(x)
    #define MACRO1(r, data, elem) elem,
    #define MACRO1_STRING(r, data, elem)    case elem: return REALLY_MAKE_STRING(elem);
    #define MACRO1_ENUM(r, data, elem)      if (REALLY_MAKE_STRING(elem) == eStrEl) return elem;
    
    
    #define MakeEnum(eName, SEQ) \
        enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \
        last_##eName##_enum}; \
        const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
        static std::string eName##2String(const enum eName eel) \
        { \
            switch (eel) \
            { \
            BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \
            default: return "Unknown enumerator value."; \
            }; \
        }; \
        static enum eName eName##2Enum(const std::string eStrEl) \
        { \
            BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \
            return (enum eName)0; \
        };
    

    有一些限制,例如Boost::Preprocessor的限制。在这种情况下,常量列表不能大于64个元素。

    遵循相同的逻辑,您还可以考虑创建稀疏枚举:

    #define EnumName(Tuple)                 BOOST_PP_TUPLE_ELEM(2, 0, Tuple)
    #define EnumValue(Tuple)                BOOST_PP_TUPLE_ELEM(2, 1, Tuple)
    #define MACRO2(r, data, elem)           EnumName(elem) EnumValue(elem),
    #define MACRO2_STRING(r, data, elem)    case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem));
    
    #define MakeEnumEx(eName, SEQ) \
        enum eName { \
        BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \
        last_##eName##_enum }; \
        const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
        static std::string eName##2String(const enum eName eel) \
        { \
            switch (eel) \
            { \
            BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \
            default: return "Unknown enumerator value."; \
            }; \
        };  
    

    在这种情况下,语法是:

    MakeEnumEx(TestEnum,
               ((x,))
               ((y,=1000))
               ((z,))
               );
    

    用法与上面类似(减去ename 2enum函数,您可以尝试从前面的语法中进行推断)。

    我在Mac和Linux上测试过它,但请注意,boost::preprocessor可能不是完全可移植的。

        10
  •  3
  •   Juan Gonzalez Burgos    9 年前

    通过合并这里的一些技术,我得出了最简单的形式:

    #define MACROSTR(k) #k
    
    #define X_NUMBERS \
           X(kZero  ) \
           X(kOne   ) \
           X(kTwo   ) \
           X(kThree ) \
           X(kFour  ) \
           X(kMax   )
    
    enum {
    #define X(Enum)       Enum,
        X_NUMBERS
    #undef X
    } kConst;
    
    static char *kConstStr[] = {
    #define X(String) MACROSTR(String),
        X_NUMBERS
    #undef X
    };
    
    int main(void)
    {
        int k;
        printf("Hello World!\n\n");
    
        for (k = 0; k < kMax; k++)
        {
            printf("%s\n", kConstStr[k]);
        }
    
        return 0;
    }
    
        11
  •  2
  •   codemania Hitesh    10 年前

    如果您使用GCC,则可以使用:

    const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};
    

    那就打个电话吧

    enum_to_string_map[enum1]
    
        12
  •  1
  •   Jonathan Leffler    16 年前

    请访问 Mu Dynamics Research Labs - Blog Archive . 今年早些时候我发现了这个——我忘记了我遇到它的确切上下文——并把它改编成了这个代码。我们可以在前面讨论添加e的优点;它适用于所解决的特定问题,但不是一般解决方案的一部分。我把它藏在我的“小插曲”文件夹中——在那里我保存一些有趣的代码片段,以备以后需要。我很不好意思说,当时我没有记录下这个想法是从哪里来的。

    标题:Paste1.h

    /*
    @(#)File:           $RCSfile: paste1.h,v $
    @(#)Version:        $Revision: 1.1 $
    @(#)Last changed:   $Date: 2008/05/17 21:38:05 $
    @(#)Purpose:        Automated Token Pasting
    */
    
    #ifndef JLSS_ID_PASTE_H
    #define JLSS_ID_PASTE_H
    
    /*
     * Common case when someone just includes this file.  In this case,
     * they just get the various E* tokens as good old enums.
     */
    #if !defined(ETYPE)
    #define ETYPE(val, desc) E##val,
    #define ETYPE_ENUM
    enum {
    #endif /* ETYPE */
    
       ETYPE(PERM,  "Operation not permitted")
       ETYPE(NOENT, "No such file or directory")
       ETYPE(SRCH,  "No such process")
       ETYPE(INTR,  "Interrupted system call")
       ETYPE(IO,    "I/O error")
       ETYPE(NXIO,  "No such device or address")
       ETYPE(2BIG,  "Arg list too long")
    
    /*
     * Close up the enum block in the common case of someone including
     * this file.
     */
    #if defined(ETYPE_ENUM)
    #undef ETYPE_ENUM
    #undef ETYPE
    ETYPE_MAX
    };
    #endif /* ETYPE_ENUM */
    
    #endif /* JLSS_ID_PASTE_H */
    

    实例来源:

    /*
    @(#)File:           $RCSfile: paste1.c,v $
    @(#)Version:        $Revision: 1.2 $
    @(#)Last changed:   $Date: 2008/06/24 01:03:38 $
    @(#)Purpose:        Automated Token Pasting
    */
    
    #include "paste1.h"
    
    static const char *sys_errlist_internal[] = {
    #undef JLSS_ID_PASTE_H
    #define ETYPE(val, desc) desc,
    #include "paste1.h"
        0
    #undef ETYPE
    };
    
    static const char *xerror(int err)
    {
        if (err >= ETYPE_MAX || err <= 0)
            return "Unknown error";
        return sys_errlist_internal[err];
    }
    
    static const char*errlist_mnemonics[] = {
    #undef JLSS_ID_PASTE_H
    #define ETYPE(val, desc) [E ## val] = "E" #val,
    #include "paste1.h"
    #undef ETYPE
    };
    
    #include <stdio.h>
    
    int main(void)
    {
        int i;
    
        for (i = 0; i < ETYPE_MAX; i++)
        {
            printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i));
        }
        return(0);
    }
    

    不一定是世界上最干净的使用C预处理器-但它确实阻止了多次写出材料。

        14
  •  0
  •   Colen    16 年前

    如果枚举索引基于0,则可以将名称放入char*数组中,并使用枚举值对其进行索引。

        15
  •  0
  •   Ben    12 年前
    #define stringify( name ) # name
    
    enum MyEnum {
        ENUMVAL1
    };
    ...stuff...
    
    stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1
    

    Further discussion on this method

    Preprocessor directive tricks for newcomers

        16
  •  0
  •   Robert Husák    11 年前

    我创建了一个简单的模板类 streamable_enum 使用流运算符的 << >> 基于 std::map<Enum, std::string> :

    #ifndef STREAMABLE_ENUM_HPP
    #define STREAMABLE_ENUM_HPP
    
    #include <iostream>
    #include <string>
    #include <map>
    
    template <typename E>
    class streamable_enum
    {
    public:
        typedef typename std::map<E, std::string> tostr_map_t;
        typedef typename std::map<std::string, E> fromstr_map_t;
    
        streamable_enum()
        {}
    
        streamable_enum(E val) :
            Val_(val)
        {}
    
        operator E() {
            return Val_;
        }
    
        bool operator==(const streamable_enum<E>& e) {
            return this->Val_ == e.Val_;
        }
    
        bool operator==(const E& e) {
            return this->Val_ == e;
        }
    
        static const tostr_map_t& to_string_map() {
            static tostr_map_t to_str_(get_enum_strings<E>());
            return to_str_;
        }
    
        static const fromstr_map_t& from_string_map() {
            static fromstr_map_t from_str_(reverse_map(to_string_map()));
            return from_str_;
        }
    private:
        E Val_;
    
        static fromstr_map_t reverse_map(const tostr_map_t& eToS) {
            fromstr_map_t sToE;
            for (auto pr : eToS) {
                sToE.emplace(pr.second, pr.first);
            }
            return sToE;
        }
    };
    
    template <typename E>
    streamable_enum<E> stream_enum(E e) {
        return streamable_enum<E>(e);
    }
    
    template <typename E>
    typename streamable_enum<E>::tostr_map_t get_enum_strings() {
        // \todo throw an appropriate exception or display compile error/warning
        return {};
    }
    
    template <typename E>
    std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) {
        auto& mp = streamable_enum<E>::to_string_map();
        auto res = mp.find(e);
        if (res != mp.end()) {
            os << res->second;
        } else {
            os.setstate(std::ios_base::failbit);
        }
        return os;
    }
    
    template <typename E>
    std::istream& operator>>(std::istream& is, streamable_enum<E>& e) {
        std::string str;
        is >> str;
        if (str.empty()) {
            is.setstate(std::ios_base::failbit);
        }
        auto& mp = streamable_enum<E>::from_string_map();
        auto res = mp.find(str);
        if (res != mp.end()) {
            e = res->second;
        } else {
            is.setstate(std::ios_base::failbit);
        }
        return is;
    }
    
    #endif
    

    用途:

    #include "streamable_enum.hpp"
    
    using std::cout;
    using std::cin;
    using std::endl;
    
    enum Animal {
        CAT,
        DOG,
        TIGER,
        RABBIT
    };
    
    template <>
    streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() {
        return {
            { CAT, "Cat"},
            { DOG, "Dog" },
            { TIGER, "Tiger" },
            { RABBIT, "Rabbit" }
        };
    }
    
    int main(int argc, char* argv []) {
        cout << "What animal do you want to buy? Our offering:" << endl;
        for (auto pr : streamable_enum<Animal>::to_string_map()) {          // Use from_string_map() and pr.first instead
            cout << " " << pr.second << endl;                               // to have them sorted in alphabetical order
        }
        streamable_enum<Animal> anim;
        cin >> anim;
        if (!cin) {
            cout << "We don't have such animal here." << endl;
        } else if (anim == Animal::TIGER) {
            cout << stream_enum(Animal::TIGER) << " was a joke..." << endl;
        } else {
            cout << "Here you are!" << endl;
        }
    
        return 0;
    }
    
        17
  •  0
  •   Community CDub    7 年前

    下面是一个使用宏的解决方案,具有以下功能:

    1. 只写一次枚举的每个值,因此没有要维护的双列表

    2. 不要将枚举值保存在以后包含的单独文件中,这样我就可以将其写入任何我想要的地方。

    3. 不要替换枚举本身,我仍然希望定义枚举类型,但除此之外,我希望能够将每个枚举名称映射到相应的字符串(不影响旧代码)

    4. 对于那些巨大的枚举,搜索应该很快,所以最好不要使用开关盒。

    https://stackoverflow.com/a/20134475/1812866

        18
  •  0
  •   daminetreg    10 年前

    我认为像Boost.Fusion这样的解决方案适合于结构和类,它们甚至在某个时刻使用枚举作为融合序列。

    所以我做了一些小宏来生成打印枚举的代码。这并不完美,与Boost没有任何关系。Fusion生成样板代码,但可以像Boost Fusion宏那样使用。我真的想生成Boost.Fusion所需的类型,以集成到允许打印结构成员名称的基础结构中,但稍后会发生这种情况,目前这只是宏:

    #ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
    #define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
    
    #include <swissarmyknife/detail/config.hpp>
    
    #include <string>
    #include <ostream>
    #include <boost/preprocessor/cat.hpp>
    #include <boost/preprocessor/stringize.hpp>
    #include <boost/preprocessor/seq/for_each.hpp>
    
    
    #define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C(                     \
        R, unused, ENUMERATION_ENTRY)                                               \
        case ENUMERATION_ENTRY:                                                     \
          return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY);                             \
        break;                                                                      
    
    /**
     * \brief Adapts ENUM to reflectable types.
     *
     * \param ENUM_TYPE To be adapted
     * \param ENUMERATION_SEQ Sequence of enum states
     */
    #define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ)                   \
        inline std::string to_string(const ENUM_TYPE& enum_value) {                 \
          switch (enum_value) {                                                     \
          BOOST_PP_SEQ_FOR_EACH(                                                    \
              SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C,                   \
              unused, ENUMERATION_SEQ)                                              \
            default:                                                                \
              return BOOST_PP_STRINGIZE(ENUM_TYPE);                                 \
          }                                                                         \
        }                                                                           \
                                                                                    \
        inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \
          os << to_string(value);                                                   \
          return os;                                                                \
        }
    
    #endif
    

    下面的旧答案很糟糕,请不要使用它。:)

    老回答:

    我一直在寻找一种在不改变太多枚举声明语法的情况下解决这个问题的方法。我找到了一个使用预处理器从字符串化枚举声明中检索字符串的解决方案。

    我可以这样定义非稀疏枚举:

    SMART_ENUM(State, 
        enum State {
            RUNNING,
            SLEEPING, 
            FAULT, 
            UNKNOWN
        })
    

    我可以用不同的方式与他们互动:

    // With a stringstream
    std::stringstream ss;
    ss << State::FAULT;
    std::string myEnumStr = ss.str();
    
    //Directly to stdout
    std::cout << State::FAULT << std::endl;
    
    //to a string
    std::string myStr = State::to_string(State::FAULT);
    
    //from a string
    State::State myEnumVal = State::from_string(State::FAULT);
    

    基于以下定义:

    #define SMART_ENUM(enumTypeArg, ...)                                                     \
    namespace enumTypeArg {                                                                  \
        __VA_ARGS__;                                                                         \
        std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) {                 \
                os << swissarmyknife::enums::to_string(#__VA_ARGS__, val);                   \
                return os;                                                                   \
        }                                                                                    \
                                                                                         \
        std::string to_string(const enumTypeArg& val) {                                      \
                return swissarmyknife::enums::to_string(#__VA_ARGS__, val);                  \
        }                                                                                    \
                                                                                         \
        enumTypeArg from_string(const std::string &str) {                                    \
                return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str);   \
        }                                                                                    \
    }                                                                                        \
    
    
    namespace swissarmyknife { namespace enums {
    
        static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) {
            size_t begin = completeEnumDeclaration.find_first_of('{');
            size_t end = completeEnumDeclaration.find_last_of('}');
            const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );
    
            size_t count = 0;
            size_t found = 0;
            do {
                found = identifiers.find_first_of(",}", found+1);
    
                if (enumVal == count) {
                    std::string identifiersSubset = identifiers.substr(0, found);
                    size_t beginId = identifiersSubset.find_last_of("{,");
                    identifiersSubset = identifiersSubset.substr(beginId+1);
                    boost::algorithm::trim(identifiersSubset);
                    return identifiersSubset;
                }
    
                ++count;
            } while (found != std::string::npos);
    
            throw std::runtime_error("The enum declaration provided doesn't contains this state.");
        }                                                  
    
        template <typename EnumType>
        static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) {
            size_t begin = completeEnumDeclaration.find_first_of('{');
            size_t end = completeEnumDeclaration.find_last_of('}');
            const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );
    
            size_t count = 0;
            size_t found = 0;
            do {
                found = identifiers.find_first_of(",}", found+1);
    
                std::string identifiersSubset = identifiers.substr(0, found);
                size_t beginId = identifiersSubset.find_last_of("{,");
                identifiersSubset = identifiersSubset.substr(beginId+1);
                boost::algorithm::trim(identifiersSubset);
    
                if (identifiersSubset == enumStr) {
                    return static_cast<EnumType>(count);
                }
    
                ++count;
            } while (found != std::string::npos);
    
            throw std::runtime_error("No valid enum value for the provided string");
        }                      
    
    }}
    

    当我需要对稀疏枚举的支持时,当我有更多的时间时,我将改进 托弦 从字符串 使用boost::xpressive实现,但这将花费编译时间,因为执行了重要的模板化,生成的可执行文件可能会更大。但这有一个优点,即它比这个丑陋的手动字符串操作代码更易于阅读和维护。 D

    否则,我总是使用boost::bimap在枚举值和字符串之间执行这种映射,但必须手动维护它。