Can I append to a preprocessor macro?
Asked Answered
H

3

14

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.

Homoiousian answered 28/12, 2010 at 22:34 Comment(1)
Yes, this is definitely doable, if you don't insist on redefinition. Somewhat painful though if you use the raw preprocessor. Somewhat less painful with Boost.Preprocessor. Alas, my cold prevent me thinking straight enough to produce a method. Hopefully someone else will.Behr
T
7

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.

Tung answered 30/12, 2010 at 19:16 Comment(0)
B
15

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

Brunhilda answered 18/8, 2017 at 16:48 Comment(2)
You save my life.Steelworker
Thank a lot. I was looking for such a solutionKnorring
T
7

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.

Tung answered 30/12, 2010 at 19:16 Comment(0)
L
2

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

Lezley answered 28/12, 2010 at 22:48 Comment(1)
C99 variadic macros are nice (and I’ve used them), but this is not really relevant to my question. (Not directly, at least, though I can imagine that the actual solution might use variadic macros somewhere.)Homoiousian

© 2022 - 2024 — McMap. All rights reserved.