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

问题描述:

假设我们有一些命名的枚举:有没有简单的方法将C++枚举转换为字符串?

enum MyEnum { 
     FOO, 
     BAR = 0x50 
}; 

我用Google搜索什么是脚本(任何语言),扫描在我的项目的所有头,并产生与每一个枚举功​​能的标题。

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

的疑难杂症是真正与通过typedef枚举和无名的空调风格枚举。有人知道这件事吗?

编辑:解决方案不应该修改我的源,除了生成的函数。枚举在API中,因此使用到目前为止提出的解决方案不是一种选择。

+0

可能的重复[简单的方法来使用枚举类型的变量作为C中的字符串?](http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types- as-string-in-c) – karlphillip 2011-07-02 23:26:52

+0

关于基于宏的工厂移动到http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in- c#202511 - 问题更新后,它在这里不再相关。 – Suma 2008-10-14 15:42:21

你可能想看看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> 

你可以使用你喜欢拉出枚举和EnumValue标签并生成所需的代码的任何语言。

+0

非常棒!作为一个简单的Python脚本的魅力。谢谢。 – 2008-10-14 19:43:00

+6

+1,GCCXML看起来非常好! (虽然我几乎-1,因为我最初误以为这是一个建议,使用上面详细的XML语法来编码您的枚举 - 一个解决方案,它是过度工程!) – 2009-03-12 09:46:02

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

问题是,一旦C程序编译完成,枚举的二进制值就是所有使用的,并且名称不见了。

我倾向于做的是创建一个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); 

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

+0

最后一个警告(不适用于变量)是一个很大的缺点,但无论如何+1。 – chappjc 2015-08-27 17:55:12

+2

仅在您不会为枚举条目设置特殊数值时有效。 – kyb 2017-08-27 13:46:27

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

当我需要这个,我通常:

  • 拉枚举定义为我的源
  • 编辑它得到的只是名称
  • 做一个宏的名称更改为的情况下条款这个问题,尽管通常在一行上:case foo:return“foo”;
  • 添加开关,默认及其它语法,使其合法

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

颜色。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. 
+11

漂亮, 虽然我不喜欢额外的文件 – 2008-10-14 18:02:57

+0

+1,但可能会混淆Intellisense和类似的东西...... – 2008-10-14 18:26:02

+2

只要确保您的构建过程不会在每个包含之前预先包含#pragma(一次)文件... – xtofl 2008-10-14 18:47:58

Suma's macro solution很不错。不过,你不需要有两个不同的宏。 C++会高兴地包含一个头两次。只留下包括警卫。

所以你必须定义只是

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

的foobar.h中,你将包括这样的:

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

enumfactory.h会做2 #include ENUMFACTORY_ARGUMENT秒。在第一轮中,它扩大了ENUM,如Suma的DECLARE_ENUM;在第二轮ENUM的工作方式如DEFINE_ENUM

可以包括enumfactory.h多次,也只要你在不同#通过定义对ENUMFACTORY_ARGUMENT

+0

似乎像suma移动答案[这里](https://stackoverflow.com/questions/147267/easy-way-to-use - 变量-的-枚举类型-AS-字符串在-C#202511)。你可能想要在你的答案中包含链接。我只是偶然发现了这个评论,并且嘲笑了这个回答这个是相当无意义的 – user463035818 2017-06-29 15:38:10

请注意,您的转换函数理想情况下应该返回一个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)); 

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

以下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/a.h,包含枚举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建议的。
  • 使用带或不带模板的预处理器宏。

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

QT是能拉那(感谢元对象编译器)的:link

我这样做有其与宏生成单独的并排的侧枚举包装类。有几个优点:

  • 可以生成它们枚举我不定义(如:操作系统平台头枚举)
  • 可以将检查范围为包装类
  • 可以做“聪明”与格式位字段枚举

当然,缺点是我需要在格式化程序类中复制枚举值,并且我没有任何脚本来生成它们。除此之外,它似乎工作得很好。

下面是从我的代码库,SANS所有的框架代码中的枚举它实现了宏和模板的例子,但你可以得到的想法:

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;一切工作原理都是一样的,但是你可以在枚举变量本身上得到范围检查和'Format()'方法。如果您需要格式化独立值,则可以使用CEnumFormatter_EHelpLocation :: FormatEnum(...)。

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

@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) 
}; 

饶有兴趣的看着路数。这里是我很久以前用过的一个:

in 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功能的另一种方法。这是常量,不继承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; 
} 

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

这是我写的一个CLI程序,可以轻松将枚举转换为字符串。 它易于使用,并需要大约5秒钟完成(包括时间cd到包含该程序的目录,然后运行它,传递给它包含枚举的文件)。这里 http://www.mediafire.com/?nttignoozzz

讨论主题就可以了:

此处下载 http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

运行与“--help”的说法程序可以让说明如何使用它。

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

此实现执行需要对定义的常量,它可以是枚举或#define S或其他任何转予整数代码做任何改动 - 在我的情况我已经在其他符号的定义的符号。它也适用于稀疏值。它甚至允许多个名称具有相同的值,并始终返回第一个名称。唯一的缺点是它需要你创建一个常量表,当新的例子被添加时,这些常量表可能会过时。

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 

Further discussion on this method

Preprocessor directive tricks for newcomers

不久前,我做了一些技巧,有枚举正确显示在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的对象不可能是模板

Previous appearance

作为变例,使用简单的lib>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 <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); 

这里是一个企图得到关于电子< <和>>流运营商自动只有一行宏命令NUM ...

定义:

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

不知道这个方案的局限性,但...评论,欢迎!

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

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

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

这是@修改user3360260答案。它具有以下新的特点

  • MyEnum fromString(const string&)支持
  • 编译与VisualStudio的2012
  • 枚举是一个实际的POD类型(不只是const声明),这样你就可以把它分配给一个变量。
  • 加入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。

这可以在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; 
} 

这里一个文件解决方案来完成(基于优雅的答案被@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; 
} 

这是我与升压解决方案:

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

检查这个帖子:

Class implementation of C++ Enums

它含有类实现的C++枚举。

我想发布这个万一有人认为它有用。

就我而言,我只是需要从一个单一的.hpp文件生成一个C++ 11枚举ToString()FromString()功能。

我写了一个python脚本,用于解析包含枚举项的头文件,并在新的.cpp文件中生成函数。

您可以使用execute_process在CMakeLists.txt中添加此脚本,或者将其作为Visual Studio中的预生成事件。 .cpp文件将自动生成,无需在每次添加新的枚举项目时手动更新它。

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

运行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; 
    } 
} 

那么,还有一个选择。一个典型的用例是你需要HTTP动词的常量,以及使用字符串版本值。

的例子:

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

} 

动词类:

// ----------------------------------------------------------- 
// ----------------------------------------------------------- 
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 

使用化合物三元语句可以是用于与几个要素(单行)枚举几分优雅。表达式的长度也仅随元素的数量而近似线性增长。

这里有一个很好的用例:

enum log_level {INFO, WARNING, ERROR}; 
... 
void logger::write(const std::string log, const log_level l) { 
    ... 
    std::string s = (l == INFO) ? "INFO" : 
        (l == WARNING) ? "WARNING" : 
        (l == ERROR) ? "ERROR" : "UNKNOWN"; 
    ... 
} 
... 

当然,它只是一个开关/ if语句块,但它是一个单行的语句。作为简单与简单的问题,它在中间的某个地方遇到。作为一个常量表达式,它也可以很容易地变成内联函数。