2011-12-28 4 views
8

У меня есть этот макрокоманд, который позволяет мне определить как перечисление C, так и список перечисляемых имен в виде строк с использованием одной конструкции. Это мешает мне дублировать имена перечислителей (и, возможно, вводить ошибки для больших списков).Подсчет макросов препроцессора

#define ENUM_DEFINITIONS(F) \ 
    F(0, Item1) \ 
    F(5, Item2) \ 
    F(15, Item3) \ 
    ... 
    F(63, ItemN) 

затем:

enum Items { 
    #define ITEM_ENUM_DEFINE(id, name) name = id, 
    ENUM_DEFINITIONS(ITEM_ENUM_DEFINE) 
    #undef ITEM_ENUM_DEFINE 

, который при расширении, следует производить:

enum Items { 
    Item1 = 0, 
    Item2 = 5, 
    Item3 = 15, 
    ... 
    ItemN = 63, 
} 

В файле реализации, у меня есть этот код:

const char* itemNames[TOTAL_ITEMS]; 
int iter = 0; 

#define ITEM_STRING_DEFINE(id, name) itemNames[iter++] = #name; 
    ENUM_DEFINITIONS(ITEM_STRING_DEFINE) 
#undef ITEM_STRING_DEFINE 

, который, при расширении производит:

itemNames[iter++] = "Item1"; 
itemNames[iter++] = "Item2"; 
itemNames[iter++] = "Item3"; 
... 
itemNames[iter++] = "ItemN"; 

Я хотел бы знать, сколько переписчик пункты, которые я создал в этой моде и быть в состоянии передать его во время компиляции массивов. В приведенном выше примере это будет определять, что TOTAL_ITEMS = N во время компиляции. Можно ли таким образом подсчитать макрокоманды?

Я видел упоминание о нестандартного COUNTER макро, похожего на ФАЙЛ и ЛИНИИ макросов, но я надеюсь, что есть более стандартный способ.

Был бы также заинтересован в слушании, если есть лучший способ достичь этого, не используя макросы.

+0

Зачем вам нужны как 'enum', так и массив строк? – Philip

+0

Кажется, это очень часто встречается в моей работе. Примером может служить конечный автомат, в котором перечислены состояния, а интерфейсный интерфейс GUI отображает имя текущего состояния. Его удобно определять только один раз и облегчает для других разработчиков, желающих добавить новые состояния. – Eyal

ответ

7

должно работать:

#define ITEM_STRING_DEFINE(id, name) #name, // note trailing comma 
const char *itemNames[] = { 
    ENUM_DEFINITIONS(ITEM_STRING_DEFINE) 
}; 

#define TOTAL_ITEMS (sizeof itemNames/sizeof itemNames[0]) 

Редактировать: Спасибо Raymond Chen за отметить, что мы не должны беспокоиться о ненужной заключительной запятой в списке. (Я неправильно искал проблему для перечислений со строгими компиляторами C89, как в Is the last comma in C enum required?.)

+1

Вам не нужно есть конечную запятую. Задняя запятая легальна и игнорируется. –

+0

Что касается последней ненужной запятой, я, похоже, помню, что компилятор SGI Irix выпустил бы непередаваемое предупреждение об этом, что заставило меня всегда опускать его. –

2

Также был бы заинтересован услышать, есть ли лучший способ достичь этого, не используя макросы.

Вы всегда можете использовать язык сценариев, такой как ruby ​​или python, для создания файлов .c и .h для вас. Если вы сделаете это хорошо, вы можете интегрировать свой скрипт в свой Makefile.

+1

Мне вообще не нравятся подходы, которые добавляют еще один язык к требованиям сборки. – Alex

1

Я знаю, что это не полный ответ. Вы можете создать макрос вокруг чего-то вроде этого.

#include <stdio.h> 

const char * array[] = { 
    "arr1", "arr2", "arr3", "arr4" 
}; 

int main (int argc, char **argv)$ 
{ 
    printf("%d\n", sizeof(array)/sizeof(const char *)); 
} 

Если вы можете изменить ваше перечисление, так что имеют непрерывные элементы, которые вы можете сделать это как-н (от Boost)

enum { A=0,B,C,D,E,F,N }; 
const char arr[N]; // can contain a character for each enum value 
4

Вы можете использовать тот же метод для подсчета вызовов.

enum itemscounter { 
    #define ITEM_ENUM_DEFINE(id, name) name##counter, 
    ENUM_DEFINITIONS(ITEM_ENUM_DEFINE) 
    #undef ITEM_ENUM_DEFINE 
TOTAL_ITEMS 
}; 
+1

Я принял вышеупомянутый ответ, но думаю, что это очень хороший (и умный) подход. Благодаря! – Eyal

0

Просмотреть рекомендации Mu Dynamics 'Enums, Strings and Laziness'; они, по крайней мере, связаны с тем, что вам нужно.

В противном случае просмотрите коллекцию Boost Preprocessor (которая может использоваться с препроцессором C, а также препроцессором C++).

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