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

Easy way to use variables of enum types as string in C?

我想做的是:

1
typedef enum { ONE, TWO, THREE } Numbers;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}

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


同时生成C标识符和字符串的技术?可以在这里使用。

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

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

枚举:h:

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
// 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:

1
2
3
4
5
6
7
8
#include"enumFactory.h"
#define SOME_ENUM(XX) \
    XX(FirstValue,) \
    XX(SecondValue,) \
    XX(SomeOtherValue,=50) \
    XX(OneMoreValue,=100) \

DECLARE_ENUM(SomeEnum,SOME_ENUM)

CPP:

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

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

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

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


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
// 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


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

您的示例可以写如下:

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
 // 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宏创建字符串数组和枚举索引),但这是最简单的演示。


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


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

它允许您这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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]);
}


的吻。你会做所有的sorts之其他开关/案例的事情与你的enums所以为什么要印刷在不同的吗?forgetting的情况下在你的打印routine是不是问题的huge新政",当你认为有意义的地方,你可以忘记其他100的案例。只是compile墙,将警告美国非exhaustive matches案例。不要用"违约",因为这将使开关exhaustive和你得到warnings韩元。代替,把开关和退出交易与违约的情况下,像这样… P / < >

1
2
3
4
5
6
7
8
const char *myenum_str(myenum e)
{
    switch(e) {
    case ONE: return"one";
    case TWO: return"two";
    }
    return"invalid";
}


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

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

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

我指的是不稀疏的形状

1
typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;

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

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


*使用的提升:尽可能让一个解决方案:preprocessor elegant喜欢以下: P / < >

步骤1:包括同步头文件: P / < >

1
#include"EnumUtilities.h"

步骤2:enumeration声明的对象与以下语法: P / < >

1
2
3
4
5
MakeEnum( TestData,
         (x)
         (y)
         (z)
         );

步骤3:使用你的数据: P / < >

得到一些的元素: P / < >

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

得到的字符串:"美联社 P / < >

1
2
3
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;

我每天早晨的enum值从字符串:美联社 P / < >

1
2
3
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;

这看起来干净和紧凑,与没有额外的文件包括。 "code)写在enumutilities.h的以下: P / < >

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

有一些限制,公元前的ones之提升::preprocessor。在这情况下,不能进入大名单constants比64的元素。 P / < >

英镑同样的逻辑,你也能想到创造sparse enum: P / < >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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."; \
        }; \
    };

在这情况下,语法: P / < >

1
2
3
4
5
MakeEnumEx(TestEnum,
           ((x,))
           ((y,=1000))
           ((z,))
           );

usage也similar级以上(零下的ename # # 2enum功能,你可以试着extrapolate从previous语法)。 P / < >

我的身体它在Mac和Linux,不过是意识的提升,::preprocessor可能不是全便携式。 P / < >


尝试将C++枚举转换为字符串。当枚举项具有任意值时,注释有一些改进可以解决这个问题。


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

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
#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!

"
);

    for (k = 0; k < kMax; k++)
    {
        printf("%s
"
, kConstStr[k]);
    }

    return 0;
}

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

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

那就打个电话吧

1
enum_to_string_map[enum1]


使某个东西同时成为C标识符和字符串


查看MU动力研究实验室的想法-博客档案。今年早些时候我发现了这个——我忘记了我遇到它的确切上下文——并把它改编成了这个代码。我们可以在前面讨论添加e的优点;它适用于所解决的特定问题,但不是一般解决方案的一部分。我把它藏在我的"小插曲"文件夹中——在那里我保存一些有趣的代码片段,以备以后需要。我很不好意思说,当时我没有记录下这个想法是从哪里来的。

标题:Paste1.h

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
/*
@(#)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 */

实例来源:

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
/*
@(#)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
"
, i, errlist_mnemonics[i], xerror(i));
    }
    return(0);
}

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


1
2
3
4
5
6
7
8
#define stringify( name ) # name

enum MyEnum {
    ENUMVAL1
};
...stuff...

stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1

进一步的discussion在这方法 P / < >

preprocessor指令tricks为newcomers P / < >


我认为,在像融合解决方案的提升。一个给adapting structs和类会好的,即使他们让它在一些点,到使用enums级融合序列。 P / < >

所以我做的只是一些小的宏代码打印到generate"到"enums。这是不完美的,有没有看到与提升。boilerplate融合产生代码,但可以用喜欢的提升融合宏。我真的想做的generate类型需要通过提升到integrate融合。在这个基础设施,allows到打印的名字struct会员,但这将发生以后,现在这只是宏: P / < >

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

旧的回答下面很坏,请不要使用,。:) P / < > 旧的回答:

我已经搜索的方式,solves这个问题没有改变太多的enums declaration语法。我来找的解决方案,用途的preprocessor到retrieve字符串从stringified enum declaration。 P / < >

我能到定义的非sparse enums像这样: P / < >

1
2
3
4
5
6
7
SMART_ENUM(State,
    enum State {
        RUNNING,
        SLEEPING,
        FAULT,
        UNKNOWN
    })

我能与他们互动,在不同的方式: P / < >

1
2
3
4
5
6
7
8
9
10
11
12
13
// 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);

的基础上的definitions以下: P / < >

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
64
65
66
67
68
69
70
71
#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");
    }                      

}}

当我需要支援为sparse enum当我会有更多的时间,我会improve * * *从字符串的字符串_ _ implementations与提升::xpressive,但这将成本在编译时间因为英语是重要的templating performed和executable产生也可能真的要更大。但这腹部的优势,它将比这更多的readable和maintanable丑女手册manipulation字符串的代码。D: P / < >

另有)总是用的提升::bimap到执行这样一个mappings之间enums字符串的值的和,但它的肚子要保持manually。 P / < >


由于我不喜欢使用宏的所有常见原因,我使用了一个更有限的宏解决方案,它具有保持枚举声明宏自由的优点。缺点包括必须复制粘贴每个枚举的宏定义,以及在向枚举添加值时必须显式添加宏调用。

1
2
3
4
5
6
7
8
9
10
11
std::ostream& operator<<(std::ostream& os, provenance_wrapper::CaptureState cs)
{
#define HANDLE(x) case x: os << #x; break;
    switch (cs) {
    HANDLE(CaptureState::UNUSED)
    HANDLE(CaptureState::ACTIVE)
    HANDLE(CaptureState::CLOSED)
    }
    return os;
#undef HANDLE
}


我有一个简单的类,streamable_enum雅templated用途的流<<运营商和>>和也的基础上std::map: P / < >

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#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

usage: P / < > <><>预编码#包括"streamable _ enum.hpp" 使用一个标准::cout; 使用一个标准::CIN; 使用一个标准::endl; enum {动物 猫, 狗, 老虎,老虎, 兔 }; template < > streamable _ enum >:<动物:tostr _地图的_得到_ enum _ <字符串>动物(){ {回报 {"猫,猫是n


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

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

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

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

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

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


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