Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this nested macro replacement failing?

I am trying to apply the X Macro concept, in order to have the possibility to initialize all struct members to a custom default (invalid) value. I write the following code:

#define LIST_OF_STRUCT_MEMBERS_foo \
    X(a) \
    X(b) \
    X(c)

#define X(name) int name;
struct foo {
     LIST_OF_STRUCT_MEMBERS_foo
};
#undef X


#define X(name) -1,
static inline void foo_invalidate(struct foo* in) {
     *in = (struct foo){
     LIST_OF_STRUCT_MEMBERS_foo
     };
}
#undef X

#define X(name) -1,
#define foo_DEFAULT_VALUE  { LIST_OF_STRUCT_MEMBERS_foo }
#undef X

static struct foo test = foo_DEFAULT_VALUE;

However, when I run the preprocessor, the definition of foo_DEFAULT_VALUE fails to substitute the X(name) calls with -1,

Preprocessor output:

struct foo {
     int a; int b; int c;
};

static inline void foo_invalidate(struct foo* in) {
     *in = (struct foo){
     -1, -1, -1, /*Here the substitution worked nicely*/
     };
}

static struct foo test = { X(a) X(b) X(c) }; /*Why this substitution failed?*/

I thought C-macros could refer to other macros. Do you know why that substitution fails? Is there any workaround?

I could live with foo_invalidate, but I am reluctant to give up one step from having a value to be used directly at initialization.

like image 877
Antonio Avatar asked Apr 12 '17 15:04

Antonio


People also ask

What is nested macro substitution in C?

A nested macro instruction definition is a macro instruction definition you can specify as a set of model statements in the body of an enclosing macro definition. This lets you create a macro definition by expanding the outer macro that contains the nested definition.

Are nested macros allowed in C?

The C preprocessor and macros are one of the most useful elements for writing portable C/C++ code . It even can be used to completely different purposes, like text processing. However, it suffers one major drawback: it does not support nested macros expansion (put differently, macros within macros).


2 Answers

Let's pretend that we are the preprocessor and encountering the line:

static struct foo test = foo_DEFAULT_VALUE;

Pass 1:

static struct foo test = { LIST_OF_STRUCT_MEMBERS_foo };

Pass 2:

static struct foo test = { X(a) X(b) X(c) };

Pass 3: Nothing to expand as X is undefined on this line.


One workaround could be defining a const variable (possibly but not necessarily static) to be used as default value:

#define X(name) -1,
static const struct foo foo_DEFAULT_VALUE = { LIST_OF_STRUCT_MEMBERS_foo };
#undef X

Which generates:

static const struct foo foo_DEFAULT_VALUE = { -1, -1, -1, };
like image 180
Eugene Sh. Avatar answered Nov 01 '22 19:11

Eugene Sh.


You might like the undef-free version of X_Macros,
it reduces the necessary care taken with defining and undefining around each use
and is better suited for definition in a header und useage in multiple code files:

#define LIST_OF_STRUCT_MEMBERS_foo(mode) \  
    X_##mode(a) \  
    X_##mode(b) \  
    X_##mode(c)  

#define X_struct(name) int name;  
#define X_list(name) -1,  
#define foo_DEFAULT_VALUE  { LIST_OF_STRUCT_MEMBERS_foo(list) }  

struct foo {  
     LIST_OF_STRUCT_MEMBERS_foo(struct)  
};  

static inline void foo_invalidate(struct foo* in) {  
     *in = (struct foo){  
     LIST_OF_STRUCT_MEMBERS_foo(list)  
     };  
}  

static struct foo test = foo_DEFAULT_VALUE;  

Output (gcc -E):

struct foo {
     int a; int b; int c;
};

static inline void foo_invalidate(struct foo* in) {
     *in = (struct foo){
     -1, -1, -1,
     };
}

static struct foo test = { -1, -1, -1, };
like image 34
Yunnosch Avatar answered Nov 01 '22 19:11

Yunnosch