Is it possible to check for the existence of an entry in a list defined by X-macro? Given the example code below, I'd like the #if defined(GEORGE)
condition to be true.
EDIT: without doing an explicit #define GEORGE
, of course. I am hoping to have a way to check an entry in the list (in the pre-processor) and I only want to do the declaration in the list. It is likely not possible but thought I ask.
Thanks!
#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;
}
Output
John Adams
George Washington
Abraham Lincoln
names[ABRAHAM] = Abraham Lincoln
You can try to use a preprocessor pattern matcher. The concept simply requires a SECOND
macro with indirection; GLUE
is nice to shove arbitrary prefixes on:
#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
...and for an X macro "column" that contains only "pseudo-identifiers":
#define NAMES \
X( JOHN, "John Adams" ) \
X( GEORGE, "George Washington" ) \
X( ABRAHAM, "Abraham Lincoln ")
...you could do this:
#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
This works because SECOND
indirectly expands to the second argument; so SECOND(GLUE(SEARCH_FOR,ID_),+0)
by default will expand to +0
; and any chain of +0
is a valid false expression. But because the expansion's indirect, and the first argument is that pasted token, then if you define the pasted token SEARCH_FOR_GEORGE
to itself expand with a comma in it, the expression after the comma becomes the new second argument. (Ask me if you need this to work on Microsoft VS preprocessors; that requires a tiny tweak).