如何轻松地将 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);
301145 次浏览

从一个窗体自动生成另一个窗体。

来源:

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

产生:

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

如果枚举值很大,那么生成的表单可以使用 unorder _ 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;
}

标题:

enum EFooOptions
{
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

在. cpp 文件中:

const wchar* FOO_OPTIONS[] = {
L"One",
L"Two",
L"Three",
L"Four"
};

警告: 不要处理错误的数组索引。:)但是您可以轻松地添加一个函数来验证枚举,然后再从数组中获取字符串。

如果您希望获得 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是重复的。

我很想有一个 m- 映射,并将其嵌入到枚举中。

用 m [ MyEnum.VAL1] = “值1”设置;

一切都结束了。

如果希望枚举名称本身为字符串,请参见 这篇文章。 否则,使用 std::map<MyEnum, char const*>就可以很好地工作(没有必要将字符串文字复制到映射中的 std: : 字符串)

对于额外的语法糖,下面介绍如何编写 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上的 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添加条目

如果您不喜欢编写这些帮助程序,可以使用现成的方式提供相同的功能。

我最近在一个供应商库(Fincad)中遇到了同样的问题。幸运的是,供应商为所有枚举提供了 xml 文档。最后,我为每个枚举类型生成了一个映射,并为每个枚举提供了一个查找函数。此技术还允许您拦截枚举范围外的查找。

我确信 swig 可以为您做类似的事情,但我很高兴提供用 Ruby 编写的代码生成工具。

下面是代码的一个示例:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
std::map<std::string, switches::FCSW2::type> ans;
ans["Act365Fixed"] = FCSW2::Act365Fixed;
ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
ans["Act360"] = FCSW2::Act360;
ans["actual/360"] = FCSW2::Act360;
ans["Act365Act"] = FCSW2::Act365Act;
ans["actual/365 (actual)"] = FCSW2::Act365Act;
ans["ISDA30360"] = FCSW2::ISDA30360;
ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
ans["ISMA30E360"] = FCSW2::ISMA30E360;
ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
if(it != switch_map.end()) {
return it->second;
} else {
throw FCSwitchLookupError("Bad Match: FCSW2");
}
}

似乎您想要走另一条路(enum 到 string,而不是 string 到 enum) ,但是这应该是很容易逆转的。

Whit

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

借用 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)

颜色

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

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

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

看看下面的语法是否适合你:

// WeekEnd enumeration
enum WeekEnd
{
Sunday = 1,
Saturday = 7
};


// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
Enum_String( Sunday );
Enum_String( Saturday );
}
End_Enum_String;


// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"


// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

如果是这样,那么你可能想看看这篇文章:
Http://www.gamedev.net/reference/snippets/features/cppstringizing/

为了调试/分析来自其他人的代码,我曾多次要求使用此功能。 为此,我编写了一个 Perl 脚本,该脚本用几个重载的 toString方法生成一个类。每个 toString方法都接受一个 Enum作为参数并返回 const char*

当然,脚本本身不解析枚举的 C + + ,而是使用 ctag 生成符号表。

Perl 脚本在这里: Http://heinitz-it.de/download/enum2string/enum2string.pl.html

我只是想用宏来展示这种可能的优雅解决方案。这并不能解决问题,但是我认为这是一个重新思考问题的好方法。

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

——编辑2——

当使用超过2个枚举时,信息表会产生一些问题(编译器问题):

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

你的回答启发我自己写一些宏。我的要求如下:

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

  2. 不要将枚举值保存在一个单独的文件中,这个文件稍后会包含 # ,所以我可以在任何地方编写它

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

  4. 搜索应该是快速的,所以最好没有开关箱,为这些巨大的枚举

此代码创建一个带有一些值的经典枚举。此外,它创建为 std: : map,将每个枚举值映射到它的名称(即 map [ E _ SUNDAY ] = “ E _ SUNDAY”,等等)

好了,这是密码:

EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest,
const pair<int, string> & keyValue) {
dest[keyValue.first] = keyValue.second;
return dest;
}


#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

H //这是您希望在需要执行这些操作时包含的文件,您将使用其中的宏:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
name value


#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
int __makeMapTmp##mapName = __makeMap##mapName();


#define MAKE_ENUM_MAP(values, mapName) \
mapName, values(ADD_TO_MAP);

H //这是如何使用它创建自定义枚举的一个示例:

#include "EnumUtils.h*


#define MyEnumValues(ADD) \
ADD(val1, ), \
ADD(val2, ), \
ADD(val3, = 100), \
ADD(val4, )


enum MyEnum {
MyEnumValues(ADD_TO_ENUM)
};


map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings);


void MyInitializationMethod()
{
// or you can initialize it inside one of your functions/methods
MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings);
}

干杯。

这里尝试使用单行宏命令自动获取 enum 上的 < < and > > 流操作符..。

定义:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>


#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str


#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)


#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
attribute std::istream& operator>>(std::istream& is, name& e) { \
const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
std::string str; \
std::istream& r = is >> str; \
const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
const std::vector<std::string> enumStr(name##Str, name##Str + len); \
const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
if (it != enumStr.end())\
e = name(it - enumStr.begin()); \
else \
throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
return r; \
}; \
attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
return (os << name##Str[e]); \
}

用法:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);


class Essai {
public:
// Declare enum inside class
enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);


};


int main() {
std::cout << Essai::Item1 << std::endl;


Essai::Test ddd = Essai::Item1;
std::cout << ddd << std::endl;


std::istringstream strm("Item2");
strm >> ddd;


std::cout << (int) ddd << std::endl;
std::cout << ddd << std::endl;
}

虽然不确定这个方案的局限性... 欢迎提出意见!

我记得我在 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;
typedef enum {
ERR_CODE_OK = 0,
ERR_CODE_SNAP,


ERR_CODE_NUM
} ERR_CODE;


const char* g_err_msg[ERR_CODE_NUM] = {
/* ERR_CODE_OK   */ "OK",
/* ERR_CODE_SNAP */ "Oh, snap!",
};

以上是我的简单解决方案。它的一个好处是“ NUM”可以控制消息数组的大小,它还可以防止出界访问(如果您明智地使用它的话)。

您还可以定义一个函数来获取字符串:

const char* get_err_msg(ERR_CODE code) {
return g_err_msg[code];
}

继我的解决方案之后,我发现下面的一个非常有趣。总体上解决了上述问题。

幻灯片: Http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

这里的代码: Https://github.com/arunksaha/enum_to_string

我使用 这个解决方案,我复制如下:

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

我愿意承认,我花了更多的时间研究这个话题。幸运的是,在野外有很好的开源解决方案。

这是两个伟大的方法,即使还不够广为人知,

(咒语)

  • 用于 C + + 11/14/17的独立智能枚举库。它支持 C + + 中智能枚举类所能提供的所有标准功能。
  • 限制: 至少需要 C + + 11。

更好的 Enums

  • 反射编译时枚举库,语法清晰,在单个头文件中,没有依赖关系。
  • 限制: 基于宏,不能在类中使用。

这个正确的老混乱是我的努力的基础上的点和片段从 SO。For _ each 必须扩展以支持超过20个枚举值。在2019年的视觉工作室测试,叮当声和 gcc。C + + 11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
_enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
_enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
_enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
_enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
_enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))


#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)


#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)


#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)


#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)


#define error_code_enum(enum_type,...)  enum class enum_type {              \
_enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
namespace _ ## enum_type ## _detail { \
template <typename> struct _ ## enum_type ## _error_code{ \
static const std::map<enum_type, const char*> enum_type ## _map; \
}; \
template <typename T> \
const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
_enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
}; \
} \
inline const char* get_error_code_name(const enum_type& value) { \
return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
}


error_code_enum(myenum,
(one, 1),
(two)
);

产生以下代码

enum class myenum {
one = 1,
two,
};
namespace _myenum_detail {
template <typename>
struct _myenum_error_code {
static const std::map<myenum, const char*> myenum_map;
};
template <typename T>
const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
{ myenum::one, "one" },
{ myenum::two, "two" },
};
}
inline const char* get_error_code_name(const myenum& value) {
return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second;
}

真遗憾,你不得不跳过预处理器这个环节,用世界上最常用的编程语言之一来做这件事..。

我知道我迟到了,但是对于其他访问这个页面的人来说,你可以试试这个,这比其他任何东西都容易,而且更有意义:

namespace texs {
typedef std::string Type;
Type apple = "apple";
Type wood = "wood";
}

通过使用指定的数组初始值设定项,您的字符串数组独立于枚举中元素的顺序:

enum Values {
Val1,
Val2
};


constexpr string_view v_name[] = {
[Val1] = "Value 1",
[Val2] = "Value 2"
}
#include <vector>
#include <string>


//Split one comma-separated value string to vector
std::vector<std::string> split(std::string csv, char separator){/*trivial*/}


//Initializer
#define ENUMIFY(name, ...)                                                                               \
struct name                                                                                             \
{                                                                                                       \
enum Enum                                                                                       \
{                                                                                                     \
__VA_ARGS__                                                                                         \
};                                                                                                    \
static const std::vector<std::string>& Names()                                                        \
{                                                                                                     \
const static std::vector<std::string> _{split(#__VA_ARGS__, ',')}; \
return _;                                                                                           \
};                                                                                                    \
};

声明:

ENUMIFY(States, INIT, ON, OFF, RUNNING)

然后所有的枚举都是可用的,加上它们的字符串是向量化的:

std::string enum_str = States::Names()[States::ON];

另一种选择是直接使用静态向量,而不使用函数包装器。

有一种非常简单的方法来重复枚举定义,如下所示:

#ifndef ENUM_ITEMS
...
enum myEnum
{
#ifndef ENUM_ITEM
#define ENUM_ITEM(i) i
#endif // !ENUM_ITEM
#endif // !ENUM_ITEMS trick: ENUM_ITEM(i) = ENUM_ITEMS ? #i : i
ENUM_ITEM(DEFINITION),
...
ENUM_ITEM(DEFINITION_N)
#ifndef ENUM_ITEMS
};
...
#endif // !ENUM_ITEMS

然后您可以通过再次包含文件来重用它

#define ENUM_ITEMS
#define ENUM_ITEM(i) i
enum myEnum
{
#include "myCpp.cpp"
};
#undef ENUM_ITEM
static const char* myEnum[] =
{
#define ENUM_ITEM(i) #i
#include "myCpp.cpp"
// Include full file with defined ENUM_ITEMS => get enum items without code around
};
int max = sizeof(myEnum) / sizeof(char*);

不错的是 定义在这种情况下会找到适当的路线..。


那么相当可移植的 Enum 类实现呢?
它并没有为了便于理解而进行过多的优化。

#define FOREACH_FRUIT(item) \
item(apple)   \
item(orange)  \
item(grape, 5)   \
item(banana)  \

无需重复或更新定义副本。

class EnumClass
{
#define GENERATE_ENUM(ENUM, ...) ENUM,
#define GENERATE_STRINGS(STRING, ...) { #STRING, ##__VA_ARGS__ },
#define GENERATE_SIZE(...) + 1
public:
enum Enum {
FOREACH_FRUIT(GENERATE_ENUM) // apple, orange, grape, banana,
} _;
EnumClass(Enum init)
{
_ = init; // grape(2)
_EnumItem build[itemsNo] = { FOREACH_FRUIT(GENERATE_STRINGS) }; // _EnumItem build[itemsNo] = { { "apple"  }, { "orange"  }, { "grape",5 }, { "banana"  }, };
int pos = 0;
for (int i = 0; i < itemsNo; i++)
{
items[i].Name = build[i].Name;
if (0 == build[i].No) {
items[i].No = pos;
for (int j = i; j--;)
{
if (items[j].No == pos)
throw "Existing item # !";
}
pos++;
}
else {
int destPos = build[i].No;
if (destPos < pos) {
for (int j = 0; j < i; j++)
{
if (items[j].No == destPos)
throw "Existing item # !";
}
}
items[i].No = destPos;
pos = destPos + 1;
}
}
}
operator int()
{
return items[_].No;
}
operator char*()
{
return items[_].Name;
}
EnumClass& operator ++(int)
{
if (_ == itemsNo - 1) {
throw "Out of Enum options !";
}
_ = static_cast<EnumClass::Enum>(_ + 1);
return *this;
}
EnumClass& operator --(int)
{
if (0 == _) {
throw "Out of Enum options !";
}
_ = static_cast<EnumClass::Enum>(_ - 1);
return *this;
}
EnumClass operator =(int right)
{
for (int i = 0; i < itemsNo; i++)
{
if (items[i].No == right)
{
_ = static_cast<EnumClass::Enum>(i);
return *this;
}
}
throw "Enum option does not exist !";
}
EnumClass operator =(char *right)
{
for (int i = 0; i < itemsNo; i++)
{
if (!strcmp(items[i].Name, right))
{
_ = static_cast<EnumClass::Enum>(i);
return *this;
}
}
throw "Enum option does not exist !";
}
protected:
static const int itemsNo = FOREACH_FRUIT(GENERATE_SIZE); // + 1 + 1 + 1 + 1;
struct _EnumItem {
char *Name;
int No;
} items[itemsNo]; // { Name = "apple" No = 0 }, { Name = "orange" No = 1 } ,{ Name = "grape" No = 5 } ,{ Name = "banana" No = 6 }


#undef GENERATE_ENUM
#undef GENERATE_STRINGS
#undef GENERATE_SIZE
};

现在你可以做任何常见的操作 + 检查定义和运行时操作:

int main()
{
EnumClass ec(EnumClass::grape);
ec = "banana"; // ec {_=banana (3)...}
ec--; // ec {_=grape (2)...}
char *name = ec;
int val = ec; // 5
printf("%s(%i)", name, val); // grape(5)
return 0;
}

打印问题... “ 从技术上讲,编译器不知道需要哪种类型。

这是我的解决方案,我参考了一些其他的设计,但是我的设计更加完整和简单易用。

// file: enum_with_string.h
#pragma once


#include <map>
#include <string>
#include <vector>


namespace EnumString {


template <typename T>
static inline void split_string_for_each(const std::string &str,
const std::string &delimiter,
const T &foreach_function,
ssize_t max_number = -1) {
ssize_t num = 0;
std::string::size_type start;
std::string::size_type end = -1;
while (true) {
start = str.find_first_not_of(delimiter, end + 1);
if (start == std::string::npos) break;  // over


end = str.find_first_of(delimiter, start + 1);


if (end == std::string::npos) {
foreach_function(num, str.substr(start));
break;
}
foreach_function(num, str.substr(start, end - start));
++num;


if (max_number > 0 && num == max_number) break;
}
}


/**
* Strip function, delete the specified characters on both sides of the string.
*/
inline std::string &strip(std::string &s,
const std::string &characters = " \t\r\n") {
s.erase(0, s.find_first_not_of(characters));
return s.erase(s.find_last_not_of(characters) + 1);
}


static inline std::map<int, std::string> ParserEnumDefine(
const std::string &define_str) {
int cur_num = 0;
std::string cur_item_str;
std::map<int, std::string> result_map;
split_string_for_each(define_str, ",", [&](int num, const std::string &str) {
split_string_for_each(
str, "=",
[&](int num, const std::string &str) {
if (num == 0) cur_item_str = str;
if (num == 1) cur_num = std::stoi(str);
},
2);
result_map.emplace(cur_num, strip(cur_item_str));
cur_num++;
});
return result_map;
}


}  // namespace EnumString


/**
* Example:
* @code
* @endcode
*/
#define ENUM_WITH_STRING(Name, ...)                                     \
enum class Name { __VA_ARGS__, __COUNT };                             \
static inline const std::string &to_string(Name value) {              \
static const auto map = EnumString::ParserEnumDefine(#__VA_ARGS__); \
static const std::string cannot_converted =                         \
"Cannot be converted to string";                                \
int int_value = (int)value;                                         \
if (map.count(int_value))                                           \
return map.at(int_value);                                         \
else                                                                \
return cannot_converted;                                          \
}

你可以这样使用它:

#include <iostream>
#include "enum_with_string.h"
ENUM_WITH_STRING(Animal, dog, cat, monkey = 50, fish, human = 100, duck)
int main() {
std::cout << to_string(Animal::dog) << std::endl;
std::cout << to_string(Animal::cat) << std::endl;
std::cout << to_string(Animal::monkey) << std::endl;
std::cout << to_string(Animal::fish) << std::endl;
std::cout << to_string(Animal::human) << std::endl;
std::cout << to_string(Animal::duck) << std::endl;
}

我有一个 Github 要点