模板元编程自己的表达式
Template Metaprogramming Eigen Expressions
假设我有一些(短)向量,我在编译时知道它的长度,还有另一个更长的向量,我在编译时不知道它的长度。我可以这样写:
template<int N>
Eigen::ArrayXd do_transformation(Eigen::Array<double,N,1> short_vec, Eigen::ArrayXd long_vec){
Eigen::ArrayXd return_vector(long_vec.size());
for(int i=0; i<N; i++){
return_vector+=short_vec(i)*long_vec.pow(2-i);
}
return return_vector;
}
有没有一种方法可以使用表达式模板构造该总和 而无需 写出:
template<>
Eigen::ArrayXd do_transformation<1>(Eigen::Array<double,1,1> short_vec, Eigen::ArrayXd long_vec){
return short_vec(0)*long_vec.pow(2);
}
template<>
Eigen::ArrayXd do_transformation<2>(Eigen::Array<double,2,1> short_vec, Eigen::ArrayXd long_vec){
return short_vec(0)*long_vec.pow(2)+short_vec(1)*long_vec.pow(1);
}
对于 N 的每个值?
理想情况下,这可以在 c++11
中完成。 really awesome 是 return 某种 Eigen 表达式的函数,这样我就可以做类似的事情:
long_vec+do_transformation(short_vec,long_vec)
并让 Eigen 指示编译器生成只遍历向量一次的代码。
如果你可以使用 C++17,我想你可以使用辅助函数(dth()
,在下面的示例中)、std::make_index_sequence
/std::index_sequence
和模板折叠。
如下(注意:代码未测试):
template <int N, std::size_t ... Is>
Eigen::ArrayXd dth (Eigen::Array<double,N,1> short_vec,
Eigen::ArrayXd long_vec,
std::index_sequence<Is...>)
{ return ((short_vec(Is) * long_vec.pow(2-Is)) + ...); }
template <int N>
Eigen::ArrayXd do_transformation (Eigen::Array<double,N,1> short_vec,
Eigen::ArrayXd long_vec)
{ return dth(short_vec, long_vec, std::make_index_sequence<N>{}); }
如果你不会用C++17,但是你可以用C++14,辅助函数会变得更复杂一点(如果你愿意,我可以写一个例子)因为你不会用模板折叠。
template <int N, std::size_t ... Is>
Eigen::ArrayXd dth (Eigen::Array<double,N,1> short_vec,
Eigen::ArrayXd long_vec,
std::index_sequence<Is...>)
{
using unused = int[];
Eigen::ArrayXd rv(long_vec.size());
(void)unused { 0, (rv += short_vec(Is) * long_vec.pow(2-Is), 0)... };
return rv;
}
对于 C++11 解决方案,您需要创建 std::index_sequence
/std::make_index_sequence
的替代品。
在 c++11 中你可以做一个很好的旧递归:
template <int I>
struct do_transformation_impl {
template<int M>
static auto run(const Array<double,M,1> &short_vec, const ArrayXd &long_vec)
-> decltype(short_vec(I)*long_vec.pow(2-I) + do_transformation_impl<I-1>::run(short_vec,long_vec))
{
return short_vec(I)*long_vec.pow(2-I) + do_transformation_impl<I-1>::run(short_vec,long_vec);
}
};
template <>
struct do_transformation_impl<0> {
template<int M>
static auto run(const Array<double,M,1> &short_vec, const ArrayXd &long_vec)
-> decltype(short_vec(0)*long_vec.pow(2))
{
return short_vec(0)*long_vec.pow(2);
}
};
template<int N>
auto do_transformation(const Array<double,N,1> &short_vec, const ArrayXd &long_vec)
-> decltype(do_transformation_impl<N-1>::run(short_vec,long_vec))
{
return do_transformation_impl<N-1>::run(short_vec,long_vec);
}
演示:https://godbolt.org/z/4xPdVm
在对 max66 的答案进行一些调整后,两种解决方案都产生相同的代码:https://godbolt.org/z/Ha5qMa
假设我有一些(短)向量,我在编译时知道它的长度,还有另一个更长的向量,我在编译时不知道它的长度。我可以这样写:
template<int N>
Eigen::ArrayXd do_transformation(Eigen::Array<double,N,1> short_vec, Eigen::ArrayXd long_vec){
Eigen::ArrayXd return_vector(long_vec.size());
for(int i=0; i<N; i++){
return_vector+=short_vec(i)*long_vec.pow(2-i);
}
return return_vector;
}
有没有一种方法可以使用表达式模板构造该总和 而无需 写出:
template<>
Eigen::ArrayXd do_transformation<1>(Eigen::Array<double,1,1> short_vec, Eigen::ArrayXd long_vec){
return short_vec(0)*long_vec.pow(2);
}
template<>
Eigen::ArrayXd do_transformation<2>(Eigen::Array<double,2,1> short_vec, Eigen::ArrayXd long_vec){
return short_vec(0)*long_vec.pow(2)+short_vec(1)*long_vec.pow(1);
}
对于 N 的每个值?
理想情况下,这可以在 c++11
中完成。 really awesome 是 return 某种 Eigen 表达式的函数,这样我就可以做类似的事情:
long_vec+do_transformation(short_vec,long_vec)
并让 Eigen 指示编译器生成只遍历向量一次的代码。
如果你可以使用 C++17,我想你可以使用辅助函数(dth()
,在下面的示例中)、std::make_index_sequence
/std::index_sequence
和模板折叠。
如下(注意:代码未测试):
template <int N, std::size_t ... Is>
Eigen::ArrayXd dth (Eigen::Array<double,N,1> short_vec,
Eigen::ArrayXd long_vec,
std::index_sequence<Is...>)
{ return ((short_vec(Is) * long_vec.pow(2-Is)) + ...); }
template <int N>
Eigen::ArrayXd do_transformation (Eigen::Array<double,N,1> short_vec,
Eigen::ArrayXd long_vec)
{ return dth(short_vec, long_vec, std::make_index_sequence<N>{}); }
如果你不会用C++17,但是你可以用C++14,辅助函数会变得更复杂一点(如果你愿意,我可以写一个例子)因为你不会用模板折叠。
template <int N, std::size_t ... Is>
Eigen::ArrayXd dth (Eigen::Array<double,N,1> short_vec,
Eigen::ArrayXd long_vec,
std::index_sequence<Is...>)
{
using unused = int[];
Eigen::ArrayXd rv(long_vec.size());
(void)unused { 0, (rv += short_vec(Is) * long_vec.pow(2-Is), 0)... };
return rv;
}
对于 C++11 解决方案,您需要创建 std::index_sequence
/std::make_index_sequence
的替代品。
在 c++11 中你可以做一个很好的旧递归:
template <int I>
struct do_transformation_impl {
template<int M>
static auto run(const Array<double,M,1> &short_vec, const ArrayXd &long_vec)
-> decltype(short_vec(I)*long_vec.pow(2-I) + do_transformation_impl<I-1>::run(short_vec,long_vec))
{
return short_vec(I)*long_vec.pow(2-I) + do_transformation_impl<I-1>::run(short_vec,long_vec);
}
};
template <>
struct do_transformation_impl<0> {
template<int M>
static auto run(const Array<double,M,1> &short_vec, const ArrayXd &long_vec)
-> decltype(short_vec(0)*long_vec.pow(2))
{
return short_vec(0)*long_vec.pow(2);
}
};
template<int N>
auto do_transformation(const Array<double,N,1> &short_vec, const ArrayXd &long_vec)
-> decltype(do_transformation_impl<N-1>::run(short_vec,long_vec))
{
return do_transformation_impl<N-1>::run(short_vec,long_vec);
}
演示:https://godbolt.org/z/4xPdVm
在对 max66 的答案进行一些调整后,两种解决方案都产生相同的代码:https://godbolt.org/z/Ha5qMa