多个模板的笛卡尔积
Cartesian product of multiple templates
我有几个类:
template <int,char>
class Foo{};
template <int,char>
class Bar{};
我想得到所有带有几个参数的组合,像这样:
// {1, 2}, {'a', 'b'}
using CartesianProduct = mp_list<Foo<1,'a'>, Foo<1,'b'>,...,Bar<2,'b'>>;
我可以将模板参数更改为类型并使用 std::integral_constant
,如果它不能用数字常量完成的话。
我发现了一个类似的问题,但难度要大得多。 Creating all template permutations with MPL。我想,我的情况有更好更简单的解决方案。
这里有一个名为 combine_view
的实现: that I used in my library https://gitlab.com/correaa/boost-covariant
也许现在MP11库里有更好的东西了。
这是我的提炼版本:
#include<boost/mpl/vector.hpp>
#include<boost/mpl/set.hpp>
#include<boost/mpl/fold.hpp>
#include<boost/mpl/zip_view.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/pop_front.hpp>
namespace boost{
namespace mpl{
template <class Seq, class ItrSeq>
class SequenceCombiner{
template < class _Seq = mpl::vector<int_<1> > >
struct StateSeq{
typedef typename pop_front<_Seq>::type sequence;
typedef typename mpl::at<_Seq, int_<0> >::type state;
typedef _Seq type;
};
template < class _Seq, class _State >
struct set_state{
typedef StateSeq< typename push_front<_Seq, _State>::type > type;
};
struct NextOp {
template<typename Out, typename In, typename Enable = typename Out::state>
class apply{
using seq = typename Out::sequence;
using new_state = typename Out::state;
using in_seq = typename mpl::at<In,int_<0> >::type;
using in_itr = typename mpl::at<In,int_<1> >::type;
using new_seq = typename mpl::push_back<seq, in_itr>::type;
public:
typedef typename set_state<new_seq, int_<0> >::type type;
};
template<typename Out, typename In>
class apply<Out,In,mpl::int_<1> >{
typedef typename Out::sequence seq;
typedef typename Out::state state;
typedef typename mpl::at<In,int_<0> >::type in_seq;
typedef typename mpl::at<In,int_<1> >::type in_itr;
typedef typename mpl::begin<in_seq>::type Itr_begin;
typedef typename mpl::next<in_itr>::type Itr_next;
typedef typename mpl::end<in_seq>::type Itr_end;
typedef typename mpl::if_<
boost::is_same<Itr_next,Itr_end>,
typename mpl::push_back<seq,Itr_begin>::type,
typename mpl::push_back<seq,Itr_next>::type
>::type new_seq;
typedef typename mpl::if_<boost::is_same<Itr_next,Itr_end>,
mpl::int_<1>,
mpl::int_<0>
>::type new_state;
public:
typedef typename set_state<new_seq, new_state>::type type;
};
};
typedef typename mpl::fold<
typename mpl::zip_view<mpl::vector<Seq, ItrSeq> >::type,
StateSeq<>,
NextOp
>::type StateResult;
public:
typedef typename mpl::if_<
boost::is_same<typename StateResult::state, int_<1> >,
typename mpl::transform<Seq, mpl::end<_1> >::type,
typename StateResult::sequence
>::type next;
};
template<typename Seq, typename Itrs>
struct combine_iterator{
typedef mpl::forward_iterator_tag category;
typedef Seq seq;
typedef typename transform<Itrs, deref<_1> >::type type;
};
template <class Seq, class Pos>
struct next<typename mpl::combine_iterator<Seq, Pos>>{
typedef typename mpl::SequenceCombiner<Seq,Pos>::next next_Pos;
typedef boost::mpl::combine_iterator<Seq, next_Pos> type;
};
template<class Seq>
class combine_view{
using Pos_begin = typename mpl::transform<Seq, mpl::begin<_1> >::type;
using Pos_end = typename mpl::transform<Seq, mpl::end<_1> >::type;
public:
using begin = combine_iterator<Seq, Pos_begin>;
using end = combine_iterator<Seq, Pos_end>;
using type = combine_view;
};
} // mpl
} // boost
使用示例:
using boost::mpl::combine_view;
using product = combine_view<
boost::mpl::list<
boost::mpl::list<double, int, std::string>,
boost::mpl::list<double, int>,
boost::mpl::list<std::string, char>
>
>::type;
static_assert( boost::mpl::size<product>::value == 12 );
为了好玩,我尝试使用可变参数模板 std::integer_sequence
和 std::tuple_cat
手动实现 并且真的很惊讶很容易让它工作:基于简单的任意长度的数组
constexpr std::array<int,2> t1 = {1, 2};
constexpr std::array<char,3> t2 = {'a', 'b', 'c'};
并给定 可变参数模板 classes (例如 Foo
和 Bar
)它自己生成所有可能的排列并将它们合并到可以定义方便别名的 std::tuple
(或 boost::mpl::list
)数据类型:
using SomeAlias = typename AllClassesAllPermutations<t1.size(), t2.size(), t1, t2, Foo, Bar>::type;
不熟悉 Boost::MPL
我试着远离它:我的第一个版本其实是基于 std::tuple
如果你想要 boost::mpl::list
而不是 std::tuple
这可以很容易地用另一个 可变模板转换函数
下一节将详细介绍如何实现这一点!
为此,我写了一个 class,它基于一些模板参数,例如上述 int
和 char
参数的组合以及相应的 template template class
- 创建一个 std::tuple
,其中包含 单个 template template class
的 int
和 char
数组的所有排列。这是通过创建两个包含成对排列的排列向量来完成的。例如。两个输入数组 t1_in = {1, 2}
和 t2_in = {'a', 'b', 'c'};
使用函数 duplicateArray
扩展为 t1 = {1, 1, 1, 2, 2, 2}
和 t2 = {'a', 'b', 'c', 'a', 'b', 'c'}
,然后创建一个 std::integer_sequence
以便两者可以融合到一个模板 T<t1[I], t2[I]>
中,与 std::integer_sequence
结合为您提供单个 class:
的所有排列
template <std::size_t I1, std::size_t I2, std::array<int,I1> const& t1_in, std::array<char,I2> const& t2_in, template <int, char> class T>
class SingleClassAllPermutations {
private:
template <std::size_t K, std::size_t I, typename TA, std::size_t J>
static constexpr auto duplicateArray(std::array<TA, J> const& arr_in) {
std::array<TA, I*J*K> arr_out {0};
std::size_t l {0};
for (std::size_t i = 0; i < I; ++i) {
for (std::size_t j = 0; j < J; ++j) {
for (std::size_t k = 0; k < K; ++k) {
arr_out[l] = arr_in[j];
++l;
}
}
}
return arr_out;
}
static constexpr std::size_t N = I1*I2;
static constexpr std::array<int,N> t1 = duplicateArray<I2,1>(t1_in);
static constexpr std::array<char,N> t2 = duplicateArray<1,I1>(t2_in);
static constexpr auto index_seq = std::make_index_sequence<N>{};
template <std::size_t... I>
static constexpr auto getTuple(std::index_sequence<I...>) {
return std::make_tuple(T<t1[I], t2[I]>() ...);
}
public:
static constexpr auto tup = getTuple(index_seq);
};
然后我创建了一个 可变参数模板,它可能需要几个不同的 template template classes
(只要它们的模板参数是 int
和 char
分别)作为额外的输入参数,然后将各个排列的内部元组 tup
与 std::tuple_cat
合并,以创建包含 [=30 的所有可能排列 的 元组=] 和 char
数组以及不同的可变参数 template template class
es:
template <std::size_t I1, std::size_t I2, std::array<int,I1> const& t1, std::array<char,I2> const& t2, template <int,char> class... Ts>
class AllClassesAllPermutations {
public:
static constexpr auto getTuple() {
return std::tuple_cat(SingleClassAllPermutations<I1,I2,t1,t2,Ts>::tup ...);
}
using type = decltype(getTuple());
};
现在可以在命名空间内定义全局 constexpr
数组和方便的 alias
例如
namespace some_namespace {
constexpr std::array<int,2> t1 = {1, 2};
constexpr std::array<char,3> t2 = {'a', 'b', 'c'};
using SomeInstantiation = typename AllClassesAllPermutations<t1.size(), t2.size(), t1, t2, Foo, Bar>::type;
}
这样就可以重复使用上面的模板来生成不同的数据类型。
然后模板将其扩展为所有可能的排列,在上述情况下为
std::tuple<Foo<1,'a'>, Foo<1,'b'>, Foo<1,'c'>, Foo<2,'a'>, Foo<2,'b'>, Foo<2,'c'>,
Bar<1,'a'>, Bar<1,'b'>, Bar<1,'c'>, Bar<2,'a'>, Bar<2,'b'>, Bar<2,'c'>>
最后如果你想要一个boost::mpl::list
代替你可以引入一个转换函数比如
template <class... Ts>
static constexpr auto tupleToMplList(std::tuple<Ts...>) {
return boost::mpl::list<Ts...>{};
}
您再次使用 decltype
而不是 std::tuple
,这导致数据类型
boost::mpl::list<Foo<1,'a'>, Foo<1,'b'>, Foo<1,'c'>, Foo<2,'a'>, Foo<2,'b'>, Foo<2,'c'>,
Bar<1,'a'>, Bar<1,'b'>, Bar<1,'c'>, Bar<2,'a'>, Bar<2,'b'>, Bar<2,'c'>>
终于自己想通了
using namespace boost::mp11;
template <typename C1, typename C2>
struct Foo{};
template <typename C1, typename C2>
struct Bar{};
template <template <typename...> typename... F>
using mp_list_q = mp_list<mp_quote<F>...>;
using TypeList = mp_product<mp_invoke_q,
mp_list_q<Foo, Bar>,
mp_list_c<int, 1, 2>,
mp_list_c<char, 'a', 'b'>>;
结果:
boost::mp11::mp_list<
Foo<std::integral_constant<int, 1>, std::integral_constant<char, (char)97> >,
Foo<std::integral_constant<int, 1>, std::integral_constant<char, (char)98> >,
Foo<std::integral_constant<int, 2>, std::integral_constant<char, (char)97> >,
Foo<std::integral_constant<int, 2>, std::integral_constant<char, (char)98> >,
Bar<std::integral_constant<int, 1>, std::integral_constant<char, (char)97> >,
Bar<std::integral_constant<int, 1>, std::integral_constant<char, (char)98> >,
Bar<std::integral_constant<int, 2>, std::integral_constant<char, (char)97> >,
Bar<std::integral_constant<int, 2>, std::integral_constant<char, (char)98> > >
它使用 std::integral_constant
作为参数,但它简单而简短。 Try it here
更新:我发现了如何使用整数本身!
template <int, char>
struct Foo{};
template <int, char>
struct Bar{};
template <template <auto...> typename F>
struct mp_quote_c
{
template <typename... C>
using fn = F<C::value...>;
};
template <template <auto...> typename... F>
using mp_list_qc = mp_list<mp_quote_c<F>...>;
using TypeList = mp_product<mp_invoke_q,
mp_list_qc<Foo, Bar>,
mp_list_c<int, 1, 2>,
mp_list_c<char, 'a', 'b'>>;
结果:
boost::mp11::mp_list<
Foo<1, (char)97>, Foo<1, (char)98>, Foo<2, (char)97>, Foo<2, (char)98>,
Bar<1, (char)97>, Bar<1, (char)98>, Bar<2, (char)97>, Bar<2, (char)98> >
UPD2: 只有 clang 可以编译这段代码。似乎 msvc 和 gcc 中存在错误,因为即使使用 -pedantic-errors
clang 也可以构建此代码
UPD3: 现在gcc也可以编译了
我有几个类:
template <int,char>
class Foo{};
template <int,char>
class Bar{};
我想得到所有带有几个参数的组合,像这样:
// {1, 2}, {'a', 'b'}
using CartesianProduct = mp_list<Foo<1,'a'>, Foo<1,'b'>,...,Bar<2,'b'>>;
我可以将模板参数更改为类型并使用 std::integral_constant
,如果它不能用数字常量完成的话。
我发现了一个类似的问题,但难度要大得多。 Creating all template permutations with MPL。我想,我的情况有更好更简单的解决方案。
这里有一个名为 combine_view
的实现: that I used in my library https://gitlab.com/correaa/boost-covariant
也许现在MP11库里有更好的东西了。
这是我的提炼版本:
#include<boost/mpl/vector.hpp>
#include<boost/mpl/set.hpp>
#include<boost/mpl/fold.hpp>
#include<boost/mpl/zip_view.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/pop_front.hpp>
namespace boost{
namespace mpl{
template <class Seq, class ItrSeq>
class SequenceCombiner{
template < class _Seq = mpl::vector<int_<1> > >
struct StateSeq{
typedef typename pop_front<_Seq>::type sequence;
typedef typename mpl::at<_Seq, int_<0> >::type state;
typedef _Seq type;
};
template < class _Seq, class _State >
struct set_state{
typedef StateSeq< typename push_front<_Seq, _State>::type > type;
};
struct NextOp {
template<typename Out, typename In, typename Enable = typename Out::state>
class apply{
using seq = typename Out::sequence;
using new_state = typename Out::state;
using in_seq = typename mpl::at<In,int_<0> >::type;
using in_itr = typename mpl::at<In,int_<1> >::type;
using new_seq = typename mpl::push_back<seq, in_itr>::type;
public:
typedef typename set_state<new_seq, int_<0> >::type type;
};
template<typename Out, typename In>
class apply<Out,In,mpl::int_<1> >{
typedef typename Out::sequence seq;
typedef typename Out::state state;
typedef typename mpl::at<In,int_<0> >::type in_seq;
typedef typename mpl::at<In,int_<1> >::type in_itr;
typedef typename mpl::begin<in_seq>::type Itr_begin;
typedef typename mpl::next<in_itr>::type Itr_next;
typedef typename mpl::end<in_seq>::type Itr_end;
typedef typename mpl::if_<
boost::is_same<Itr_next,Itr_end>,
typename mpl::push_back<seq,Itr_begin>::type,
typename mpl::push_back<seq,Itr_next>::type
>::type new_seq;
typedef typename mpl::if_<boost::is_same<Itr_next,Itr_end>,
mpl::int_<1>,
mpl::int_<0>
>::type new_state;
public:
typedef typename set_state<new_seq, new_state>::type type;
};
};
typedef typename mpl::fold<
typename mpl::zip_view<mpl::vector<Seq, ItrSeq> >::type,
StateSeq<>,
NextOp
>::type StateResult;
public:
typedef typename mpl::if_<
boost::is_same<typename StateResult::state, int_<1> >,
typename mpl::transform<Seq, mpl::end<_1> >::type,
typename StateResult::sequence
>::type next;
};
template<typename Seq, typename Itrs>
struct combine_iterator{
typedef mpl::forward_iterator_tag category;
typedef Seq seq;
typedef typename transform<Itrs, deref<_1> >::type type;
};
template <class Seq, class Pos>
struct next<typename mpl::combine_iterator<Seq, Pos>>{
typedef typename mpl::SequenceCombiner<Seq,Pos>::next next_Pos;
typedef boost::mpl::combine_iterator<Seq, next_Pos> type;
};
template<class Seq>
class combine_view{
using Pos_begin = typename mpl::transform<Seq, mpl::begin<_1> >::type;
using Pos_end = typename mpl::transform<Seq, mpl::end<_1> >::type;
public:
using begin = combine_iterator<Seq, Pos_begin>;
using end = combine_iterator<Seq, Pos_end>;
using type = combine_view;
};
} // mpl
} // boost
使用示例:
using boost::mpl::combine_view;
using product = combine_view<
boost::mpl::list<
boost::mpl::list<double, int, std::string>,
boost::mpl::list<double, int>,
boost::mpl::list<std::string, char>
>
>::type;
static_assert( boost::mpl::size<product>::value == 12 );
为了好玩,我尝试使用可变参数模板 std::integer_sequence
和 std::tuple_cat
手动实现 并且真的很惊讶很容易让它工作:基于简单的任意长度的数组
constexpr std::array<int,2> t1 = {1, 2};
constexpr std::array<char,3> t2 = {'a', 'b', 'c'};
并给定 可变参数模板 classes (例如 Foo
和 Bar
)它自己生成所有可能的排列并将它们合并到可以定义方便别名的 std::tuple
(或 boost::mpl::list
)数据类型:
using SomeAlias = typename AllClassesAllPermutations<t1.size(), t2.size(), t1, t2, Foo, Bar>::type;
不熟悉
Boost::MPL
我试着远离它:我的第一个版本其实是基于std::tuple
如果你想要
boost::mpl::list
而不是std::tuple
这可以很容易地用另一个 可变模板转换函数
下一节将详细介绍如何实现这一点!
为此,我写了一个 class,它基于一些模板参数,例如上述 int
和 char
参数的组合以及相应的 template template class
- 创建一个 std::tuple
,其中包含 单个 template template class
的 int
和 char
数组的所有排列。这是通过创建两个包含成对排列的排列向量来完成的。例如。两个输入数组 t1_in = {1, 2}
和 t2_in = {'a', 'b', 'c'};
使用函数 duplicateArray
扩展为 t1 = {1, 1, 1, 2, 2, 2}
和 t2 = {'a', 'b', 'c', 'a', 'b', 'c'}
,然后创建一个 std::integer_sequence
以便两者可以融合到一个模板 T<t1[I], t2[I]>
中,与 std::integer_sequence
结合为您提供单个 class:
template <std::size_t I1, std::size_t I2, std::array<int,I1> const& t1_in, std::array<char,I2> const& t2_in, template <int, char> class T>
class SingleClassAllPermutations {
private:
template <std::size_t K, std::size_t I, typename TA, std::size_t J>
static constexpr auto duplicateArray(std::array<TA, J> const& arr_in) {
std::array<TA, I*J*K> arr_out {0};
std::size_t l {0};
for (std::size_t i = 0; i < I; ++i) {
for (std::size_t j = 0; j < J; ++j) {
for (std::size_t k = 0; k < K; ++k) {
arr_out[l] = arr_in[j];
++l;
}
}
}
return arr_out;
}
static constexpr std::size_t N = I1*I2;
static constexpr std::array<int,N> t1 = duplicateArray<I2,1>(t1_in);
static constexpr std::array<char,N> t2 = duplicateArray<1,I1>(t2_in);
static constexpr auto index_seq = std::make_index_sequence<N>{};
template <std::size_t... I>
static constexpr auto getTuple(std::index_sequence<I...>) {
return std::make_tuple(T<t1[I], t2[I]>() ...);
}
public:
static constexpr auto tup = getTuple(index_seq);
};
然后我创建了一个 可变参数模板,它可能需要几个不同的 template template classes
(只要它们的模板参数是 int
和 char
分别)作为额外的输入参数,然后将各个排列的内部元组 tup
与 std::tuple_cat
合并,以创建包含 [=30 的所有可能排列 的 元组=] 和 char
数组以及不同的可变参数 template template class
es:
template <std::size_t I1, std::size_t I2, std::array<int,I1> const& t1, std::array<char,I2> const& t2, template <int,char> class... Ts>
class AllClassesAllPermutations {
public:
static constexpr auto getTuple() {
return std::tuple_cat(SingleClassAllPermutations<I1,I2,t1,t2,Ts>::tup ...);
}
using type = decltype(getTuple());
};
现在可以在命名空间内定义全局 constexpr
数组和方便的 alias
例如
namespace some_namespace {
constexpr std::array<int,2> t1 = {1, 2};
constexpr std::array<char,3> t2 = {'a', 'b', 'c'};
using SomeInstantiation = typename AllClassesAllPermutations<t1.size(), t2.size(), t1, t2, Foo, Bar>::type;
}
这样就可以重复使用上面的模板来生成不同的数据类型。
然后模板将其扩展为所有可能的排列,在上述情况下为
std::tuple<Foo<1,'a'>, Foo<1,'b'>, Foo<1,'c'>, Foo<2,'a'>, Foo<2,'b'>, Foo<2,'c'>,
Bar<1,'a'>, Bar<1,'b'>, Bar<1,'c'>, Bar<2,'a'>, Bar<2,'b'>, Bar<2,'c'>>
最后如果你想要一个boost::mpl::list
代替你可以引入一个转换函数比如
template <class... Ts>
static constexpr auto tupleToMplList(std::tuple<Ts...>) {
return boost::mpl::list<Ts...>{};
}
您再次使用 decltype
而不是 std::tuple
,这导致数据类型
boost::mpl::list<Foo<1,'a'>, Foo<1,'b'>, Foo<1,'c'>, Foo<2,'a'>, Foo<2,'b'>, Foo<2,'c'>,
Bar<1,'a'>, Bar<1,'b'>, Bar<1,'c'>, Bar<2,'a'>, Bar<2,'b'>, Bar<2,'c'>>
终于自己想通了
using namespace boost::mp11;
template <typename C1, typename C2>
struct Foo{};
template <typename C1, typename C2>
struct Bar{};
template <template <typename...> typename... F>
using mp_list_q = mp_list<mp_quote<F>...>;
using TypeList = mp_product<mp_invoke_q,
mp_list_q<Foo, Bar>,
mp_list_c<int, 1, 2>,
mp_list_c<char, 'a', 'b'>>;
结果:
boost::mp11::mp_list<
Foo<std::integral_constant<int, 1>, std::integral_constant<char, (char)97> >,
Foo<std::integral_constant<int, 1>, std::integral_constant<char, (char)98> >,
Foo<std::integral_constant<int, 2>, std::integral_constant<char, (char)97> >,
Foo<std::integral_constant<int, 2>, std::integral_constant<char, (char)98> >,
Bar<std::integral_constant<int, 1>, std::integral_constant<char, (char)97> >,
Bar<std::integral_constant<int, 1>, std::integral_constant<char, (char)98> >,
Bar<std::integral_constant<int, 2>, std::integral_constant<char, (char)97> >,
Bar<std::integral_constant<int, 2>, std::integral_constant<char, (char)98> > >
它使用 std::integral_constant
作为参数,但它简单而简短。 Try it here
更新:我发现了如何使用整数本身!
template <int, char>
struct Foo{};
template <int, char>
struct Bar{};
template <template <auto...> typename F>
struct mp_quote_c
{
template <typename... C>
using fn = F<C::value...>;
};
template <template <auto...> typename... F>
using mp_list_qc = mp_list<mp_quote_c<F>...>;
using TypeList = mp_product<mp_invoke_q,
mp_list_qc<Foo, Bar>,
mp_list_c<int, 1, 2>,
mp_list_c<char, 'a', 'b'>>;
结果:
boost::mp11::mp_list<
Foo<1, (char)97>, Foo<1, (char)98>, Foo<2, (char)97>, Foo<2, (char)98>,
Bar<1, (char)97>, Bar<1, (char)98>, Bar<2, (char)97>, Bar<2, (char)98> >
UPD2: 只有 clang 可以编译这段代码。似乎 msvc 和 gcc 中存在错误,因为即使使用 -pedantic-errors
UPD3: 现在gcc也可以编译了