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.
source to share
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.
source to share
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
source to share