I am writing a library that stores Eigen expression templates as member variables to do the complicated calculations it needs to do. However, it seems like I'm not able to store or return these expression templates unless they are directly converted in MatrixXd or alike. This forces every step to be saved to a temporary, and ruins the efficiency of the whole design.
Here's a short example that causes the trouble. Holder just holds an Eigen matrix, and Summer takes two holders and outputs the sum of the two matrices they hold when you call get(). The test that follows fails (segfault or std::bad_alloc) when the sum expression template is evaluated into a matrix.
Include file
#ifndef PB_SIMPLE_H
#define PB_SIMPLE_H
#include <Eigen/Dense>
template <class EigenType>
class Holder {
public:
typedef EigenType result_type;
private:
result_type in_;
public:
Holder(const EigenType& in) : in_(in) {}
result_type get() const { return in_; }
};
template <class HoldLeft, class HoldRight>
class Summer {
public:
typedef const typename Eigen::CwiseBinaryOp<
Eigen::internal::scalar_sum_op<double>,
const typename HoldLeft::result_type,
const typename HoldRight::result_type> result_type;
// typedef Eigen::MatrixXd result_type;
private:
HoldLeft left_;
HoldRight right_;
public:
Summer(const HoldLeft& left, const HoldRight& right)
: left_(left), right_(right) {}
result_type get() const { return left_.get() + right_.get(); }
};
typedef Holder<Eigen::MatrixXd> MatrixHolder;
typedef Summer<MatrixHolder, MatrixHolder> MatrixSummer;
#endif /* PB_SIMPLE_H */
simple test
#include "PbSimple.h"
#include <Eigen/Dense>
int main(int, char * []) {
const unsigned int szx=10,szy=3;
Eigen::MatrixXd x(Eigen::MatrixXd::Constant(szx,szy,1));
MatrixHolder vx(x);
Eigen::MatrixXd y(Eigen::MatrixXd::Constant(szx,szy,2));
MatrixHolder vy(y);
MatrixSummer vsum(vx,vy);
auto expr = vsum.get();
MatrixHolder vz(expr); //force evaluation of sum into new matrix, fails here
return 0;
}
- In the include file, if you use the commented out typedef instead, it works fine.
- I suspect the problem is due to a dangling reference, but can't prove it.
This is because
Holder::get
returns a copy of the matrix as a temporary. Then this temporary is stored as a const reference by theCWiseBinaryOp
object returned bySummer::get
, then this temporary is deleted, and finally whenexpr
gets evaluated,expr
is referencing deleted objects. You can fix this issue by makingHolder::get
returns a const reference to the matrix.