Проверьте наличие записи в списке препроцессора C
Можно ли проверить наличие записи в списке, определенном X-макросом? Учитывая приведенный ниже пример кода, я хотел бы #if defined(GEORGE)
условие, чтобы быть правдой.
РЕДАКТИРОВАТЬ: не делая явного #define GEORGE
, конечно. Я надеюсь, что есть способ проверить запись в списке (в препроцессоре), и я хочу только сделать объявление в списке. Это, вероятно, не возможно, но думал, что я спрашиваю.
Спасибо!
#include <stdio.h>
#define NAMES \
X( JOHN, "John Adams" ) \
X( GEORGE, "George Washington" ) \
X( ABRAHAM, "Abraham Lincoln ")
#define X(_id, _name) _id,
typedef enum {
NAMES
} names_e;
#undef X
typedef struct {
char *name;
} names_t;
#define X(_id, _name) [_id] = { .name = _name },
static names_t const names[] = {
NAMES
};
#undef X
int main(void) {
int i;
for (i=0; i < sizeof(names)/sizeof(names[0]); i++) {
printf("%s\n", names[i].name);
}
printf("names[ABRAHAM] = %s\n", names[ABRAHAM].name);
#if defined(GEORGE)
printf("names[GEORGE] = %s\n", names[GEORGE].name);
#endif
return 0;
}
Выход
John Adams
George Washington
Abraham Lincoln
names[ABRAHAM] = Abraham Lincoln
2 ответа
Вы можете попробовать использовать сопоставитель шаблонов препроцессора. Концепция просто требует SECOND
макрос с косвенностью; GLUE
Приятно пихать произвольные префиксы на:
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
... и для макроса "столбец" X, который содержит только "псевдоидентификаторы":
#define NAMES \
X( JOHN, "John Adams" ) \
X( GEORGE, "George Washington" ) \
X( ABRAHAM, "Abraham Lincoln ")
... вы могли бы сделать это:
#define X(ID_, NAME_) SECOND(GLUE(SEARCH_FOR_,ID_),+0)
#define SEARCH_FOR_GEORGE ,+1
#if NAMES
printf("names[GEORGE] = %s\n", names[GEORGE].name);
#endif
#undef SEARCH_FOR_GEORGE
#define SEARCH_FOR_THOMAS ,+1
#if NAMES
#error Dewey wins!
#endif
#undef SEARCH_FOR_THOMAS
#undef X
Это работает, потому что SECOND
косвенно распространяется на второй аргумент; так SECOND(GLUE(SEARCH_FOR,ID_),+0)
по умолчанию расширится до +0
; и любая цепочка +0
является допустимым ложным выражением. Но поскольку расширение является косвенным, и первый аргумент - это вставленный токен, то если вы определите вставленный токен SEARCH_FOR_GEORGE
для самого себя с запятой, выражение после запятой становится новым вторым аргументом. (Спросите меня, нужно ли это для работы на препроцессорах Microsoft VS; для этого требуется небольшая настройка).
К сожалению, вы не можете писать макросы внутри тела макроса, поэтому будет сложно управлять им с помощью набросков, которые у вас есть (я не вижу способа добиться этого).
Вы можете тестировать макросы только в препроцессоре (не имена элементов перечисления), и вы не можете писать макросы через препроцессор (a #define
не может с пользой генерировать новый #define
основываясь на его аргументах), так что вы должны сделать какую-то тяжелую работу.
Если вы определите
#define P_JOHN 0
#define P_GEORGE 1
#define P_ABRAHAM 2
тогда вы можете использовать:
#define NAMES \
X( JOHN, "John Adams" ) \
X( GEORGE, "George Washington" ) \
X( ABRAHAM, "Abraham Lincoln ")
#define X(_id, _name) _id = P_ ## _id, // Crucial change
typedef enum
{
NAMES
} names_e;
#undef X
typedef struct
{
char *name;
} names_t;
#define X(_id, _name) [_id] = { .name = _name },
static names_t const names[] =
{
NAMES
};
#undef X
int main(void) {
int i;
for (i=0; i < sizeof(names)/sizeof(names[0]); i++) {
printf("%s\n", names[i].name);
}
printf("names[ABRAHAM] = %s\n", names[ABRAHAM].name);
#if defined(P_GEORGE) // Crucial change
printf("names[GEORGE] = %s\n", names[GEORGE].name);
#endif
return 0;
}
Обратите внимание, что ответственность за то, чтобы P_*
имена имеют уникальные номера (и я не уверен, что вы собираетесь делать с Джорджем Бушем-младшим и Джорджем Бушем-младшим - там, похоже, конфликт с Джорджем Вашингтоном).
Пример вывода:
John Adams
George Washington
Abraham Lincoln
names[ABRAHAM] = Abraham Lincoln
names[GEORGE] = George Washington