有没有简单的方法将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中,因此使用到目前为止提出的解决方案不是一种选择。
你可能想看看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标签并生成所需的代码的任何语言。
非常棒!作为一个简单的Python脚本的魅力。谢谢。 – 2008-10-14 19:43:00
+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的一个问题是枚举二进制值不一定从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.
漂亮, 虽然我不喜欢额外的文件 – 2008-10-14 18:02:57
+1,但可能会混淆Intellisense和类似的东西...... – 2008-10-14 18:26:02
只要确保您的构建过程不会在每个包含之前预先包含#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
似乎像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代码来自定义。这种方法在您的应用程序中可能会或可能不会实用,但应该记住这一点。
我这样做有其与宏生成单独的并排的侧枚举包装类。有几个优点:
- 可以生成它们枚举我不定义(如:操作系统平台头枚举)
- 可以将检查范围为包装类
- 可以做“聪明”与格式位字段枚举
当然,缺点是我需要在格式化程序类中复制枚举值,并且我没有任何脚本来生成它们。除此之外,它似乎工作得很好。
下面是从我的代码库,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
讨论主题就可以了:
运行与“--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
不久前,我做了一些技巧,有枚举正确显示在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的对象不可能是模板
作为变例,使用简单的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);
我想发布这个万一有人认为它有用。
就我而言,我只是需要从一个单一的.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语句块,但它是一个单行的语句。作为简单与简单的问题,它在中间的某个地方遇到。作为一个常量表达式,它也可以很容易地变成内联函数。
可能的重复[简单的方法来使用枚举类型的变量作为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
关于基于宏的工厂移动到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