这是以下question的扩展,已经有很好的答案。
但是,在预处理器执行扩展的所有答案中,枚举在同一项目下以特殊方式声明。 但是,如果声明这些枚举的头文件不是您自己的,或者枚举是按id顺序声明的。
在我的情况下,我有一个我正在使用的SDK,其中大型枚举表已在单独的头文件中声明,如下所示:
#define NODE_GRAPH_TYPE(_name, _enum) _name = _enum,
enum NodeGraphType
{
// next available ID value is 7
NODE_GRAPH_TYPE(GT_UNKNOWN , 0)
NODE_GRAPH_TYPE(GT_STANDARD , 1)
NODE_GRAPH_TYPE(GT_APP_SETTINGS , 3)
NODE_GRAPH_TYPE(GT_GEOMETRYARCHIVE , 2)
NODE_GRAPH_TYPE(GT_SCRIPTED , 5)
NODE_GRAPH_TYPE(GT_SCRIPT_STORAGE , 6)
NODE_GRAPH_TYPE(GT_PROJECT_SETTINGS , 4)
};
我想要做的是尽量避免这种事情:
function NODE_GRAPH_NAME(int type)
{
switch(type)
{
case: GT_PROJECT_SETTINGS:
return "GT_PROJECT_SETTINGS";
// ...
};
}
或
static const char* NODE_GRAPH_NAME[] = {
"GT_UNKNOWN",
/// ...
}
到目前为止,我已经提出了最好的建议:
#define STRINGIFY(_x) #_x
static const char* NODE_GRAPH_NAME[] = {
STRINGIFY(GT_UNKNOWN),
STRINGIFY(GT_STANDARD),
STRINGIFY(GT_GEOMETRYARCHIVE),
STRINGIFY(GT_APP_SETTINGS)
// etc
};
但是它仍然要求我复制所有枚举表而不仅仅是那些,但是它们按顺序排列并仔细检查没有漏洞。
是否有更优雅和自动化的方式来解决这个问题? 如果它有帮助,我实际上是在C ++下编译它。但是我链接的库是纯粹的C.所以你可以用模板做一些奇特的事情。
根据Thomas Matthews的回答,这就是我提出的以下内容:
这不是完全自动的,但是不太难以使用。 在重新定义NODE_GRAPH_TYPE时,我可以从SDK标头中逐字复制表,并将其放入NODE_GRAPH_TYPES表定义中。 它有效。然后我可以创建一个查找函数来定位我需要的项目。关于这一点的好处是漏洞无关紧要,也没有乱序定义。
#undef NODE_GRAPH_TYPE
#define NODE_GRAPH_TYPE(_name, _enum) { _enum, #_name },
static const Enum_Entry NODE_GRAPH_TYPES[] =
{
NODE_GRAPH_TYPE(GT_UNKNOWN , 0)
NODE_GRAPH_TYPE(GT_STANDARD , 1)
NODE_GRAPH_TYPE(GT_APP_SETTINGS , 3)
NODE_GRAPH_TYPE(GT_GEOMETRYARCHIVE , 2)
NODE_GRAPH_TYPE(GT_SCRIPTED , 5)
NODE_GRAPH_TYPE(GT_SCRIPT_STORAGE , 6)
NODE_GRAPH_TYPE(GT_PROJECT_SETTINGS , 4)
};
static const unsigned int NODE_GRAPH_TYPES_COUNT =
sizeof(NODE_GRAPH_TYPES) / sizeof(NODE_GRAPH_TYPES[0]);
const char* NODE_GRAPH_TYPE_NAME(int id)
{
for (int i = 0; i < NODE_GRAPH_TYPES_COUNT; ++i){
if (PIN_TYPES[i].enum_value == id)
return PIN_TYPES[i].enum_text;
}
return "Unknown";
}
答案 0 :(得分:3)
您无法将enum
自动转换为enum
文本或文本,因为enum
标识符仅在编译期间存在,而不是在运行时期间存在。
最好的方法是查表或std::map
。
我使用表查找,因为我可以创建表static
和const
并在main
之前初始化它。
struct Enum_Entry
{
int enum_value;
const char * enum_text;
};
static const Enum_Entry enum_table[] =
{
{GT_UNKNOWN, "GT_UNKNOWN"},
//...
};
static const unsigned int table_capacity =
sizeof(enum_table) / sizeof(enum_table[0]);
通过同时使用enum
值和文本,可以使用一个表将文本转换为enum
和enum
为文本。
使用std::map
时,其中一个转换方向效率不高。
答案 1 :(得分:2)
至少在C中,你可以在你提出的解决方案中使用一个变量,它不需要你的枚举值没有空洞或按顺序表达。 C指定了初始值设定项,您可以通过它初始化数组的指定元素。即:
#define STRINGIFY(x) #x
enum c { X, Y = 3, Z = 1 };
static const char * E_NAME[] = {
[X] = STRINGIFY(X),
[Y] = STRINGIFY(Y),
[Z] = STRINGIFY(Z)
};
const char *e_name(enum c x) {
return E_NAME[x];
}
如果你想避免重复枚举常量列表,那么这将适合X宏处理:
#define STRINGIFY(x) #x
#define ENUM_ELEMENTS E(X,), E(Y,=3), E(Z,=1)
#define E(c, i) c i
enum c {
ENUM_ELEMENTS
};
#undef E
#define E(c, i) [c] = STRINGIFY(c)
static const char *E_NAME[] = {
ENUM_ELEMENTS
};
#undef E
const char *e_name(enum c x) {
return E_NAME[x];
}
X宏版本是否有帮助是值得商榷的;当然,如果枚举元素的数量更多,那将更有利。
答案 2 :(得分:1)
您可以通过以下方式构建:
#define NODEGRAPHTYPES \
NODE_GRAPH_TYPE(GT_UNKNOWN , 0)\
NODE_GRAPH_TYPE(GT_STANDARD , 1)\
NODE_GRAPH_TYPE(GT_APP_SETTINGS , 3)\
NODE_GRAPH_TYPE(GT_GEOMETRYARCHIVE , 2)\
NODE_GRAPH_TYPE(GT_SCRIPTED , 5)\
NODE_GRAPH_TYPE(GT_SCRIPT_STORAGE , 6)\
NODE_GRAPH_TYPE(GT_PROJECT_SETTINGS , 4)
然后你可以在定义枚举时重新使用它,并在定义枚举到文本转换时如下:
enum NodeGraphType {
#define NODE_GRAPH_TYPE(NAME, VALUE) NAME = VALUE,
NODEGRAPHTYPES
#undef NODE_GRAPH_TYPE
};
static const char *NodeGraphName(enum NodeGraphType type)
{
switch (type) {
#define NODE_GRAPH_TYPE(NAME, VALUE) case NAME: return #NAME;
NODEGRAPHTYPES
#undef NODE_GRAPH_TYPE
}
return "*Unknown*";
}
答案 3 :(得分:0)
您可以利用现有的头文件生成类似X-macro的文件。根据您提供的示例生成一个包含NODE_GRAPH_TYPE
行的中间文件相对容易。
grep '^ *NODE_GRAPH_TYPE' node_graph.h > node_graphs.x
然后,您可以创建一个包含此中间文件的文件,其中包含您自己的NODE_GRAPH_TYPE
定义,以便按您希望的方式定义表格。下面说明了第一个示例中的函数调用。
static const char * NODE_GRAPH_NAME(enum NodeGraphType)
{
switch(type)
{
# define NODE_GRAPH_TYPE(x, y) case x: return #x;
# include "node_graphs.x"
# undef NODE_GRAPH_TYPE
default: break;
}
return NULL;
}