c++ - 如何轻松地将c++枚举映射到字符串

我正在使用一些库的头文件,里面有一堆枚举类型,,我希望有一种方法将枚举值转换为用户字符串 - 反之亦然。

RTTI不会为我做这件事,因为'用户字符串'需要比枚举更具可读性。

一个强力解决方案将是这样的一堆函数,但是,我觉得这有点像C的风格。


enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
 switch e
 {
 case VAL1: return "Value 1";
 case VAL2: return "Value 2";
 case VAL1: return "Value 3";
 default: throw Exception("Bad MyEnum");
 }
}

我觉得会有一个优雅的解决方案,使用模板,但是,我没有找到头绪。

我现在的感觉是避免模板和做类似这样的事情:


char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) 
 { 
 return MyGetValue((int)T, strings); 
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

        eee e;
        fff f;
        std::cout<< getStringValue(e);
        std::cout<< getStringValue(f);

时间:

如果希望enum名本身作为字符串,请参见这个帖子 ,否则 std::map<MyEnum, char const*> 将工作得很好,(在map中将字符串文本复制到std::strings没有任何意义),

对于额外的语法糖,下面是如何编写map_init类的方法。 我们的目标是


std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
 (eValue1, "A")
 (eValue2, "B")
 (eValue3, "C")
;

函数 template <typename T> map_init(T&) 返回一个map_init_helper<T>map_init_helper<T>存储一个t&,并定义,map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&) (从operator()返回*this允许operator()链接,比如,std::ostream s上的operator<< ),


template<typename T> struct map_init_helper
{
 T& data;
 map_init_helper(T& d) : data(d) {}
 map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
 {
 data[key] = value;
 return *this;
 }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
 return map_init_helper<T>(item);
}

由于函数和helper类是模板化的,所以,可以对任何映射使用它们,或者像结构一样使用映射,换句话说它还可以向std::unordered_map中添加条目

如果你不喜欢编写这些helper,boost::assign提供了相同的功能。

MSalters解决方案是一个很好的解决方案,但是,基本上重新实现了boost::assign::map_list_of ,如果你有boost,你可以直接使用它:


#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
 (AA, "AA")
 (BB, "BB")
 (CC, "CC");

int main()
{
 std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
 return 0;
}

自动从另一个表单生成表单。

来源:


enum {
 VALUE1, /* value 1 */
 VALUE2, /* value 2 */
};

生成:


const char* enum2str[] = {
 "value 1", /* VALUE1 */
 "value 2", /* VALUE2 */
};

如果enum值很大,那么生成的表单可以使用unordered_map < >或模板,如Constantin所建议。

来源:


enum State{
 state0 = 0, /* state 0 */
 state1 = 1, /* state 1 */
 state2 = 2, /* state 2 */
 state3 = 4, /* state 3 */

 state16 = 0x10000, /* state 16 */
};

生成:


template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

例如:


#include <iostream>

int main()
{
 std::cout << enum2str<state16>::value << std::endl;
 return 0;
}

我建议使用 X-macros 是最好的解决方案和以下模板函数:

借用marcinkoziukmyopenidcom和扩展,


enum Colours {
# define X(a) a,
# include "colours.def"
# undef X
 ColoursCount
};

char const* const colours_str[] = {
# define X(a) #a,
# include "colours.def"
# undef X
 0
};

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) 
template <> 
TYPE str2enum<TYPE>( const char* str ) 
 { 
 for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) 
        if( !strcmp( ARRAY[i], str ) ) 
                return TYPE(i); 
 return TYPE(0); 
 }

#define ENUM2STR(TYPE,ARRAY) 
template <> 
const char* enum2str<TYPE>( TYPE v ) 
 { 
 return ARRAY[v]; 
 }

#define ENUMANDSTR(TYPE,ARRAY)
 STR2ENUM(TYPE,ARRAY) 
 ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

colour.def


X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

我记得在StackOverflow的其他地方已经回答了这个问题。重复它,基本上它是基于可变性宏的解决方案,并且很容易使用:


#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; 
inline std::ostream& operator<<(std::ostream& os, name value) { 
std::string enumName = #name; 
std::string str = #__VA_ARGS__; 
int len = str.length(); 
std::vector<std::string> strings; 
std::ostringstream temp; 
for(int i = 0; i < len; i ++) { 
if(isspace(str[i])) continue; 
 else if(str[i] == ',') { 
 strings.push_back(temp.str()); 
 temp.str(std::string());
 } 
 else temp<< str[i]; 
} 
strings.push_back(temp.str()); 
os << enumName << "::" << strings[static_cast<int>(value)]; 
return os;} 

要在代码中使用它,只需执行以下操作:


AWESOME_MAKE_ENUM(Animal,
 DOG,
 CAT,
 HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;

如果要获取MyEnum变量的字符串表示形式,则模板将不会剪切它,模板可以专门针对编译时已知的整数值。

但是,如果这是你想要的,请尝试:


#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
 static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) 
 template<> struct StrMyEnum<val> { 
 static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
 std::cout << StrMyEnum<VAL2>::name();
}

这是冗长的,但是,会捕捉到像你在问题中所做的错误-你的case VAL1是重复的。

我只想用宏来展示这种可能优雅的解决方案。


#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
 {
 MY_LIST(PLAIN)
 };

const char *szMyEnum[] =
 {
 MY_LIST(STRINGY)
 };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " << szMyEnum[value2] << value2 << std::endl;

return 0;
}

----编辑-----

经过一些互联网研究和一些自己的经验,我得出以下解决方案:


//this is the enum definition
#define COLOR_LIST(X) 
 X( RED ,=21) 
 X( GREEN ) 
 X( BLUE ) 
 X( PURPLE , =242) 
 X( ORANGE ) 
 X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << " " << testfunenum(PURPLE) << PURPLE << " " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] << " "<< colorinfo::enum2str(colorinfo::values[i]);

 return EXIT_SUCCESS;
}

我只是想发布,也许有人会发现这个解决方案有用,没有需要c++11类的模板类,不需要提升,这样也可以使用简单的C。

-----EDIT2 -----

信息表在使用2个枚举(编译器问题)时可能会产生一些问题,以下变通方法可用:


#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};

...