Проверьте наличие записи в списке препроцессора 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
Другие вопросы по тегам