How can I call a macro with all the dots in the catharsian product of argument sets?

Suppose I have a macro

FOO(a, b)

      

and I want to call 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 by either newlines, semicolons, or both, this is a minor issue, so let's ignore it; the exact order of the 4 calls is also not important.

Now, if I only had a "dimension" of parameters, I could use William Swanson's mechanism (as described here on the site, and there is even a github repository there ) 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 point is that I have two dimensions; and it seems impossible / difficult to separate yours __VA_ARGS__

into two different sets and iterate over the two-dimensional space of pairs of elements from these sets.

Can this be done (intelligently)?

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 okay too.
  • The solution should be compile time only and valid mostly everywhere in your C / C ++ translation unit; particularly in class definitions and file scopes.
  • You may be guessing that this is an XY problem and you are correct; but please don't pick my motivation as X and Y are interesting IMHO.
  • If you can maintain the lexicographic order of the macros, that would be fine.
+3


source to share


2 answers


The boost preprocessor library can execute Cartesian products already on preprocessor lists and sequences. You don't specify which preprocessor data types you want to inject ... assuming A and B are tuples, and that you have variables, 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 it FOO

is in all the headers, I assume you want to call FOO as a macro; EVAL

You can do it here.

You can easily extend this to higher dimensional Cartesian products.

+1


source


BOOST_REPEAT_PP

can help you.

For example, if you have two arrays of numbers, you can 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 can also follow some approach, like the following (not required 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

      

+3


source







All Articles