有没有一种简单的方法来将C ++枚举转换为字符串?

假设我们有一些命名枚举:

enum MyEnum { FOO, BAR = 0x50 }; 

我搜索的是一个脚本(任何语言),它扫描我的项目中的所有标题,并生成一个标头,每个枚举一个函数。

 char* enum_to_string(MyEnum t); 

和一个像这样的实现:

 char* enum_to_string(MyEnum t){ switch(t){ case FOO: return "FOO"; case BAR: return "BAR"; default: return "INVALID ENUM"; } } 

这个问题实际上是用typedefed枚举和未命名的C风格枚举。 有人知道这个吗?

编辑:解决方案不应该修改我的源,除了生成的功能。 枚举是在一个API中,所以使用到目前为止提出的解决方案不是一个选项。

你可能想看看GCCXML 。

在示例代码上运行GCCXML会产生:

 <GCC_XML> <Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/> <Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/> <Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1"> <EnumValue name="FOO" init="0"/> <EnumValue name="BAR" init="80"/> </Enumeration> <File id="f0" name="my_enum.h"/> </GCC_XML> 

您可以使用任何您喜欢的语言来提取Enumeration和EnumValue标记并生成所需的代码。

X宏是最好的解决方案。 例:

 #include <iostream> 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 }; std::ostream& operator<<(std::ostream& os, enum Colours c) { if (c >= ColoursCount || c < 0) return os << "???"; return os << colours_str[c]; } int main() { std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl; } 

colours.def:

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

但是,我通常更喜欢以下方法,所以可以稍微调整一下字符串。

 #define X(a, b) a, #define X(a, b) b, X(Red, "red") X(Green, "green") // etc. 

@hydroo:没有额外的文件:

 #define SOME_ENUM(DO) \ DO(Foo) \ DO(Bar) \ DO(Baz) #define MAKE_ENUM(VAR) VAR, enum MetaSyntacticVariable{ SOME_ENUM(MAKE_ENUM) }; #define MAKE_STRINGS(VAR) #VAR, const char* const MetaSyntacticVariableNames[] = { SOME_ENUM(MAKE_STRINGS) }; 

我倾向于做的是创建一个C数组,名称与枚举值的顺序和位置相同。

例如。

 enum colours { red, green, blue }; const char *colour_names[] = { "red", "green", "blue" }; 

那么你可以在你想要一个人类可读值的地方使用数组,例如

 colours mycolour = red; cout << "the colour is" << colour_names[mycolour]; 

在某些情况下,你可以用字符串化操作符(见预处理器引用中的#)做一些尝试,例如:

 #define printword(XX) cout << #XX; printword(red); 

将打印“红色”标准输出。 不幸的是,它不能用于变量(因为你会得到打印出的变量名)

我有一个令人难以置信的简单的使用宏,这完全干的方式。 它涉及可变宏和一些简单的解析魔术。 开始:

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

QT能够得到(感谢元对象编译器): 链接

我今天刚刚重新发明了这个轮子,并认为我会分享它。

这个实现不需要对定义常量的代码进行任何修改,这些常量可以是枚举或者#define或者其他任何可以转换为整数的东西 – 在我的例子中,我用其他符号定义了符号。 它也适用于稀疏值。 它甚至允许多个名称相同的值,总是返回第一个。 唯一的缺点是它需要你创建一个常量表,当新的例子被添加时,这些常量表可能会过时。

 struct IdAndName { int id; const char * name; bool operator<(const IdAndName &rhs) const { return id < rhs.id; } }; #define ID_AND_NAME(x) { x, #x } const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end) { if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id) std::stable_sort(table_begin, table_end); IdAndName searchee = { id, NULL }; IdAndName *p = std::lower_bound(table_begin, table_end, searchee); return (p == table_end || p->id != id) ? NULL : p->name; } template<int N> const char * IdToName(int id, IdAndName (&table)[N]) { return IdToName(id, &table[0], &table[N]); } 

一个如何使用它的例子:

 static IdAndName WindowsErrorTable[] = { ID_AND_NAME(INT_MAX), // flag value to indicate unsorted table ID_AND_NAME(NO_ERROR), ID_AND_NAME(ERROR_INVALID_FUNCTION), ID_AND_NAME(ERROR_FILE_NOT_FOUND), ID_AND_NAME(ERROR_PATH_NOT_FOUND), ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES), ID_AND_NAME(ERROR_ACCESS_DENIED), ID_AND_NAME(ERROR_INVALID_HANDLE), ID_AND_NAME(ERROR_ARENA_TRASHED), ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY), ID_AND_NAME(ERROR_INVALID_BLOCK), ID_AND_NAME(ERROR_BAD_ENVIRONMENT), ID_AND_NAME(ERROR_BAD_FORMAT), ID_AND_NAME(ERROR_INVALID_ACCESS), ID_AND_NAME(ERROR_INVALID_DATA), ID_AND_NAME(ERROR_INVALID_DRIVE), ID_AND_NAME(ERROR_CURRENT_DIRECTORY), ID_AND_NAME(ERROR_NOT_SAME_DEVICE), ID_AND_NAME(ERROR_NO_MORE_FILES) }; const char * error_name = IdToName(GetLastError(), WindowsErrorTable); 

IdToName函数依赖于std::lower_bound进行快速查找,这需要对表进行排序。 如果表格中的前两个条目不合格,则该功能将自动对其进行排序。

编辑:评论让我想到了另一种使用相同原则的方式。 宏简化了大switch语句的生成。

 #define ID_AND_NAME(x) case x: return #x const char * WindowsErrorToName(int id) { switch(id) { ID_AND_NAME(ERROR_INVALID_FUNCTION); ID_AND_NAME(ERROR_FILE_NOT_FOUND); ID_AND_NAME(ERROR_PATH_NOT_FOUND); ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES); ID_AND_NAME(ERROR_ACCESS_DENIED); ID_AND_NAME(ERROR_INVALID_HANDLE); ID_AND_NAME(ERROR_ARENA_TRASHED); ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY); ID_AND_NAME(ERROR_INVALID_BLOCK); ID_AND_NAME(ERROR_BAD_ENVIRONMENT); ID_AND_NAME(ERROR_BAD_FORMAT); ID_AND_NAME(ERROR_INVALID_ACCESS); ID_AND_NAME(ERROR_INVALID_DATA); ID_AND_NAME(ERROR_INVALID_DRIVE); ID_AND_NAME(ERROR_CURRENT_DIRECTORY); ID_AND_NAME(ERROR_NOT_SAME_DEVICE); ID_AND_NAME(ERROR_NO_MORE_FILES); default: return NULL; } } 
 #define stringify( name ) # name enum MyEnum { ENUMVAL1 }; ...stuff... stringify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1 

进一步讨论这种方法

预处理器指令技巧为新人

有趣的是看多少种方法。 这是我很久以前用过的一个:

在文件myenummap.h中:

 #include <map> #include <string> enum test{ one, two, three, five=5, six, seven }; struct mymap : std::map<unsigned int, std::string> { mymap() { this->operator[]( one ) = "ONE"; this->operator[]( two ) = "TWO"; this->operator[]( three ) = "THREE"; this->operator[]( five ) = "FIVE"; this->operator[]( six ) = "SIX"; this->operator[]( seven ) = "SEVEN"; }; ~mymap(){}; }; 

在main.cpp中

 #include "myenummap.h" ... mymap nummap; std::cout<< nummap[ one ] << std::endl; 

它不是常量,但它很方便。

这是另一种使用C ++ 11功能的方法。 这是const,不会继承STL容器,并且有点整齐:

 #include <vector> #include <string> #include <algorithm> #include <iostream> //These stay together and must be modified together enum test{ one, two, three, five=5, six, seven }; std::string enum_to_str(test const& e) { typedef std::pair<int,std::string> mapping; auto m = [](test const& e,std::string const& s){return mapping(static_cast<int>(e),s);}; std::vector<mapping> const nummap = { m(one,"one"), m(two,"two"), m(three,"three"), m(five,"five"), m(six,"six"), m(seven,"seven"), }; for(auto i : nummap) { if(i.first==static_cast<int>(e)) { return i.second; } } return ""; } int main() { // std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<<std::endl; //returns an empty string std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works return 0; } 
 #include <stdarg.h> #include <algorithm> #include <string> #include <vector> #include <sstream> #include <map> #define SMART_ENUM(EnumName, ...) \ class EnumName \ { \ private: \ static std::map<int, std::string> nameMap; \ public: \ enum {__VA_ARGS__}; \ private: \ static std::map<int, std::string> initMap() \ { \ using namespace std; \ \ int val = 0; \ string buf_1, buf_2, str = #__VA_ARGS__; \ replace(str.begin(), str.end(), '=', ' '); \ stringstream stream(str); \ vector<string> strings; \ while (getline(stream, buf_1, ',')) \ strings.push_back(buf_1); \ map<int, string> tmp; \ for(vector<string>::iterator it = strings.begin(); \ it != strings.end(); \ ++it) \ { \ buf_1.clear(); buf_2.clear(); \ stringstream localStream(*it); \ localStream>> buf_1 >> buf_2; \ if(buf_2.size() > 0) \ val = atoi(buf_2.c_str()); \ tmp[val++] = buf_1; \ } \ return tmp; \ } \ public: \ static std::string toString(int aInt) \ { \ return nameMap[aInt]; \ } \ }; \ std::map<int, std::string> \ EnumName::nameMap = EnumName::initMap(); 

用法:

 SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN) cout<<MyEnum::toString(MyEnum::TWO); cout<<MyEnum::toString(10); 

这可以在C ++ 11中完成

 #include <map> enum MyEnum { AA, BB, CC, DD }; static std::map< MyEnum, const char * > info = { {AA, "This is an apple"}, {BB, "This is a book"}, {CC, "This is a coffee"}, {DD, "This is a door"} }; void main() { std::cout << info[AA] << endl << info[BB] << endl << info[CC] << endl << info[DD] << endl; } 

这是对@ user3360260答案的修改。 它具有以下新功能

  • MyEnum fromString(const string&)支持
  • 使用VisualStudio 2012进行编译
  • 枚举是一个实际的POD类型(不只是常量声明),所以你可以把它分配给一个变量。
  • 添加C ++的“范围”功能(矢量形式),以允许“foreach”迭代枚举

用法:

 SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN) MyEnum foo = MyEnum::TWO; cout << MyEnum::toString(foo); // static method cout << foo.toString(); // member method cout << MyEnum::toString(MyEnum::TWO); cout << MyEnum::toString(10); MyEnum foo = myEnum::fromString("TWO"); // C++11 iteration over all values for( auto x : MyEnum::allValues() ) { cout << x.toString() << endl; } 

这是代码

 #define SMART_ENUM(EnumName, ...) \ class EnumName \ { \ public: \ EnumName() : value(0) {} \ EnumName(int x) : value(x) {} \ public: \ enum {__VA_ARGS__}; \ private: \ static void initMap(std::map<int, std::string>& tmp) \ { \ using namespace std; \ \ int val = 0; \ string buf_1, buf_2, str = #__VA_ARGS__; \ replace(str.begin(), str.end(), '=', ' '); \ stringstream stream(str); \ vector<string> strings; \ while (getline(stream, buf_1, ',')) \ strings.push_back(buf_1); \ for(vector<string>::iterator it = strings.begin(); \ it != strings.end(); \ ++it) \ { \ buf_1.clear(); buf_2.clear(); \ stringstream localStream(*it); \ localStream>> buf_1 >> buf_2; \ if(buf_2.size() > 0) \ val = atoi(buf_2.c_str()); \ tmp[val++] = buf_1; \ } \ } \ int value; \ public: \ operator int () const { return value; } \ std::string toString(void) const { \ return toString(value); \ } \ static std::string toString(int aInt) \ { \ return nameMap()[aInt]; \ } \ static EnumName fromString(const std::string& s) \ { \ auto it = find_if(nameMap().begin(), nameMap().end(), [s](const std::pair<int,std::string>& p) { \ return p.second == s; \ }); \ if (it == nameMap().end()) { \ /*value not found*/ \ throw EnumName::Exception(); \ } else { \ return EnumName(it->first); \ } \ } \ class Exception : public std::exception {}; \ static std::map<int,std::string>& nameMap() { \ static std::map<int,std::string> nameMap0; \ if (nameMap0.size() ==0) initMap(nameMap0); \ return nameMap0; \ } \ static std::vector<EnumName> allValues() { \ std::vector<EnumName> x{ __VA_ARGS__ }; \ return x; \ } \ bool operator<(const EnumName a) const { return (int)*this < (int)a; } \ }; 

请注意,转换为字符串是一个快速查找,而从字符串转换是一个缓慢的线性搜索。 但是字符串非常昂贵(和相关的文件IO),我不觉得需要优化或使用bimap。

Suma的宏观解决方案很好。 不过,你不需要有两个不同的宏。 C ++将愉快地包含一个头两次。 只要不要包括警卫。

所以你会有一个foobar.h定义

 ENUM(Foo, 1) ENUM(Bar, 2) 

你可以像这样包含它:

 #define ENUMFACTORY_ARGUMENT "foobar.h" #include "enumfactory.h" 

enumfactory.h将执行2 #include ENUMFACTORY_ARGUMENT s。 在第一轮中,它像Suma的DECLARE_ENUM一样扩展了ENUM; 在第二轮中,ENUM的工作方式与DEFINE_ENUM类似。

你也可以多次包含enumfactory.h,只要你传递给ENUMFACTORY_ARGUMENT的#define

请注意,您的转换函数理想情况下应返回一个const char *。

如果你有能力把你的枚举放在单独的头文件中,你也许可以用宏来做这样的事情(哦,这将是丑陋的):

 #include "enum_def.h" #include "colour.h" #include "enum_conv.h" #include "colour.h" 

其中enum_def.h有:

 #undef ENUM_START #undef ENUM_ADD #undef ENUM_END #define ENUM_START(NAME) enum NAME { #define ENUM_ADD(NAME, VALUE) NAME = VALUE, #define ENUM_END }; 

而enum_conv.h有:

 #undef ENUM_START #undef ENUM_ADD #undef ENUM_END #define ENUM_START(NAME) const char *##NAME##_to_string(NAME val) { switch (val) { #define ENUM_ADD(NAME, VALUE) case NAME: return #NAME; #define ENUM_END default: return "Invalid value"; } } 

最后,colour.h有:

 ENUM_START(colour) ENUM_ADD(red, 0xff0000) ENUM_ADD(green, 0x00ff00) ENUM_ADD(blue, 0x0000ff) ENUM_END 

你可以使用转换功能:

 printf("%s", colour_to_string(colour::red)); 

这是丑陋的,但它是唯一的方法(在预处理器级别),可以让你只在代码中的一个地方定义枚举。 因此,您的代码不容易出现错误,因为修改了枚举。 您的枚举定义和转换函数将始终保持同步。 不过,我再说一遍,这很丑陋:)

另一个答案:在某些情况下,定义非代码格式的枚举是有意义的,例如CSV,YAML或XML文件,然后从定义中生成C ++枚举代码和字符串代码。 这种方法在您的应用程序中可能会或可能不会实用,但要记住这一点。

我用独立的并行枚举包装类,这是与宏生成的。 有几个优点:

  • 可以生成他们的枚举我不定义(例如:操作系统平台头枚举)
  • 可以将范围检查纳入包装类
  • 可以使用位域枚举进行“更智能”的格式化

不利的一面是,我需要复制格式化类中的枚举值,而我没有任何脚本来生成它们。 除此之外,它似乎工作得很好。

这是一个来自我的代码库枚举的例子,没有实现宏和模板的所有框架代码,但你可以得到这样的想法:

 enum EHelpLocation { HELP_LOCATION_UNKNOWN = 0, HELP_LOCAL_FILE = 1, HELP_HTML_ONLINE = 2, }; class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation > { public: static inline CString FormatEnum( EHelpLocation eValue ) { switch ( eValue ) { ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCATION_UNKNOWN ); ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCAL_FILE ); ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_HTML_ONLINE ); default: return FormatAsNumber( eValue ); } } }; DECLARE_RANGE_CHECK_CLASS( EHelpLocation, CRangeInfoSequential< HELP_HTML_ONLINE > ); typedef ESmartEnum< EHelpLocation, HELP_LOCATION_UNKNOWN, CEnumFormatter_EHelpLocation, CRangeInfo_EHelpLocation > SEHelpLocation; 

这个想法是,而不是使用EHelpLocation,你使用SEHelpLocation; 一切工作是一样的,但你得到范围检查和枚举变量本身的“格式()”方法。 如果您需要格式化独立值,则可以使用CEnumFormatter_EHelpLocation :: FormatEnum(…)。

希望这是有帮助的。 我意识到这也没有解决关于一个脚本实际上生成其他类的原始问题,但我希望这个结构可以帮助某个人试图解决同样的问题,或者写出这样的脚本。

这里是一个单一的解决方案(根据@Marcin的优雅的答案:

 #include <iostream> #define ENUM_TXT \ X(Red) \ X(Green) \ X(Blue) \ X(Cyan) \ X(Yellow) \ X(Magenta) \ enum Colours { # define X(a) a, ENUM_TXT # undef X ColoursCount }; char const* const colours_str[] = { # define X(a) #a, ENUM_TXT # undef X 0 }; std::ostream& operator<<(std::ostream& os, enum Colours c) { if (c >= ColoursCount || c < 0) return os << "???"; return os << colours_str[c] << std::endl; } int main() { std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl; } 

这是我的BOOST解决方案:

 #include <boost/preprocessor.hpp> #define X_STR_ENUM_TOSTRING_CASE(r, data, elem) \ case elem : return BOOST_PP_STRINGIZE(elem); #define X_ENUM_STR_TOENUM_IF(r, data, elem) \ else if(data == BOOST_PP_STRINGIZE(elem)) return elem; #define STR_ENUM(name, enumerators) \ enum name { \ BOOST_PP_SEQ_ENUM(enumerators) \ }; \ \ inline const QString enumToStr(name v) \ { \ switch (v) \ { \ BOOST_PP_SEQ_FOR_EACH( \ X_STR_ENUM_TOSTRING_CASE, \ name, \ enumerators \ ) \ \ default: \ return "[Unknown " BOOST_PP_STRINGIZE(name) "]"; \ } \ } \ \ template <typename T> \ inline const T strToEnum(QString v); \ \ template <> \ inline const name strToEnum(QString v) \ { \ if(v=="") \ throw std::runtime_error("Empty enum value"); \ \ BOOST_PP_SEQ_FOR_EACH( \ X_ENUM_STR_TOENUM_IF, \ v, \ enumerators \ ) \ \ else \ throw std::runtime_error( \ QString("[Unknown value %1 for enum %2]") \ .arg(v) \ .arg(BOOST_PP_STRINGIZE(name)) \ .toStdString().c_str()); \ } 

要创建枚举,请声明:

 STR_ENUM ( SERVICE_RELOAD, (reload_log) (reload_settings) (reload_qxml_server) ) 

对于转换:

 SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log"); QString serviceReloadStr = enumToStr(reload_log); 

答案0的问题是枚举二进制值不一定从0开始,不一定是连续的。

当我需要这个时,我通常:

  • 将枚举定义拉入我的源代码中
  • 编辑它以获取名称
  • 做一个宏来改变问题中的case子句的名字,尽管通常在一行:case foo:return“foo”;
  • 添加开关,默认和其他语法使其合法

下面的ruby脚本尝试解析标题,并将原始标题旁边的所需源构建。

 #! /usr/bin/env ruby # Let's "parse" the headers # Note that using a regular expression is rather fragile # and may break on some inputs GLOBS = [ "toto/*.h", "tutu/*.h", "tutu/*.hxx" ] enums = {} GLOBS.each { |glob| Dir[glob].each { |header| enums[header] = File.open(header, 'rb') { |f| f.read }.scan(/enum\s+(\w+)\s+\{\s*([^}]+?)\s*\}/m).collect { |enum_name, enum_key_and_values| [ enum_name, enum_key_and_values.split(/\s*,\s*/).collect { |enum_key_and_value| enum_key_and_value.split(/\s*=\s*/).first } ] } } } # Now we build a .h and .cpp alongside the parsed headers # using the template engine provided with ruby require 'erb' template_h = ERB.new <<-EOS #ifndef <%= enum_name %>_to_string_h_ #define <%= enum_name %>_to_string_h_ 1 #include "<%= header %>" char* enum_to_string(<%= enum_name %> e); #endif EOS template_cpp = ERB.new <<-EOS #include "<%= enum_name %>_to_string.h" char* enum_to_string(<%= enum_name %> e) { switch (e) {<% enum_keys.each do |enum_key| %> case <%= enum_key %>: return "<%= enum_key %>";<% end %> default: return "INVALID <%= enum_name %> VALUE"; } } EOS enums.each { |header, enum_name_and_keys| enum_name_and_keys.each { |enum_name, enum_keys| File.open("#{File.dirname(header)}/#{enum_name}_to_string.h", 'wb') { |built_h| built_h.write(template_h.result(binding)) } File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", 'wb') { |built_cpp| built_cpp.write(template_cpp.result(binding)) } } } 

使用正则表达式使得这个“解析器”非常脆弱,它可能无法正常处理特定的头文件。

假设您有一个标题toto / ah,包含枚举MyEnum和MyEnum2的定义。 该脚本将构建:

 toto/MyEnum_to_string.h toto/MyEnum_to_string.cpp toto/MyEnum2_to_string.h toto/MyEnum2_to_string.cpp 

更强大的解决方案将是:

  • 构建从另一个来源定义枚举和操作的所有源。 这意味着你将定义你的枚举在一个XML / YML /任何文件比C / C ++更容易解析。
  • 使用像Avdi建议的真正的编译器。
  • 使用带或不带模板的预处理器宏。

这是未发行的软件,但似乎来自Frank Laub的BOOST_ENUM可能适合该法案。 我喜欢的部分是,你可以在一个类的范围内定义一个枚举,这个类的大部分基于宏的枚举通常不允许你这样做。 它位于Boost Vault中: http : //www.boostpro.com/vault/index.php? action=downloadfile&filename = enum_rev4.6.zip&directory =&它自2006年以来没有任何发展,所以我没有知道它如何编译新的Boost版本。 在libs / test下查看使用示例。

这几乎是它可以完成的唯一方法(一个字符串数组也可以工作)。

问题是,一旦编译了一个C程序,enum的二进制值就是所有使用的,并且名字不见了。

这是我写的一个CLI程序,可以很容易地将枚举转换为字符串。 它易于使用,大约需要5秒钟的时间来完成(包括时间cd到包含该程序的目录,然后运行它,传递给它包含枚举的文件)。

在这里下载: http : //www.mediafire.com/?nttignoozzz

讨论主题在这里: http : //cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

使用“–help”参数运行程序以获取如何使用它的说明。

不久之前,我做了一些技巧,让枚举在QComboBox中正确显示,并将枚举和字符串表示定义为一个语句

 #pragma once #include <boost/unordered_map.hpp> namespace enumeration { struct enumerator_base : boost::noncopyable { typedef boost::unordered_map<int, std::wstring> kv_storage_t; typedef kv_storage_t::value_type kv_type; kv_storage_t const & kv() const { return storage_; } LPCWSTR name(int i) const { kv_storage_t::const_iterator it = storage_.find(i); if(it != storage_.end()) return it->second.c_str(); return L"empty"; } protected: kv_storage_t storage_; }; template<class T> struct enumerator; template<class D> struct enum_singleton : enumerator_base { static enumerator_base const & instance() { static D inst; return inst; } }; } #define QENUM_ENTRY(K, V, N) K, N storage_.insert(std::make_pair((int)K, V)); #define QBEGIN_ENUM(NAME, C) \ enum NAME \ { \ C \ } \ }; \ } \ #define QEND_ENUM(NAME) \ }; \ namespace enumeration \ { \ template<> \ struct enumerator<NAME>\ : enum_singleton< enumerator<NAME> >\ { \ enumerator() \ { //usage /* QBEGIN_ENUM(test_t, QENUM_ENTRY(test_entry_1, L"number uno", QENUM_ENTRY(test_entry_2, L"number dos", QENUM_ENTRY(test_entry_3, L"number tres", QEND_ENUM(test_t))))) */ 

现在你有enumeration::enum_singleton<your_enum>::instance()能够将枚举转换为字符串。 如果你用boost::bimap替换kv_storage_t ,你也可以做后向转换。 由于Qt对象不能作为模板,所以引入了转换器的通用基类来将其存储在Qt对象中

以前的样子

作为变种,使用简单的库> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

在代码中

 #include <EnumString.h> enum FORM { F_NONE = 0, F_BOX, F_CUBE, F_SPHERE, }; 

添加行

 Begin_Enum_String( FORM ) { Enum_String( F_NONE ); Enum_String( F_BOX ); Enum_String( F_CUBE ); Enum_String( F_SPHERE ); } End_Enum_String; 

如果enum中的值不是dublicate工作正常。

用法示例

 enum FORM f = ... const std::string& str = EnumString< FORM >::From( f ); 

反之亦然

 assert( EnumString< FORM >::To( f, str ) ); 

这是一个尝试获取<<和>>流运算符自动使用一行宏命令自动枚举…

定义:

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

Not sure about the limitations of this scheme though… comments are welcome!

 #include <iostream> #include <map> #define IDMAP(x) (x,#x) std::map<int , std::string> enToStr; class mapEnumtoString { public: mapEnumtoString(){ } mapEnumtoString& operator()(int i,std::string str) { enToStr[i] = str; return *this; } public: std::string operator [] (int i) { return enToStr[i]; } }; mapEnumtoString k; mapEnumtoString& init() { return k; } int main() { init() IDMAP(1) IDMAP(2) IDMAP(3) IDMAP(4) IDMAP(5); std::cout<<enToStr[1]; std::cout<<enToStr[2]; std::cout<<enToStr[3]; std::cout<<enToStr[4]; std::cout<<enToStr[5]; } 

Check this post:

Class implementation of C++ Enums

it contains class implementation of c++ enum.

I want to post this in case someone finds it useful.

In my case, I simply need to generate ToString() and FromString() functions for a single C++11 enum from a single .hpp file.

I wrote a python script that parses the header file containing the enum items and generates the functions in a new .cpp file.

You can add this script in CMakeLists.txt with execute_process , or as a pre-build event in Visual Studio. The .cpp file will be automatically generated, without the need to manually update it each time a new enum item is added.

generate_enum_strings.py

 # This script is used to generate strings from C++ enums import re import sys import os fileName = sys.argv[1] enumName = os.path.basename(os.path.splitext(fileName)[0]) with open(fileName, 'r') as f: content = f.read().replace('\n', '') searchResult = re.search('enum(.*)\{(.*?)\};', content) tokens = searchResult.group(2) tokens = tokens.split(',') tokens = map(str.strip, tokens) tokens = map(lambda token: re.search('([a-zA-Z0-9_]*)', token).group(1), tokens) textOut = '' textOut += '\n#include "' + enumName + '.hpp"\n\n' textOut += 'namespace myns\n' textOut += '{\n' textOut += ' std::string ToString(ErrorCode errorCode)\n' textOut += ' {\n' textOut += ' switch (errorCode)\n' textOut += ' {\n' for token in tokens: textOut += ' case ' + enumName + '::' + token + ':\n' textOut += ' return "' + token + '";\n' textOut += ' default:\n' textOut += ' return "Last";\n' textOut += ' }\n' textOut += ' }\n' textOut += '\n' textOut += ' ' + enumName + ' FromString(const std::string &errorCode)\n' textOut += ' {\n' textOut += ' if ("' + tokens[0] + '" == errorCode)\n' textOut += ' {\n' textOut += ' return ' + enumName + '::' + tokens[0] + ';\n' textOut += ' }\n' for token in tokens[1:]: textOut += ' else if("' + token + '" == errorCode)\n' textOut += ' {\n' textOut += ' return ' + enumName + '::' + token + ';\n' textOut += ' }\n' textOut += '\n' textOut += ' return ' + enumName + '::Last;\n' textOut += ' }\n' textOut += '}\n' fileOut = open(enumName + '.cpp', 'w') fileOut.write(textOut) 

例:

ErrorCode.hpp

 #pragma once #include <string> #include <cstdint> namespace myns { enum class ErrorCode : uint32_t { OK = 0, OutOfSpace, ConnectionFailure, InvalidJson, DatabaseFailure, HttpError, FileSystemError, FailedToEncrypt, FailedToDecrypt, EndOfFile, FailedToOpenFileForRead, FailedToOpenFileForWrite, FailedToLaunchProcess, Last }; std::string ToString(ErrorCode errorCode); ErrorCode FromString(const std::string &errorCode); } 

Run python generate_enum_strings.py ErrorCode.hpp

结果:

ErrorCode.cpp

 #include "ErrorCode.hpp" namespace myns { std::string ToString(ErrorCode errorCode) { switch (errorCode) { case ErrorCode::OK: return "OK"; case ErrorCode::OutOfSpace: return "OutOfSpace"; case ErrorCode::ConnectionFailure: return "ConnectionFailure"; case ErrorCode::InvalidJson: return "InvalidJson"; case ErrorCode::DatabaseFailure: return "DatabaseFailure"; case ErrorCode::HttpError: return "HttpError"; case ErrorCode::FileSystemError: return "FileSystemError"; case ErrorCode::FailedToEncrypt: return "FailedToEncrypt"; case ErrorCode::FailedToDecrypt: return "FailedToDecrypt"; case ErrorCode::EndOfFile: return "EndOfFile"; case ErrorCode::FailedToOpenFileForRead: return "FailedToOpenFileForRead"; case ErrorCode::FailedToOpenFileForWrite: return "FailedToOpenFileForWrite"; case ErrorCode::FailedToLaunchProcess: return "FailedToLaunchProcess"; case ErrorCode::Last: return "Last"; default: return "Last"; } } ErrorCode FromString(const std::string &errorCode) { if ("OK" == errorCode) { return ErrorCode::OK; } else if("OutOfSpace" == errorCode) { return ErrorCode::OutOfSpace; } else if("ConnectionFailure" == errorCode) { return ErrorCode::ConnectionFailure; } else if("InvalidJson" == errorCode) { return ErrorCode::InvalidJson; } else if("DatabaseFailure" == errorCode) { return ErrorCode::DatabaseFailure; } else if("HttpError" == errorCode) { return ErrorCode::HttpError; } else if("FileSystemError" == errorCode) { return ErrorCode::FileSystemError; } else if("FailedToEncrypt" == errorCode) { return ErrorCode::FailedToEncrypt; } else if("FailedToDecrypt" == errorCode) { return ErrorCode::FailedToDecrypt; } else if("EndOfFile" == errorCode) { return ErrorCode::EndOfFile; } else if("FailedToOpenFileForRead" == errorCode) { return ErrorCode::FailedToOpenFileForRead; } else if("FailedToOpenFileForWrite" == errorCode) { return ErrorCode::FailedToOpenFileForWrite; } else if("FailedToLaunchProcess" == errorCode) { return ErrorCode::FailedToLaunchProcess; } else if("Last" == errorCode) { return ErrorCode::Last; } return ErrorCode::Last; } } 

Well, yet another option. A typical use case is where you need constant for the HTTP verbs as well as using is string version values.

例子:

 int main () { VERB a = VERB::GET; VERB b = VERB::GET; VERB c = VERB::POST; VERB d = VERB::PUT; VERB e = VERB::DELETE; std::cout << a.toString() << std::endl; std::cout << a << std::endl; if ( a == VERB::GET ) { std::cout << "yes" << std::endl; } if ( a == b ) { std::cout << "yes" << std::endl; } if ( a != c ) { std::cout << "no" << std::endl; } } 

The VERB class:

 // ----------------------------------------------------------- // ----------------------------------------------------------- class VERB { private: // private constants enum Verb {GET_=0, POST_, PUT_, DELETE_}; // private string values static const std::string theStrings[]; // private value const Verb value; const std::string text; // private constructor VERB (Verb v) : value(v), text (theStrings[v]) { // std::cout << " constructor \n"; } public: operator const char * () const { return text.c_str(); } operator const std::string () const { return text; } const std::string toString () const { return text; } bool operator == (const VERB & other) const { return (*this).value == other.value; } bool operator != (const VERB & other) const { return ! ( (*this) == other); } // --- static const VERB GET; static const VERB POST; static const VERB PUT; static const VERB DELETE; }; const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"}; const VERB VERB::GET = VERB ( VERB::Verb::GET_ ); const VERB VERB::POST = VERB ( VERB::Verb::POST_ ); const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ ); const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ ); // end of file