如何自动将枚举转换为字符串

时间:2017-02-08 20:54:10

标签: c++ c enums

这是以下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";
} 

4 个答案:

答案 0 :(得分:3)

您无法将enum自动转换为enum 文本或文本,因为enum标识符仅在编译期间存在,而不是在运行时期间存在。

最好的方法是查表或std::map

我使用表查找,因为我可以创建表staticconst并在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值和文本,可以使用一个表将文本转换为enumenum为文本。
使用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;
}