3

I would like to construct a macro that takes a variable number of arguments and distributes the first argument to each of the subsequent in a format similar to the examples shown below:

Call:   MACRO(F,A)
Result: F:A

Call:   MACRO(F,A,B,C)
Result: F:A F:B F:C

I have seen https://github.com/swansontec/map-macro and the general concept of the recursion workaround through:

#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__)))
#define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__)))
#define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__)))
#define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__)))
#define EVAL(...)  EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))

But I cannot wrap my mind around how to apply this to my problem. Can anyone provide an example to achieve the results shown above? Thanks!

a3f
  • 8,517
  • 1
  • 41
  • 46
pt3dNyc
  • 369
  • 1
  • 16

3 Answers3

6

With Boost.PP:

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

#define TRANSFORM(r, data, elem) data:elem

#define MACRO(F, ...) \
    BOOST_PP_SEQ_FOR_EACH(TRANSFORM, F, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

Demo. This will (AFAIR) work for up to 255 arguments.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Thanks, this really simplifies the code! One question though: if the first element is actually a fixed-length tuple rather than a single element, how do I unpack it within TRANSFORM? Meaning, I'd like MACRO((FX,FY), A, B) to result in FX_FY:A FX_FY:B but the unpacking is not working as expected. (Sorry, I'm new to Boost.PP) Thanks! – pt3dNyc Jan 04 '15 at 13:17
  • So you want to concatenate all elements in a tuple with `_` and pass that as the argument to `F`? – Columbo Jan 04 '15 at 13:24
  • Yeah - I use that as example, but I'd like to be able to expand the "data" tuple and pass its elements into another macro that would format an identifier... along the lines of concatenating them with '_' separators. The contents of the "data" tuple would always have a fixed number of elements. – pt3dNyc Jan 04 '15 at 13:27
  • @pt3dNyc Something like [**this**](http://coliru.stacked-crooked.com/a/fc6f1f877bcc9564)? – Columbo Jan 04 '15 at 13:32
  • Yes, but I need to be able to format it in two different ways... The full result I'm hoping to achieve is: Call: MACRO( (F1,F2,F3), (A1,A2), (B1,B2) ) Result: { F1_F2_F3::F1_F3 } { F1_F2_F3::F1_F3 } The idea being that I'm using (F1,F2,F3) as something of a parameter pack to format various identifiers for each of the subsequent pairs - (A1,A2), etc - to which it is applied. Thanks very much for your help! – pt3dNyc Jan 04 '15 at 13:53
  • @pt3dNyc Are you sure you cannot use variadic templates for your matter? – Columbo Jan 04 '15 at 14:02
  • Yup, you're right - just had to work with it a bit more. Thanks for all your help, this is a great solution! – pt3dNyc Jan 04 '15 at 15:03
4

You can use the NARGS macro, which counts the arguments of variadic macros, to create recursive macros that apply a prefix to all your aguments:

#define NARGS(...) NARGS_(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define NARGS_(_5, _4, _3, _2, _1, N, ...) N

#define CONC(A, B) CONC_(A, B)
#define CONC_(A, B) A##B

#define PREFIX_0(P, E) E
#define PREFIX_1(P, E) P:E
#define PREFIX_2(P, E, ...) P:E PREFIX_1(P, __VA_ARGS__)
#define PREFIX_3(P, E, ...) P:E PREFIX_2(P, __VA_ARGS__)
#define PREFIX_4(P, E, ...) P:E PREFIX_3(P, __VA_ARGS__)
#define PREFIX_5(P, E, ...) P:E PREFIX_4(P, __VA_ARGS__)

#define PREFIX(P, ...) CONC(PREFIX_, NARGS(__VA_ARGS__)) (P, __VA_ARGS__)

PREFIX(F, A)
PREFIX(F, A, B, C, D)

Here, the total number of arguments is limited to 5 for brevity, but you can extend this solution by extending the NARGS and NARGS_ in the obvious way and by writing more PREFIX_X macros.

M Oehm
  • 28,726
  • 3
  • 31
  • 42
  • Thanks! Though I was originally hoping to implement without Boost.PP, it seems like it would probably be worth the trouble to include the dependency. This is a great solution, but might get a bit out of control in my use case. Thanks again! – pt3dNyc Jan 04 '15 at 13:59
0

You might pass your own macro to the MAP macro:

Example

#include<iostream>

struct F {
    static void A() { std::cout << "A\n"; }
    static void B() { std::cout << "B\n"; }
    static void C() { std::cout << "C\n"; }
};

int main()
{
    #define CALL_MEMBER(Member) F::Member();
    #define CALL_STRUCTURE(Structure) Structure::CALL_MEMBER
    #define MACRO(Structure, ...)  MAP(CALL_STRUCTURE(Structure), __VA_ARGS__)
    MACRO(F, A)
    MACRO(F, A, B, C)
}

The MAP macro from https://github.com/swansontec/map-macro

/*
* Copyright (C) 2012 William Swanson
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the names of the authors or
* their institutions shall not be used in advertising or otherwise to
* promote the sale, use or other dealings in this Software without
* prior written authorization from the authors.
*/
#ifndef MAP_H_INCLUDED
#define MAP_H_INCLUDED
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__)))
#define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__)))
#define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__)))
#define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__)))
#define EVAL(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
#define MAP_END(...)
#define MAP_OUT
#define MAP_GET_END() 0, MAP_END
#define MAP_NEXT0(test, next, ...) next MAP_OUT
#define MAP_NEXT1(test, next) MAP_NEXT0 (test, next, 0)
#define MAP_NEXT(test, next) MAP_NEXT1 (MAP_GET_END test, next)
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
#endif