eigen custom type expression template type issue

46 Views Asked by At

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

0

There are 0 best solutions below