Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is variadic macro subsitution for every argument possible?

I read quite a few questions on SO about variadic macros now, but it doesn't seem like anyone answered the most simple question:

#define IDENTITY(x) x
#define IDENTITY_FOR_ALL(...) ???

Is there a way to make IDENTITY_FOR_ALL expand to IDENTITY(X) for all arguments? Is it also possible for arbitrary numbers of arguments?

like image 320
iFreilicht Avatar asked Oct 20 '14 21:10

iFreilicht


People also ask

How many arguments macro can have?

A comma must separate each parameter. For portability, you should not have more than 31 parameters for a macro. The parameter list may end with an ellipsis (…).

How do I use variadic macros?

To use variadic macros, the ellipsis may be specified as the final formal argument in a macro definition, and the replacement identifier __VA_ARGS__ may be used in the definition to insert the extra arguments. __VA_ARGS__ is replaced by all of the arguments that match the ellipsis, including commas between them.

Can you supply more than one argument in a macro call?

Yes. If you try untouchable(foo,first) you get an error because the macro only sees two parameters but was expecting 4.

What are the macros that are designed to support variable length arguments?

To support variable length arguments in macro, we must include ellipses (…) in macro definition. There is also “__VA_ARGS__” preprocessing identifier which takes care of variable length argument substitutions which are provided to macro.


2 Answers

There is no such thing as a pack expansion for variadic macros as there is for variadic templates.

You could use Boost.Preprocessor (or its methods) though.

If you don't want any commas between the elements, use

#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define ID_OP(_, func, elem) func(elem)
#define APPLY_TO_ALL(func, ...)                \
    BOOST_PP_SEQ_FOR_EACH(                     \
        ID_OP, func,                           \
        BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)  \
    )

// example call:

#define SomeTransformation(x) #x // stringize the argument

APPLY_TO_ALL(SomeTransformation, 1, 2, 3) // expands to "1" "2" "3"

Demo. With commas:

#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define ID_OP(_, func, elem) func(elem)
#define APPLY_TO_ALL(func, ...)               \
    BOOST_PP_SEQ_ENUM(                        \
    BOOST_PP_SEQ_TRANSFORM(                   \
        ID_OP, func,                          \
        BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) \
    ))

// example call:

APPLY_TO_ALL(SomeTransformation, 1, 2, 3) // expands to "1", "2", "3"

Demo. Check the preprocessor output with g++ -std=c++11 -E -P file.

like image 129
Columbo Avatar answered Sep 29 '22 16:09

Columbo


Assuming you need a PP solution, you can use BOOST_PP_REPEAT:

//invoke IDENTITY_FOR_ALL_MACRO with each index and the given tuple
#define IDENTITY_FOR_ALL(...)                   \
    BOOST_PP_REPEAT(                            \
        BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),    \
        IDENTITY_FOR_ALL_MACRO,                 \
        BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \
    )

//use the index to access the right element of the passed tuple
#define IDENTITY_FOR_ALL_MACRO(z, n, data) \
    IDENTITY(BOOST_PP_TUPLE_ELEM(n, data))

IDENTITY_FOR_ALL(abc, 123, "woopee")
//translated to abc 123 "woopee"

It would be decently straightforward to turn this into a more general macro that takes a macro to invoke and the list of single arguments to pass one by one if you need to do this with several different macros and not just IDENTITY.

I'm not 100% sure what you mean by an arbitrary number of arguments, but if you want to invoke IDENTITY with two arguments at a time instead of one, you can change the bottom macro to use BOOST_PP_MUL and BOOST_PP_INC to access the "2n"th and "2n+1"th elements of the tuple, and then invoke the macro only half as many times in the REPEAT call.

like image 32
chris Avatar answered Sep 29 '22 17:09

chris