Is there any way in standard C—or with GNU extensions—to append stuff to a macro definition? E.g., given a macro defined as
#define List foo bar
can I append bas
so that it List
expands as if I’d defined it
#define List foo bar bas
?
I was hoping I could do something like this:
#define List foo bar bas
#define List_ Expand(List)
#undef List
#define List Expand(List_) quux
but I can’t figure out how to define the Expand()
macro so it’ll do what I want.
Motivation:
I’m playing with discriminated/tagged unions along these lines:
struct quux_foo { int x; };
struct quux_bar { char *s; };
struct quux_bas { void *p; };
enum quux_type {quux_foo, quux_bar, quux_bas};
struct quux {
enum quux_type type;
union {
struct quux_foo foo;
struct quux_bar bar;
struct quux_bas bas;
} t;
};
I figure this is a good place for the X-macro. If I define a macro
#define quux_table X(foo) X(bar) X(bas)
the enumeration & structure can be defined thus, and never get out of sync:
#define X(t) quux_ ## t,
enum quux_type {quux_table};
#undef X
#define X(t) struct quux_ ## t t;
struct quux {
enum quux_type type;
union {quux_table} t;
};
#undef X
Of course, the quux_*
structures can get out of sync, so I’d like to do something like this, only legally:
struct quux_foo { int x; };
#define quux_table quux_table X(foo)
struct quux_bar { char *s; };
#define quux_table quux_table X(bar)
struct quux_bas { void *p; };
#define quux_table quux_table X(bas)
(Well, what I really want to be able to do is something like
member_struct(quux, foo) { int x; };
but I’m well aware that macros cannot be (re)defined from within macros.)
Anyhow, that’s my motivating example. Is there a way to accomplish this?
Boost.Preprocessor examples are fine, if you can show me how to make the X-macro technique work with that library.
Effectively, no.
Macros are lazily evaluated. When you #define List_ Expand(List)
, its replacement list is the sequence of four tokens Expand
, (
, List
, and )
. There isn't any way to expand a macro into a replacement list.
All macro replacement takes place when a macro is invoked.
I'd recommend looking at using the Boost.Preprocessor library for automatic code generation. It's a bit of work, but you can accomplish some fairly impressive things using it. It should be fully compatible with C.
There is a way!
Using the new _Pragma keyword this can be achieved in gcc (though not with msvc)
If you pop a macro within it's own definition it will delay it's expansion until the macro is expanded for the first time. This allows you to make it's previous expansion part of it's own definition. However, since it is popped during it's expansion, it can only be used once
Here is some sample code to see it in action
#define pushfoo _Pragma("push_macro(\"foo\")") //for convenience
#define popfoo _Pragma("pop_macro(\"foo\")")
#define foo 1
pushfoo //push the old value
#undef foo //so you don't get a warning on the next line
#define foo popfoo foo , 2 //append to the previous value of foo
pushfoo
#undef foo
#define foo popfoo foo , 3
pushfoo
#undef foo
#define foo popfoo foo , 4
foo //this whole list will expand to something like popfoo foo popfoo foo popfoo foo , 4
//which will in turn expand to 1 , 2 , 3 , 4
foo //the second time this will expand to just 1
This option should make automatic code generation a fair bit easier, though unfortunately only on gcc (maybe clang, haven't tested)
To be honest there is no reason I can find why this must work, it is most probably undefined behavior that happens to work. I'm guessing the reason is that after popping foo, the current macro being expanded is no longer associated with the name foo which allows the symbol foo
to be expanded, but that is only my conjecture
Edit:
After testing on clang, this does not does work on clang.
I don't know why I thought clang did not work, maybe it didn't on a different machine. I definitely did get it to work with the code given though
I'm not sure if this helps, but you can do vari arg macros. Mr. Conrad of the x264 project loves preprocessor abuse. If they sound like they might help you can find out more Here