有没有一种简单的方法可以将 C++ 枚举转换为字符串?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/201593/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a simple way to convert C++ enum to string?
提问by Edu Felipe
Suppose we have some named enums:
假设我们有一些命名的枚举:
enum MyEnum {
FOO,
BAR = 0x50
};
What I googled for is a script (any language) that scans all the headers in my project and generates a header with one function per enum.
我用谷歌搜索的是一个脚本(任何语言),它扫描我项目中的所有标题并生成一个每个枚举一个函数的标题。
char* enum_to_string(MyEnum t);
And a implementation with something like this:
和这样的实现:
char* enum_to_string(MyEnum t){
switch(t){
case FOO:
return "FOO";
case BAR:
return "BAR";
default:
return "INVALID ENUM";
}
}
The gotcha is really with typedefed enums, and unnamed C style enums. Does anybody know something for this?
问题在于 typedefed 枚举和未命名的 C 风格枚举。有人知道这件事吗?
EDIT: The solution should not modify my source, except for the generated functions. The enums are in an API, so using the solutions proposed until now is just not an option.
编辑:该解决方案不应修改我的源代码,生成的函数除外。枚举位于 API 中,因此使用迄今为止提出的解决方案并不是一种选择。
采纳答案by Avdi
You may want to check out GCCXML.
您可能想查看GCCXML。
Running GCCXML on your sample code produces:
在您的示例代码上运行 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>
You could use any language you prefer to pull out the Enumeration and EnumValue tags and generate your desired code.
您可以使用您喜欢的任何语言来提取 Enumeration 和 EnumValue 标签并生成您想要的代码。
回答by Marcin Koziuk
X-macros are the best solution. Example:
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:
颜色.def:
X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)
However, I usually prefer the following method, so that it's possible to tweak the string a bit.
但是,我通常更喜欢以下方法,以便可以稍微调整字符串。
#define X(a, b) a,
#define X(a, b) b,
X(Red, "red")
X(Green, "green")
// etc.
回答by Jasper Bekkers
@hydroo: Without the extra file:
@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)
};
回答by gbjbaanb
What I tend to do is create a C array with the names in the same order and position as the enum values.
我倾向于做的是创建一个 C 数组,其名称的顺序和位置与枚举值相同。
eg.
例如。
enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };
then you can use the array in places where you want a human-readable value, eg
然后您可以在需要人类可读值的地方使用该数组,例如
colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];
You could experiment a little with the stringizing operator (see # in your preprocessor reference) that will do what you want, in some circumstances- eg:
在某些情况下,您可以尝试使用字符串化运算符(请参阅预处理器参考中的 # ),它可以满足您的要求 - 例如:
#define printword(XX) cout << #XX;
printword(red);
will print "red" to stdout. Unfortunately it won't work for a variable (as you'll get the variable name printed out)
将“红色”打印到标准输出。不幸的是,它不适用于变量(因为您会打印出变量名称)
回答by Debdatta Basu
I have an incredibly simple to use macro that does this in a completely DRY fashion. It involves variadic macros and some simple parsing magic. Here goes:
我有一个非常简单易用的宏,它以完全 DRY 的方式执行此操作。它涉及可变参数宏和一些简单的解析魔法。开始:
#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;}
To use this in your code, simply do:
要在您的代码中使用它,只需执行以下操作:
AWESOME_MAKE_ENUM(Animal,
DOG,
CAT,
HORSE
);
回答by Ronny Brendel
回答by serge
This can be done in C++11
这可以在 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;
}
回答by Mark Ransom
I just re-invented this wheel today, and thought I'd share it.
我今天刚刚重新发明了这个轮子,并认为我会分享它。
This implementation does notrequire any changes to the code that defines the constants, which can be enumerations or #define
s or anything else that devolves to an integer - in my case I had symbols defined in terms of other symbols. It also works well with sparse values. It even allows multiple names for the same value, returning the first one always. The only downside is that it requires you to make a table of the constants, which might become out-of-date as new ones are added for example.
此实现执行不要求对定义的常量,它可以是枚举或代码进行任何更改#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]);
}
An example of how you'd use it:
您将如何使用它的示例:
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);
The IdToName
function relies on std::lower_bound
to do quick lookups, which requires the table to be sorted. If the first two entries in the table are out of order, the function will sort it automatically.
该IdToName
函数依赖于std::lower_bound
进行快速查找,这需要对表进行排序。如果表中的前两个条目乱序,该函数将自动对其进行排序。
Edit: A comment made me think of another way of using the same principle. A macro simplifies the generation of a big switch
statement.
编辑:评论让我想到了使用相同原理的另一种方式。宏简化了大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;
}
}
回答by Ben
#define stringify( name ) # name
enum MyEnum {
ENUMVAL1
};
...stuff...
stringify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1
Further discussion on this method
回答by Carl
Interesting to see the number of ways. here's one i used a long time ago:
有趣的看到方式的数量。这是我很久以前用过的一个:
in file myenummap.h:
在文件 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(){};
};
in main.cpp
在 main.cpp 中
#include "myenummap.h"
...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;
Its not const, but its convenient.
它不是常量,但它很方便。
Here's another way that uses C++11 features. This is const, doesn't inherit an STL container and is a little tidier:
这是使用 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;
}