10

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.

Community
  • 1
  • 1
Antonio
  • 19,451
  • 13
  • 99
  • 197
  • 2
    You have `X(name)` defined to `-1,` around the `#define` for `foo_DEFAULT_VALUE`, but not where you actually *use* it. You need the `X` macro defined where the substitution occurs, around the `static struct foo test = foo_DEFAULT_VALUE;` line. – Dmitri Apr 12 '17 at 15:16

3 Answers3

10

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, };
Antonio
  • 19,451
  • 13
  • 99
  • 197
Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61
  • What could be a workaround? I have thought about the following, but I wonder if it is optimal: `#define X(name) -1, #define foo_DEFAULT_VALUE_GENERATOR { LIST_OF_STRUCT_MEMBERS_foo } static const struct foo_DEFAULT_VALUE = foo_DEFAULT_VALUE_GENERATOR; #undef X` – Antonio Apr 12 '17 at 15:22
  • Well, the straight forward way would be apparently that. Move the `#undef` after the line with the declaration. Otherwise a redesign would be needed. – Eugene Sh. Apr 12 '17 at 15:24
  • 1
    @Antonio `X` doesn't even need to be defined where `foo_DEFAULT_VALUE_GENERATOR` is defined, as long as it's defined where it's used. – Dmitri Apr 12 '17 at 15:28
  • 2
    Instead of defining `foo_DEFAULT_VALUE` as a macro expanding to an initializer, you could declare it as a `const struct foo` *with* initializer. – John Bollinger Apr 12 '17 at 15:28
  • @JohnBollinger Yep, does my edit match with what you said? – Antonio Apr 12 '17 at 15:31
  • 1
    Yes, @Antonio, though personally, I wouldn't bother with the `foo_DEFAULT_VALUE_GENERATOR` macro, either, since you won't be able to use it later. I'd just put its replacement text directly into the declaration of `foo_DEFAULT_VALUE`. – John Bollinger Apr 12 '17 at 15:34
  • @EugeneSh. Excuse me, I have removed the code in the second part of the answer because it does not work for the same reason the code in the question doesn't work – Antonio Apr 12 '17 at 15:57
  • @Antonio Ow.. right. I've confused myself, sorry :) Should always test it. – Eugene Sh. Apr 12 '17 at 15:58
4

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, };
Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • 1
    Would you believe me, if I say I invented it? I do not mean I invented it *first* (there could be others, earlier), but I did come up with it by myself. – Yunnosch Apr 13 '17 at 12:58
  • I do believe you, I searched the subject a lot yesterday, and never came across to such an implementation! I will award you a bounty as soon as it is possible! – Antonio Apr 13 '17 at 13:01
  • Hello everybody, if you know other sources for this method, please let me know. If nobody finds anything I could claim to actually have invented it **first**. ;-) – Yunnosch Apr 19 '17 at 20:16
-1

You might also try to check the output of expanded macros. If you are using gcc as a compiler, gcc -E <filename> >> full_src.txt' shall help. More details are here: Seeing expanded C macros

Community
  • 1
  • 1
vivekn
  • 35
  • 6