I want to use Eigen with a custom type, which happens to be implementing the expression template pradigm itself:
Basically, I got my base class:
namespace NS
{
template<class Expr>
class MyTypeExpression
{
public:
double eval() const { return static_cast<Expr const&>(*this).eval(); }
};
}
My custom type:
namespace NS
{
class MyType : public MyTypeExpression<MyType>
{
public:
MyType(double value = 0) : m_value(value) {}
template<typename Expr> MyType(const MyTypeExpression<Expr>& expr) : m_value(expr.eval()) {}
~MyType() {}
double eval() const { return m_value; }
template<typename Expr> MyType& operator= (const MyTypeExpression<Expr>& expr) { m_value = expr.eval(); return *this; }
template<typename Expr> MyType& operator+=(const MyTypeExpression<Expr>& expr) { m_value += expr.eval(); return *this; }
template<typename Expr> MyType& operator-=(const MyTypeExpression<Expr>& expr) { m_value -= expr.eval(); return *this; }
template<typename Expr> MyType& operator*=(const MyTypeExpression<Expr>& expr) { m_value *= expr.eval(); return *this; }
template<typename Expr> MyType& operator/=(const MyTypeExpression<Expr>& expr) { m_value /= expr.eval(); return *this; }
private:
double m_value;
};
}
an my operators:
namespace NS
{
template<class Expr> inline const Expr& conj(const NS::MyTypeExpression<Expr>& x) { return *static_cast<const Expr*>(&x); }
template<class Expr> inline const Expr& real(const NS::MyTypeExpression<Expr>& x) { return *static_cast<const Expr*>(&x); }
template<class Expr> inline MyType imag(const NS::MyTypeExpression<Expr>&) { return NS::MyType(); }
template<class Expr> inline MyType abs (const NS::MyTypeExpression<Expr>& x) { return NS::MyType(std::fabs(x.eval())); }
template<class Expr> inline MyType abs2(const NS::MyTypeExpression<Expr>& x) { const double y = x.eval(); return NS::MyType(y*y); }
}
template<class Lhs, class Rhs>
NS::MyTypeBinaryExpression<Lhs,Rhs,NS::OpSum> operator+(const NS::MyTypeExpression<Lhs>& lhs, const NS::MyTypeExpression<Rhs>& rhs)
{ return NS::MyTypeBinaryExpression<Lhs,Rhs,NS::OpSum>(*static_cast<const Lhs*>(&lhs), *static_cast<const Rhs*>(&rhs)); }
template<class Lhs, class Rhs>
NS::MyTypeBinaryExpression<Lhs,Rhs,NS::OpSub> operator-(const NS::MyTypeExpression<Lhs>& lhs, const NS::MyTypeExpression<Rhs>& rhs)
{ return NS::MyTypeBinaryExpression<Lhs,Rhs,NS::OpSub>(*static_cast<const Lhs*>(&lhs), *static_cast<const Rhs*>(&rhs)); }
template<class Lhs, class Rhs>
NS::MyTypeBinaryExpression<Lhs,Rhs,NS::OpProd> operator*(const NS::MyTypeExpression<Lhs>& lhs, const NS::MyTypeExpression<Rhs>& rhs)
{ return NS::MyTypeBinaryExpression<Lhs,Rhs,NS::OpProd>(*static_cast<const Lhs*>(&lhs), *static_cast<const Rhs*>(&rhs)); }
template<class Lhs, class Rhs>
NS::MyTypeBinaryExpression<Lhs,Rhs,NS::OpDiv> operator/(const NS::MyTypeExpression<Lhs>& lhs, const NS::MyTypeExpression<Rhs>& rhs)
{ return NS::MyTypeBinaryExpression<Lhs,Rhs,NS::OpDiv>(*static_cast<const Lhs*>(&lhs), *static_cast<const Rhs*>(&rhs)); }
template<class Expr>
std::ostream& operator<<(std::ostream& out, const NS::MyTypeExpression<Expr>& x) { out << x.eval(); return out; }
A simple test works fine:
NS::MyType a(1);
NS::MyType b(2);
std::cout << a + b << std::endl;
But using my type with eigen yeilds a tone of errors:
typedef Eigen::Matrix< NS::MyType, Eigen::Dynamic, 1 > Vector;
Vector a(10);
std::cout << a << std::endl;
/usr/include/eigen3/Eigen/src/Core/IO.h:175:14: error: no match for ‘operator<<’ (operand types are ‘std::stringstream’ {aka ‘std::__cxx11::basic_stringstream<char>’} and ‘const Scalar’ {aka ‘const NS::MyType’})
175 | sstr << m.coeff(i,j);
I did implemented a Eigen::NumTraits for my type but maybe I did it wrong...
namespace Eigen
{
template<> struct NumTraits<NS::MyType> : NumTraits<double> // permits to get the epsilon, dummy_precision, lowest, highest functions
{
typedef NS::MyType Real;
typedef NS::MyType NonInteger;
typedef NS::MyType Nested;
enum {
IsComplex = 0,
IsInteger = 0,
IsSigned = 1,
RequireInitialization = 1,
ReadCost = 1,
AddCost = 3,
MulCost = 3
};
};
}
I will attach a full minimal working example.
Has anyone ever experienced such an issue with eigen ?
Edit: I copy-pasted the wrong base class, it is now corrected