Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I append to a preprocessor macro?

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.

like image 707
J. C. Salomon Avatar asked Dec 28 '10 22:12

J. C. Salomon


1 Answers

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

like image 58
rtpax Avatar answered Oct 08 '22 17:10

rtpax