2017-02-08 7 views
1

Это расширение следующего 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. Поэтому вы могли бы что-то придумать с шаблонами.


Основываясь на Томаса 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"; 
} 

ответ

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 одно из направлений преобразования не очень эффективно.

+0

Эй, это замечательно. Я придумал что-то, основанное на этой идее. – Matt

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]; 
} 

Если вы хотите, чтобы избежать повторения списка констант перечислений, которые были бы пригодны для Х макро лечения:

#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 макро версия спорно; конечно, было бы большим преимуществом, если бы количество элементов перечисления было больше.

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*"; 
} 
+0

Проблема с этим заключается в том, что я буду вмешиваться или переопределять существующий заголовок. Это большая работа, когда заголовок уже очень большой. Перечисление уже определено в заголовочном файле SDK, поэтому я получаю ошибки сразу с места в карьер. – Matt

+0

Извините за случайное редактирование. Но спасибо за ответ. Это может оказаться полезным для кого-то. – Matt

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; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^