如何调用包含参数集的笛卡尔积中所有点的宏?
How can I invoke a macro with all points in a catersian product of argument sets?
假设我有一个宏
FOO(a, b)
我想用 a
的一组特定(固定)可能值调用此宏;和 b
的一组固定值。因此,如果我的 b 值集是 bar
和 baz
,而我的 b 值集是 fiz
和 bang
,我想要:
FOO(bar, fiz)
FOO(bar, bang)
FOO(baz, fiz)
FOO(baz, bang)
由换行符或分号分隔,或两者兼而有之 - 这是一个小问题,所以让我们忽略它; 4 次调用的确切顺序也不重要。
现在,如果我只有一个 'dimension' 参数,我可以使用 William Swanson 的 mechanism (as described here on the site; and there's even a github repository),然后写
MAP(SINGLE_PARAMETER_MACRO, bar, baz, quux)
获得
SINGLE_PARAMETER_MACRO(bar)
SINGLE_PARAMETER_MACRO(baz)
SINGLE_PARAMETER_MACRO(quux)
但问题是,我有 两个 维度;似乎 impossible/tricky 将您的 __VA_ARGS__
分成两个不同的集合,并从这些集合中迭代二维 space 元素对。
这可以(合理地)完成吗?
备注:
- 我对基于 C 预处理器的解决方案很感兴趣,但如果您出于某种奇怪的原因而只能在 C++ 中使用,那也没关系。
- 解决方案必须仅在编译时提供,并且在您的 C/C++ 翻译单元中几乎所有地方都有效;具体来说,在 class 定义和文件范围内。
- 您可能会猜到这是一个 XY problem,您是对的;但请不要挑剔我的动机,因为 X 和 Y 都很有趣恕我直言。
- 如果您可以维护宏调用的字典顺序,那就太好了。
BOOST_REPEAT_PP
可以帮到你。
例如,如果你有两个数字数组,你可以在 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
}
输出:
2 1
2 3
2 7
4 1
4 3
4 7
如果您不想使用 std::array
,您也可以采用如下方法(不需要 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
}
输出:
bar fiz
bar bang
baz fiz
baz bang
ban fiz
ban bang
boost 预处理器库可以执行已经在预处理器列表和序列上的笛卡尔积。您没有指定要输入的预处理器数据类型...假设 A 和 B 是元组,并且您有可变参数,您可以这样做:
#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))
由于 FOO
全部大写,我假设您想最终将 FOO 作为宏调用; EVAL
这里允许你这样做。
您可以轻松地将其扩展到更高维度的笛卡尔积。
假设我有一个宏
FOO(a, b)
我想用 a
的一组特定(固定)可能值调用此宏;和 b
的一组固定值。因此,如果我的 b 值集是 bar
和 baz
,而我的 b 值集是 fiz
和 bang
,我想要:
FOO(bar, fiz)
FOO(bar, bang)
FOO(baz, fiz)
FOO(baz, bang)
由换行符或分号分隔,或两者兼而有之 - 这是一个小问题,所以让我们忽略它; 4 次调用的确切顺序也不重要。
现在,如果我只有一个 'dimension' 参数,我可以使用 William Swanson 的 mechanism (as described here on the site; and there's even a github repository),然后写
MAP(SINGLE_PARAMETER_MACRO, bar, baz, quux)
获得
SINGLE_PARAMETER_MACRO(bar)
SINGLE_PARAMETER_MACRO(baz)
SINGLE_PARAMETER_MACRO(quux)
但问题是,我有 两个 维度;似乎 impossible/tricky 将您的 __VA_ARGS__
分成两个不同的集合,并从这些集合中迭代二维 space 元素对。
这可以(合理地)完成吗?
备注:
- 我对基于 C 预处理器的解决方案很感兴趣,但如果您出于某种奇怪的原因而只能在 C++ 中使用,那也没关系。
- 解决方案必须仅在编译时提供,并且在您的 C/C++ 翻译单元中几乎所有地方都有效;具体来说,在 class 定义和文件范围内。
- 您可能会猜到这是一个 XY problem,您是对的;但请不要挑剔我的动机,因为 X 和 Y 都很有趣恕我直言。
- 如果您可以维护宏调用的字典顺序,那就太好了。
BOOST_REPEAT_PP
可以帮到你。
例如,如果你有两个数字数组,你可以在 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
}
输出:
2 1
2 3
2 7
4 1
4 3
4 7
如果您不想使用 std::array
,您也可以采用如下方法(不需要 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
}
输出:
bar fiz
bar bang
baz fiz
baz bang
ban fiz
ban bang
boost 预处理器库可以执行已经在预处理器列表和序列上的笛卡尔积。您没有指定要输入的预处理器数据类型...假设 A 和 B 是元组,并且您有可变参数,您可以这样做:
#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))
由于 FOO
全部大写,我假设您想最终将 FOO 作为宏调用; EVAL
这里允许你这样做。
您可以轻松地将其扩展到更高维度的笛卡尔积。