Question

Suppose some tokens FOO, BAR, and DUD (and possibly more) are #defined or not. I would like to have a macro EXTEND(name) that generates a valid extended name, e.g.

#define FOO
#undef  BAR
#define DUD
EXTEND(object)

expands to

object_foo_dud

Is it possible to write macro EXTEND with less than O(2n) lines if there are n macro tokens (like FOO, BAR, and DUD)? I think it should be possible with O(n) lines, but how?

I've tried this:

#ifdef FOO
#  define ExtFOO(name) name ## _foo
#else
#  define ExtFOO(name) name
#endif

#ifdef BAR
#  define ExtBAR(name) ExtFOO(name) ## _bar
#else
#  define ExtBAR(name) ExtFOO(name)
#endif

#ifdef DUD
#  define ExtDUD(name) ExtBAR(name) ## _dud
#else
#  define ExtDUD(name) ExtBAR(name)
#endif

#define EXTEND(name) ExtDUD(name)

but

test.cc:26:5: error: pasting formed ')_dud', an invalid preprocessing token
EXTEND(object)
^

Was it helpful?

Solution

The ## operator concatenates two preprocessing tokens and must yield a single valid token. For example, from section 6.10.3.3 of the C99 spec:

For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token. Placemarker preprocessing tokens are handled specially: concatenation of two placemarkers results in a single placemarker preprocessing token, and concatenation of a placemarker with a non-placemarker preprocessing token results in the non-placemarker preprocessing token. If the result is not a valid preprocessing token, the behavior is undefined. The resulting token is available for further macro replacement. The order of evaluation of ## operators is unspecified.

So the expansion ExtBAR(name) ## _dud is invalid since it would yield ExtBAR(object)_dud.

I'd go with the following approach:

#ifdef FOO
#  define ValFOO _foo
#else
#  define ValFOO
#endif

#ifdef BAR
#  define ValBAR _bar
#else
#  define ValBAR
#endif

#ifdef DUD
#  define ValDUD _dud
#else
#  define ValDUD
#endif

#define CONCAT(a, b, c, d) a ## b ## c ## d
#define XCONCAT(a, b, c, d) CONCAT(a, b, c, d)
#define EXTEND(name) XCONCAT(name, ValFOO, ValBAR, ValDUD)

The intermediate XCONCAT step is needed because macro arguments are not expanded if they're concatenated using ##.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top