(转载)C++中枚举与字符串相互转换

时间:2021-11-24 17:50:23

C++中枚举与字符串相互转换


 

前言

有的时候我们喜欢使用一些外部的文件保存管理一些配置信息,这些配置文件大多都是文本格式例如ini,xml等,这样方便编辑和管理。因此在使用的过程中必然会遇到各种字符串转换问题。
最常见的便是将字符串的数字转换为对应的整形(integer)或者浮点(float),如果遇到枚举类型,可能便会想当做是整形来处理,但觉得不是特别理想。如果能有办法直接转换为枚举会方便很多。

案例

一个简单的配置文件:

1 [config]
2 fooInt = 10
3 fooFloat = 2.5
4 fooEnum = cpp

在C++里面有枚举:

1 enum ProgLang
2 {
3     e_cpp,
4     e_java,
5     e_csharp
6 };

如果对于前面两个数值,可以很简单的读取,例如:

1 Config cfg;//Config 类见<<C++编写Config类读取配置文件>>
2 cfg.Load("config.ini");
3  
4 int fooInt = cfg.GetValue<int>("fooInt");
5 float fooFloat = cfg.GetValue<float>("fooFloat");

对于后面那个枚举,可能就稍微麻烦一点:

1 ProgLang fooEnum;
2 const char* rawData = cfg.GetValue<const char*>("fooEnum");
3 if (strcmp(rawData, "cpp")) fooEnum = e_cpp;
4 else if (strcmp(rawData, "java")) fooEnum = e_java;
5 else if (strcmp(rawData, "csharp")) fooEnum = e_csharp;

如果直接在配置文件里面不保存”cpp“字符串而直接保存枚举值,这里便可以当整形取出来再做类型转换。不过这样不安全,也不好维护,如果枚举内部的定义发生了变化,外部保存的数据自然也就会失效出错。

解决方案

C++没有(也不可能会有)反射机制,枚举成员在编译以后也已经变成了纯粹的数值,失去了名字。所以想使用字符串作为搜索依据,必须为枚举保留一份名字信息,例如:

1 enum ProgLang
2 {
3     e_cpp,
4     e_java,
5     e_csharp
6 };
7 const char* ProgLangNames[] = { "cpp", "java", "csharp" };

这个字符串数组只能预先写好,无论是手动还是通过工具自动生成。然后可以在这个字符串数组里面搜索目标字符串,将找到的结果下标转换为对应的枚举值即可,例如:

1 for(int i = 0; i < sizeof(ProgLangNames) / sizeof(ProgLangNames[0]); ++i)
2 {
3     if (strcmp(rawData, ProgLangNames[i]))
4     {
5         fooEnum = static_cast<ProgLang>(i);
6         break;
7     }
8 }

枚举关联字符串

解决方案找到了,随之而来的问题就是如何将枚举与所需要的字符串查找表联系起来。如有多个枚举:

01 enum ProgLang
02 {
03     e_cpp,
04     e_java,
05     e_csharp
06 };
07 const char* ProgLangNames[] = { "cpp", "java", "csharp" };
08  
09 enum ScriptLang
10 {
11     e_lua,
12     e_actionscript,
13     e_javascript
14 };
15 const char* ScriptLangNames[] = { "lua", "actionscript", "javascript" };

枚举不像类或结构体可以定义自己的成员变量,所以查找表只能在外部定义,通过实例化模板类来将他们相互联系起来。例如:

1 <template EnumType>
2 struct SEnumName
3 {
4     static const char* List[];
5 }

这定义了一个查找表的模板结构体,然后将之前的代码改造为:

01 enum ProgLang
02 {
03     e_cpp,
04     e_java,
05     e_csharp
06 };
07 const char* SEnumName<ProgLang>::List[] =
08 {
09     "cpp",
10     "java",
11     "csharp"
12 };
13  
14 enum ScriptLang
15 {
16     e_lua,
17     e_actionscript,
18     e_javascript
19 };
20 const char* SEnumName<ScriptLang>::List[] =
21 {
22     "lua",
23     "actionscript",
24     "javascript"
25 };

然后便可以实现字符串转枚举的功能:

view sourceprint?
01 template<typename EnumType>
02 EnumType ConvertStringToEnum(const char* pStr)
03 {
04     EnumType fooEnum = static_cast<EnumType>(-1);
05     int count = sizeof(SEnumName<EnumType>::List) /
06         sizeof(SEnumName<EnumType>::List[0]);
07     for(int i = 0; i < count; ++i)
08     {
09         if (strcmp(rawData, SEnumName<EnumType>::List[i]))
10         {
11             fooEnum = static_cast<ProgLang>(i);
12             break;
13         }
14     }
15     return fooEnum;
16 }

这样一来,最开始的问题就可以简化为:

1 ProgLang fooEnum = cfg.GetValue<ProgLang>("fooEnum");

有了这种方式,自然枚举反转回字符串也很容易了,就不再赘述了。

 

转载出处:  http://a.vifix.us/blog/cpp-convert-string-enum