0

I want a macro that takes any even number of parameters, stitches each pair together, adds some name to the end, let's say Type, and then passes them as template arguments to some type, let's call it CompileTimeList. So I would give it something like this:

MACRO( \
  First,Set, \
  Second,Type, \
  Third,Thing)

and it would expand to:

CompileTimeList<
  FirstSetType,
  SecondTypeType,
  ThirdThingType>

The naive solution would be a variadic recursive template, which is not allowed:

Can we have recursive macros?

I could manually hack together a macro using this methodology:

Macro recursive expansion to a sequence

But it would be limited to however many parameters I've defined. I want to be able to expand any length of parameters. Is this possible in C++11? I also have Boost 1.55 available, which wasn't mentioned in the other questions.

In order to attempt to implement chris' answer, I've written the following program:

  1 #include <boost/preprocessor.hpp>
  2
  3 #include <vector>
  4 #include <algorithm>
  5
  6 #define MACRO(...)  \
  7     std::find( \
  8     BOOST_PP_ENUM( \
  9         BOOST_PP_DIV(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 2), \
 10         ENUM_MACRO, \
 11         BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \
 12     ) \
 13     )
 14
 15 #define ENUM_MACRO(z, n, data) \
 16     BOOST_PP_CAT( \
 17         BOOST_PP_CAT( \
 18             BOOST_PP_TUPLE_ELEM(BOOST_PP_MUL(n, 2), data), \
 19             BOOST_PP_TUPLE_ELEM(BOOST_PP_INC(BOOST_PP_MUL(n, 2)), data) \
 20         ), \
 21         Type \
 22     )
 23
 24
 25 typedef int FirstNameType;
 26 typedef std::vector<int>::iterator SecondNameType;
 27 typedef std::vector<int>::iterator ThirdNameType;
 28
 29 int main()
 30 {
 31     std::vector<int>();
 32     auto it = MACRO(First, Name, Second, Name, Third, Name);
 33     return 0;
 34 }

Which produces an avalanche of errors relating to the use of commas, starting with:

test.cpp: In function ‘int main()’: src/boost/boost/preprocessor/punctuation/comma.hpp:19:27: error: expected primary-expression before ‘,’ token # define BOOST_PP_COMMA() ,

Community
  • 1
  • 1
quant
  • 21,507
  • 32
  • 115
  • 211
  • I would instead go for `MACRO((First, Set), (Second, Type), (Third, Thing))` and use `BOOST_PP_TUPLE_*`. – chris Jul 11 '14 at 01:40
  • @chris Thanks, I wasn't aware of this. But how do you get around the "reursion isn't allowed" problem? – quant Jul 11 '14 at 01:42
  • @Jason: By recursing anyway: File x.h: `#include "x.h"`... – Kerrek SB Jul 11 '14 at 01:43
  • @KerrekSB I'm not sure I follow. You mean I should `#include` recursively? – quant Jul 11 '14 at 01:45
  • @Jason, Boost provides utilities for enumerating sequences. Have a look through http://www.boost.org/doc/libs/1_55_0/libs/preprocessor/doc/index.html – chris Jul 11 '14 at 01:45
  • @Jason: It depends how desperate you are. You can let Boost do the job for you, that's a little more readable. – Kerrek SB Jul 11 '14 at 01:46
  • @KerrekSB Well I'd prefer to keep it as clean as possible. Boost seems like a possible solution. – quant Jul 11 '14 at 02:03

1 Answers1

1

Your desired syntax can be achieved through Boost with a couple macros:

#define BOOST_PP_VARIADICS
#include <boost/preprocessor.hpp>

//The main part is BOOST_PP_ENUM, which loops the number of arguments / 2 times.
//Each loop, ENUM_MACRO is called with the index and a tuple containing the arguments.
//A comma is inserted between all expansions of ENUM_MACRO.
#define MACRO(...)  \
    CompileTimeList< \
    BOOST_PP_ENUM( \
        BOOST_PP_DIV(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 2), \
        ENUM_MACRO, \
        BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \
    ) \
    >

//Since it's called #args/2 times, we multiply the index by 2.
//All it does is concatenate the first in its pair with the second, and then to Type.
#define ENUM_MACRO(z, n, data) \
    BOOST_PP_CAT( \
        BOOST_PP_CAT( \
            BOOST_PP_TUPLE_ELEM(BOOST_PP_MUL(n, 2), data), \
            BOOST_PP_TUPLE_ELEM(BOOST_PP_INC(BOOST_PP_MUL(n, 2)), data) \
        ), \
        Type \
    )


MACRO(a, b, c, d) //CompileTimeList< abType , cdType >

See it work here.

While in its current state, this macro will simply ignore the last argument in an odd-numbered list, it is possible to produce an error with a simple static_assert, or to do something special with the last argument if so desired.

chris
  • 60,560
  • 13
  • 143
  • 205
  • I can't get this to run, have to tried running it? I get a bunch of errors that seem to be unrelated... like errors in `BOOST_PP_COMMA()` – quant Jul 15 '14 at 07:38
  • I've modified my answer to include my attempt at implementing your suggestion. – quant Jul 15 '14 at 07:42
  • @Jason It expands to `auto it = std::find( FirstNameType , SecondNameType , ThirdNameType );` just fine (you can use `g++ -E` to see the preprocessed version of your source code). Your problem is that you can't use types as function arguments. – T.C. Jul 15 '14 at 07:52
  • @Jason, `BOOST_PP_VARIADICS` needs to be defined. I'm not sure offhand if there's an easy way to change the code to not need it, but there's also a proper header to include for it (something like ``. Now since Coliru doesn't seem to have that header, I'm not sure whether it's included straight from `` or not. Anyway, your example [does work with Clang](http://coliru.stacked-crooked.com/a/b49255e554842767), minus the regular compilation errors of types as arguments. – chris Jul 15 '14 at 12:11