Creating list of stringized macro arguments with variadics and late expansions

271 views Asked by At

I have the following problem - given variable number of macro arguments argX to create a list of stringized arguments #argX

Example:

LIST(A, B) -> "A", "B"
LIST(A, B, C) -> "A", "B", "C"

I'm using Boost, so the above macro is not too difficult to implement using helper macros for each number of arguments and dispatching LIST(...) to the appropriate LIST_n(arg1, ... argn).

The problem starts when the inputs to LIST are themselves macros. In that case (if I use ... and __VA_ARGS__), the macros get expanded before they are stringized, giving:

#define A 10
LIST(A, B) -> "10", "B"

I want this to work with macros defined in Windows headers and most of the values there are macros (MB_OK, AF_INET, ...), so all I get is a list of stringized numbers.

When not using __VA_ARGS__ everything works fine:

#define A 10
#define LIST_1(arg0) #arg0
LIST_1(A) -> "A"

I've tried several macros that defer the expansion of __VA_ARGS__ to a later time (till LIST_1, for example, that has no variadics), but nothing worked.

Is this even possible to implement using C preprocessor?

1

There are 1 answers

0
Paul Fultz II On BEST ANSWER

I'm sorry but there is now way to do this on msvc. Because of classic bugs in their preprocessor(see here and here) they treat __VA_ARGS__ as a single argument. To break it up into separate arguments requires applying another scan which will then expand the macros as well. On a C99 preprocessor you can inhibit the expansion of __VA_ARGS__ using an empty placeholder:

/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)

/* p is an empty placeholder used to inhibit the expansion of __VA_ARGS__ */
#define STRINGIZE_ALL(p, ...) FIRST(NARGS(__VA_ARGS__), PRIMITIVE_STRINGIZE_ALL(p ## __VA_ARGS__,~,~,~,~,~,~,~,~))
#define PRIMITIVE_STRINGIZE_ALL(x1, x2, x3, x4, x5, x6, x7, x8, ...)  #x1, #x2, #x3, #x4, #x5, #x6, #x7, #x8

/* Retrieve the first n arguments from __VA_ARGS__ */
#define FIRST(n, ...) CAT(FIRST_, n)(__VA_ARGS__,~,~,~,~,~,~,~,~)
#define FIRST_1(x1, ...) x1
#define FIRST_2(x1, x2, ...) x1, x2
#define FIRST_3(x1, x2, x3, ...) x1, x2, x3
#define FIRST_4(x1, x2, x3, x4, ...) x1, x2, x3, x4
#define FIRST_5(x1, x2, x3, x4, x5, ...) x1, x2, x3, x4, x5
#define FIRST_6(x1, x2, x3, x4, x5, x6, ...) x1, x2, x3, x4, x5, x6
#define FIRST_7(x1, x2, x3, x4, x5, x6, x7, ...) x1, x2, x3, x4, x5, x6, x7
#define FIRST_8(x1, x2, x3, x4, x5, x6, x7, x8, ...) x1, x2, x3, x4, x5, x6, x7, x8

#define A 10
STRINGIZE_ALL(, A, B)

This will work for up to 8 arguments on gcc and clang 3.4 or higher.