Brian Hawkins
Brian Hawkins

Reputation: 2930

Eigen template on scalar type

I'm working on code with a lot of classes that template on a scalar type T and implement methods on containers like std::valarray<T>. I'd like to add support for Eigen containers, but I'm having trouble figuring out a way to template only on the scalar type while maintaining some of Eigen's flexibility.

For example, the Eigen docs suggest using Eigen::DenseBase<Derived>, but that seems to mean I'd have to change all my class defintions to template on the array type Derived and then use Derived::Scalar everywhere. Pretty intrusive just to support another container.

I could also just use Matrix<T,Dynamic,Dynamic>, but this is pretty restrictive. For the cases I care about, I was thinking a reasonable middle ground would be to use Ref<Matrix<T,Dynamic,Dynamic>> since that would cover Matrix and slices thereof with one interface. But this doesn't seem to work, and I'm not sure why. Here's a concrete example of what I mean:

#include<iostream>
#include<Eigen/Dense>

using namespace std;
using namespace Eigen;

double
sum1(const Ref<const Matrix<double,Dynamic,Dynamic>> &m)
{
    double x = 0.0;
    for (int j=0; j<m.cols(); ++j)
        for (int i=0; i<m.rows(); ++i)
            x += m(i,j);
    return x;
}

template <typename T>
T
sum2(const Ref<const Matrix<T,Dynamic,Dynamic>> &m)
{
    T x = 0.0;
    for (int j=0; j<m.cols(); ++j)
        for (int i=0; i<m.rows(); ++i)
            x += m(i,j);
    return x;
}

int main()
{
    Matrix<double,Dynamic,Dynamic> a(2,2);
    a << 0,2,1,3;
    cout << "sum1(a) = " << sum1(a) << endl;            // ok
    cout << "sum2(a) = " << sum2(a) << endl;            // error
    cout << "sum2(a) = " << sum2<double>(a) << endl;    // ok
    return 0;
}

But I get errors when I compile:

$ clang++  -std=c++17 -I/usr/include/eigen3   eig2.cpp   -o eig2
eig2.cpp:33:29: error: no matching function for call to 'sum2'
    cout << "sum2(a) = " << sum2(a) << endl;            // error
                            ^~~~
eig2.cpp:19:1: note: candidate template ignored: could not match 'Ref' against
      'Matrix'
sum2(const Ref<const Matrix<T,Dynamic,Dynamic>> &m)
^
1 error generated.

Is there a way to implement sum2 so that it supports Matrix and Ref and doesn't require explicit template arguments to use it?

Upvotes: 4

Views: 1415

Answers (1)

chtz
chtz

Reputation: 18807

The reason that

template <typename T>
T
sum2(const Ref<const Matrix<T,Dynamic,Dynamic>> &m)

does not work for Matrix<T,Dynamic,Dynamic> is that C++ can't deduce template arguments and do type-conversion at the same time (Ref is a different type than Matrix, but it can be constructed with O(1) effort from it).

One alternative (requiring C++11 or higher) which allows you to keep the function body is:

template<typename Derived, typename T=typename Derived::Scalar>
T sum3(const MatrixBase<Derived> &m) 

If you want to allow Array as well, you can use DenseBase, if you want to allow sparse matrices you can use EigenBase.

However, this is not equivalent to the Ref implementation with explicit type substitution, since if Derived is an expression, it may get evaluated multiple times inside your function.

An alternative is to write

template <typename T>
T
sum_impl(const Ref<const Matrix<T,Dynamic,Dynamic>> &m)
{ /* here comes the actual implementation */ }

template<typename Derived>
typename Derived::Scalar sum4(const Eigen::MatrixBase<Derived>& m)
{
    return sum_impl<typename Derived::Scalar>(m);
}

Upvotes: 7

Related Questions