3

Suppose I have a macro

FOO(a, b)

and I want to invoke this macro with a certain (fixed) set of possible values for a; and a certain fixed set of values for b. Thus, if my set of b values is bar and baz and my set of b values is fiz and bang, I want:

FOO(bar, fiz)
FOO(bar, bang)
FOO(baz, fiz)
FOO(baz, bang)

separated either by new lines or by semicolons, or both - that's a minor issue so let's ignore it; the exact order of the 4 invocations is also not important.

Now, if I only had one 'dimension' of parameters, I could use William Swanson's mechanism (as described here on the site; and there's even a github repository for it), and write

MAP(SINGLE_PARAMETER_MACRO, bar, baz, quux)

to obtain

SINGLE_PARAMETER_MACRO(bar)
SINGLE_PARAMETER_MACRO(baz)
SINGLE_PARAMETER_MACRO(quux)

but the thing is, I have two dimensions; and it seems to be impossible/tricky to separate your __VA_ARGS__ into two different sets and iterate the two-dimensional space of pairs of elements from these sets.

Can this be (reasonably) done?

Notes:

  • I'm interested in a C-preprocessor-based solution, but if you have something that only works in C++ for some strange reason, that's ok too.
  • Solution must be compile-time only, and valid mostly everywhere in your C/C++ translation unit; specifically, within class definitions and at file scope.
  • You might be guessing this is an XY problem, and you're right; but please don't pick at my motivation since both X and Y are interesting IMHO.
  • If you can maintain a lexicographic order of the macro invocations, that would be nice.
Community
  • 1
  • 1
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 2
    I don't think there are any looping mechanisms in the preprocessor. Why can't you do this in a runtime loop? – Barmar May 01 '17 at 16:49
  • @Barmar: `FOO` may be a declaration or a definition, not a statement. The preprocessor admits a list-head-vs-tail looping mechanism - but it's not inherently multi-dimensional. – einpoklum May 01 '17 at 16:50
  • @WilliamSwanson : Perhaps as the author of the related answer you might have some advice for me? – einpoklum May 01 '17 at 16:52
  • Maybe you'll be interested in invoking cartesian product in case of function parameters: http://stackoverflow.com/questions/39687907/is-it-possible-to-invoke-a-method-with-all-possible-k-combinations-with-repetit – W.F. May 01 '17 at 16:55
  • @W.F.: While it doesn't answer this question (the "Y") per se, I think I might be able to adapt something like that to solve my "X" problem. But it's a PITA :-( – einpoklum May 01 '17 at 17:46

2 Answers2

3

BOOST_REPEAT_PP can help you.

For instance, if you have two arrays of numbers, you could do this in c++14:

#include <boost/preprocessor/repetition/repeat.hpp>
#include <array>
#include <iostream>

constexpr std::array<int, 2> a = {2, 4};
constexpr std::array<int, 3> b = {1, 3, 7};

#define MYNUMBER2(z, n, x) std::cout << a[x] << " " << b[n] << std::endl;
#define MYNUMBER(z, n, x) BOOST_PP_REPEAT(3, MYNUMBER2, n)

int main() {
  BOOST_PP_REPEAT(2, MYNUMBER, 0); // The "0" is actually not used
}

Output:

2 1
2 3
2 7
4 1
4 3
4 7

If you don't want to use std::array, you could also follow some approach like the following (does not require c++14):

#include <boost/preprocessor/repetition/repeat.hpp>
#include <iostream>

#define A_0 "bar"
#define A_1 "baz"
#define A_2 "ban"

#define B_0 "fiz"
#define B_1 "bang"

#define FOO(s1, s2) std::cout << s1 << " " << s2 << std::endl;
#define MYSTRING2(z, n, x) FOO(A_##x, B_##n)
#define MYSTRING(z, n, x) BOOST_PP_REPEAT(2, MYSTRING2, n)

int main() {
  BOOST_PP_REPEAT(3, MYSTRING, 0); // The "0" is actually not used
}

Output:

bar fiz
bar bang
baz fiz
baz bang
ban fiz
ban bang
oLen
  • 5,177
  • 1
  • 32
  • 48
  • Does this work at file scope, i.e. for definitions/declarations? – einpoklum May 01 '17 at 18:10
  • @einpoklum yes, it only performs macro substitution so anything should be possible. – oLen May 01 '17 at 18:31
  • 1
    @einpoklum yes, you can easily use it for declarations. [Example](https://wandbox.org/permlink/5iEXuuaDdbp1cdaY). – xinaiz May 01 '17 at 18:48
  • This has the drawback of not being able to have the two sets of arguments on the same line / in the same macro invocation, but rather having to define an inner macro - one level of recursion for each parameter of `FOO`. But it's already an improvement so +1. – einpoklum May 01 '17 at 20:01
2

The boost preprocessor library can perform Cartesian products already on preprocessor lists and sequences. You don't specify what preprocessor data types you want to input... assuming that A and B are tuples, and that you have variadics, you can do:

#include <boost/preprocessor/seq/for_each_product.hpp>
#include <boost/preprocessor/seq/to_tuple.hpp>
#include <boost/preprocessor/tuple/to_seq.hpp>

#define EVAL(...) __VA_ARGS__
#define FOO_SEMI_DELIM(R,SEQ_X) EVAL(FOO BOOST_PP_SEQ_TO_TUPLE(SEQ_X));
#define FOO_CARTESIAN(TUP_A,TUP_B) \
   BOOST_PP_SEQ_FOR_EACH_PRODUCT \
   ( FOO_SEMI_DELIM, \
     (BOOST_PP_TUPLE_TO_SEQ(TUP_A)) \
     (BOOST_PP_TUPLE_TO_SEQ(TUP_B)) \
   )

FOO_CARTESIAN((John,Jane),(Smith,Jones,Parker,Peterson))

Since FOO is in all caps, I'm assuming you want to wind up calling FOO as a macro; EVAL here allows you to do this.

You can easily extend this to Cartesian products of higher dimensions.

H Walters
  • 2,634
  • 1
  • 11
  • 13
  • That's my answer. A hefty dependency on the Boost preprocessor mechanism, but that's a reasonable price to pay I suppose. – einpoklum May 02 '17 at 07:13