My actual code example is quite complex, but I will try to summarize the behavior I am seeing with a simple illustration.
I have a macro that I want to be able to call individually, or multiple times as a part of a larger macro expansion:
#define DO_STUFF(name,...) \
STUFF1(name,__VA_ARGS__) \
STUFF2(name,__VA_ARGS__) \
STUFF3(name,__VA_ARGS__)
I can use DO_STUFF(dave, int, char)
and similar variations directly in a source file and it generates the code I expect.
I also want to invoke big lists of the DO_STUFF
macro with another list of inputs. In order to handle this case, I am using boost preprocessor with a sequence of tuples (or variadic sequence):
DO_LOTS_OF_STUFF(
(dave, ball, pen) \
(alice, cat, dog, bicycle) \
(peter, bird) )
My definition of DO_LOTS_OF_STUFF
looks like:
#define DO_LOTS_OF_STUFF(SEQ) \
BOOST_PP_SEQ_FOR_EACH( \
INVOKE_DS, _, \
BOOST_PP_VARIADIC_SEQ_TO_SEQ(SEQ) \
)
#define INVOKE_DS( r, data, elem ) \
DO_STUFF(BOOST_PP_TUPLE_ENUM(elem)); \
When I invoke DO_LOTS_OF_STUFF
as illustrated above, STUFF1
, STUFF2
, and STUFF3
are all invoked with an extra comma at the end with an empty parameter.
If I break the expansion at the point that DO_STUFF
is invoked (by changing its name), the preprocessor output looks like I expect:
DO_STUFF(dave, ball, pen)
DO_STUFF(alice, cat, dog, bicycle)
DO_STUFF(peter, bird)
If I break the expansion at the STUFF1
, STUFF2
, and STUFF3
level, they appear in the output with an extra empty parameter:
STUFF1(dave, ball, pen,)
STUFF2(dave, ball, pen,)
STUFF3(dave, ball, pen,)
STUFF1(alice, cat, dog, bicycle,)
STUFF2(alice, cat, dog, bicycle,)
STUFF3(alice, cat, dog, bicycle,)
STUFF1(peter, bird,)
STUFF2(peter, bird,)
STUFF3(peter, bird,)
Is this just one of those things to avoid in preprocessor meta-programming like "don't use ##
"? "Don't use __VA_ARGS__
in nested macros"?
Any advice on how to define DO_STUFF
or DO_LOTS_OF_STUFF
to avoid this issue?
I figured out what is going on.
Where I invoke DO_STUFF
with SEQ_FOR_EACH
I use BOOST_PP_TUPLE_ENUM
:
#define INVOKE_DS( r, data, elem ) \
DO_STUFF(BOOST_PP_TUPLE_ENUM(elem)); \
BOOST_PP_TUPLE_ENUM
turns a tuple (a,b,c,d)
into comma separated tokens without the parenthesis a,b,c,d
.
I assumed that that meant that DO_STUFF(BOOST_PP_TUPLE_ENUM((a,b,c,d)))
would see 4 arguments, but in fact it still sees only one that is a,b,c,d
.
This expansion:
#define DO_STUFF(name,...) \
STUFF1(name,__VA_ARGS__) \
STUFF2(name,__VA_ARGS__) \
STUFF3(name,__VA_ARGS__)
Is actually adding the extra empty parameter in this case because __VA_ARGS__
is empty.
So the solution was pretty simple. I just created a different variant of the DO_STUFF
macro that is used in the DO_LOTS_OF_STUFF
case
#define DO_LOTS_OF_STUFF(SEQ) \
BOOST_PP_SEQ_FOR_EACH( \
INVOKE_DS, _, \
BOOST_PP_VARIADIC_SEQ_TO_SEQ(SEQ) \
)
#define INVOKE_DS( r, data, elem ) \
DO_STUFF_1(BOOST_PP_TUPLE_ENUM(elem)); \
#define DO_STUFF_1(tuple_args) \
STUFF1(tuple_args) \
STUFF2(tuple_args) \
STUFF3(tuple_args)