Edu*_*ipe 118 c++ string scripting enums
假设我们有一些命名的枚举:
enum MyEnum {
FOO,
BAR = 0x50
};
Run Code Online (Sandbox Code Playgroud)
我搜索的是一个脚本(任何语言),它扫描我项目中的所有标题,并生成一个标题,每个枚举一个函数.
char* enum_to_string(MyEnum t);
Run Code Online (Sandbox Code Playgroud)
以及类似这样的实现:
char* enum_to_string(MyEnum t){
switch(t){
case FOO:
return "FOO";
case BAR:
return "BAR";
default:
return "INVALID ENUM";
}
}
Run Code Online (Sandbox Code Playgroud)
这个问题确实与typedefed枚举和未命名的C风格枚举有关.有人知道这个吗?
编辑:解决方案不应该修改我的源,除了生成的函数.枚举是在API中,因此使用迄今为止提出的解决方案不是一种选择.
Mar*_*iuk 73
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;
}
Run Code Online (Sandbox Code Playgroud)
colours.def:
X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)
Run Code Online (Sandbox Code Playgroud)
但是,我通常更喜欢以下方法,因此可以稍微调整字符串.
#define X(a, b) a,
#define X(a, b) b,
X(Red, "red")
X(Green, "green")
// etc.
Run Code Online (Sandbox Code Playgroud)
Avd*_*vdi 48
您可能想要查看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>
Run Code Online (Sandbox Code Playgroud)
您可以使用您喜欢的任何语言来提取Enumeration和EnumValue标记并生成所需的代码.
Jas*_*ers 42
@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)
};
Run Code Online (Sandbox Code Playgroud)
gbj*_*anb 33
我倾向于创建一个C数组,其名称与枚举值的顺序和位置相同.
例如.
enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };
Run Code Online (Sandbox Code Playgroud)
然后你可以在你想要人类可读值的地方使用数组,例如
colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];
Run Code Online (Sandbox Code Playgroud)
您可以尝试使用字符串化运算符(请参阅预处理器参考中的#),在某些情况下可以执行您想要的操作 - 例如:
#define printword(XX) cout << #XX;
printword(red);
Run Code Online (Sandbox Code Playgroud)
将打印"红色"到标准输出.不幸的是,它不适用于变量(因为你将打印出变量名称)
小智 10
我有一个非常简单易用的宏,以完全干燥的方式做到这一点.它涉及可变参数宏和一些简单的解析魔法.开始:
#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;}
Run Code Online (Sandbox Code Playgroud)
要在代码中使用它,只需执行以下操作:
AWESOME_MAKE_ENUM(Animal,
DOG,
CAT,
HORSE
);
Run Code Online (Sandbox Code Playgroud)
我今天刚刚重新发明了这个轮子,并且认为我会分享它.
此实现执行不要求对定义的常量,它可以是枚举或代码进行任何更改#defineS或其他任何转予整数-在我的情况,我在其他符号的定义的符号.它也适用于稀疏值.它甚至允许多个名称用于相同的值,始终返回第一个名称.唯一的缺点是它需要你创建一个常量表,例如,当添加新的常量时,它可能会变得过时.
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]);
}
Run Code Online (Sandbox Code Playgroud)
您如何使用它的一个示例:
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);
Run Code Online (Sandbox Code Playgroud)
该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;
}
}
Run Code Online (Sandbox Code Playgroud)
这可以在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;
}
Run Code Online (Sandbox Code Playgroud)
#define stringify( name ) # name
enum MyEnum {
ENUMVAL1
};
...stuff...
stringify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1
Run Code Online (Sandbox Code Playgroud)
虽然有点晚了,但我真的很喜欢这种模式,因为它可以让您避免复制粘贴错误,并且如果枚举没有映射到字符串,它会直接无法编译。它还具有非常友好的优点,constexpr因此内联得非常好。它还不需要中间类、switch 语句或运行时值。
// Create a mapping between the enum value and the string
#define MY_ENUM_LIST(DECLARE) \
DECLARE(foo, "This is a foo!") \
DECLARE(bar, "This is a bar!") \
DECLARE(bam, "This is a bam!")
// Define the enum officially
enum class MyEnum {
#define ENUM_ENTRY(NAME, TEXT) NAME, // TEXT expressly not used here
MY_ENUM_LIST(ENUM_ENTRY)
#undef ENUM_ENTRY // Always undef as a good citizen ;)
};
// Create a template function that would fail to compile if called
template <MyEnum KEY> constexpr const char* MyEnumText() {}
// Specialize that bad function with versions that map the enum value to the string declared above
#define ENUM_FUNC(NAME, TEXT) template <> constexpr const char* MyEnumText<MyEnum::NAME>() { return TEXT; }
MY_ENUM_LIST(ENUM_FUNC)
#undef ENUM_FUNC
Run Code Online (Sandbox Code Playgroud)
您使用它的方式非常简单。如果您始终在需要字符串的站点上对枚举值进行硬编码,则只需调用以下的专用版本MyEnumText:
const auto text{::MyEnumText<MyEnum::foo>()}; // inlines beautifully
Run Code Online (Sandbox Code Playgroud)
如果您需要处理动态枚举值,您可以添加此附加帮助程序:
constexpr const char* MyEnumText(MyEnum key) {
switch (key) {
#define ENUM_CASE(NAME, TEXT) case MyEnum::NAME: return MyEnumText<MyEnum::NAME>();
MY_ENUM_LIST(ENUM_CASE)
#undef ENUM_CASE
}
return nullptr;
}
Run Code Online (Sandbox Code Playgroud)
其调用方式与模板特化类似:
const auto text{::MyEnumText(MyEnum::foo)}; // inlines beautifully
Run Code Online (Sandbox Code Playgroud)
或者
const MyEnum e{GetTheEnumValue()};
const auto text{::MyEnumText(e)};
Run Code Online (Sandbox Code Playgroud)
有趣的是看多少种方法.这是我很久以前用过的一个:
在文件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(){};
};
Run Code Online (Sandbox Code Playgroud)
在main.cpp中
#include "myenummap.h"
...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;
Run Code Online (Sandbox Code Playgroud)
它不是常数,但它方便.
这是使用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;
}
Run Code Online (Sandbox Code Playgroud)
这是一个单文件解决方案(基于@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;
}
Run Code Online (Sandbox Code Playgroud)