C/C++的enum,无法直接转换到字符串,比如 enum FRUIT {apple, grape=10, orange};,永远只有0、10、11,无法得到能直观理解的“appl”、“grape”、“orange”了。Java语言就能在打印枚举值时打印出字符串。
cout << FRUIT(10) 我们希望输出的是grape,而不是10
直到C++2020标准都出台了,也没听说C/C++给出标准解决方案。而这个需求其实一直非常强烈。
常见的解决方案:
1)做一个class,它有一个operator int()成员函数,然后定义若干这个class的全局变量冒充枚举。缺点就是,在模板编程的时候,可能因为模板会阻止类型间转换动作而导致这个对象可能无法正确转换到int
2)幅度修改定义枚举的语法,实现了夹带私货的动作
#define FOREACH_FRUIT(FRUIT, OP) \ FRUIT(apple) \ FRUIT(orange OP(apple)) \ FRUIT(grape) \ FRUIT(banana OP(0xFFFF)) enum FRUIT_ENUM {FOREACH_FRUIT(GENERATE_ENUM, ASSIGN)}; struct {int value; const char * name;} FRUIT_ENUM_VALUE_TO_NAME[] = {FOREACH_FRUIT(GENERATE_STRING, EQUAL)};
3) 尽可能接近enum语法,定义一个真实enum。但是,很难支持grape=10这样的指定值语法。
这里给出一个可以解决这个困难的方案,要点是使用optional甩开=前面的部分,而大家一般是是试图甩开=后面的部分,导致无解:
- #define SEMOICOLON() ;
- #define TO_ENUM_VALUE_NAME(a) [](){\
- std::optional<int> x, a;\
- x=a;\
- if (x)\
- {\
- v.emplace_back(*x, #a);\
- }\
- else if (v.empty())\
- {\
- v.emplace_back(0, #a);\
- }\
- else\
- {\
- v.emplace_back(v.back().first + 1, #a);\
- }\
- }()
- #define ENUM(EnumName, ...) enum EnumName {__VA_ARGS__};\
- inline string to_string(EnumName x)\
- {\
- static vector<pair<int, string>> v;\
- static bool inited = [](){\
- FOR_EACH(TO_ENUM_VALUE_NAME, SEMOICOLON, __VA_ARGS__);\
- return true;\
- }();\
- static map<int, string> EnumName##_StrMap;\
- auto iter = EnumName##_StrMap.find(x);\
- if (iter == EnumName##_StrMap.end())\
- {\
- std::tie(iter, std::ignore) = EnumName##_StrMap.emplace(x, to_string(int(x)) + "(" + #EnumName + ":"+ algorithm::join(v | adaptors::filtered([x](auto & a){return a.first == int(x);}) | adaptors::map_values, "/") + ")");\
- }\
- return iter->second;\
- }
- ENUM(FRUIT, apple, grape = apple + 1, orange = 10, banana=orange);
不定参数宏的遍历,网上很多讲解,要点就是使用PP_NARG宏,所以FOR_EACH宏部分就留给大家作为作业了
最终效果:
- cout << apple << " " << grape << " " << orange << endl;
- cout << to_string(FRUIT(0)) << endl;
- cout << to_string(grape) << endl;
- cout << to_string(FRUIT(10)) << endl;
输出:
- 0 1 10
- 0(FRUIT:apple)
- 1(FRUIT:grape = apple + 1)
- 10(FRUIT:orange = 10/banana=orange