Question

I trying do write a C++ template-class which should be able to deal with "simple" types and with "Eigen::MatrixBase"-like types via the same interface. I manage to get thie desired behavior with two different simple types, but struggle to squeeze the Eigen-syntax into my minimal-example... Maybe someone can give me an advice?

While looking around, this comes close to what i wanna have -- without Eigen. This looks similar as well.

#ifndef __MINIMAL_H__
#define __MINIMAL_H__

#include <Eigen/Core>

// base-class defining and interface + common functions
// child-classes with possible overloads. does not work to
// define basic eigen-types as template specialization parameter

template<typename T>
class MinimalBase
{
    public:
        MinimalBase(const T& val)
            :val(val){};
        T val;
        // pure virtual interface
        virtual void setZero() = 0;
        // the common functionality
        void increase() { val = val*6; };
        void decrease() { val = val/7; };
};


// again pure virtual, so that the compiler will not instantiate this when
// trying to deduce the correct template specialization
template<typename T>
class Minimal : public MinimalBase<T>
{
    public:
        Minimal(const T& val)
            :MinimalBase<T>(val){};
        virtual void setZero() = 0;
        // the rest of the common functionality is inherited from MinimalBase
};

// one spezialization for "int"
template<>
class Minimal<int> : public MinimalBase<int>
{
    public:
        Minimal()
            :MinimalBase<int>(4){};
        virtual void setZero() { val = 0; };
};

// this one is actually shorter...
template<>
class Minimal<short> : public MinimalBase<short>
{
    public:
        Minimal()
            :MinimalBase<short>(1){};
        virtual void setZero() { val = 0; };
        void increase() { val = val*3; };
};

// and the eigen-one (at best limited to vector-like matrices)... how to do this?
template<class Derived>
class Minimal<Eigen::MatrixBase<Derived> > : public MinimalBase<Eigen::MatrixBase<Derived> >
{
        public:
        Minimal<Eigen::MatrixBase<Derived> >()
            :MinimalBase<Eigen::MatrixBase<Derived> >(Eigen::MatrixBase<Derived>::Zero()){};
        virtual void setZero() { this->val.setZero(); };
};
#endif

The last block, with the Eigen stuff, does not compile. I'm more lost in the general direction how to solve this, the specific syntax is not clear to me. Using this header the following lines will not compile in your generic minimal-example-main-cpp (short of the Eigen-stuff):

Minimal<int>A;
Minimal<short>B;
// this does not work:
Minimal<Eigen::Vector2f>C;

std::cerr << "before: " << A.val << " " << B.val << "\n";
A.increase();
A.decrease();
B.increase();
B.setZero()
std::cerr << "after: " << A.val << " " << B.val << "\n";

The Compiler error message looks like this:

/home/joe/test/test.cpp: In function ‘int main()’:
/home/joe/test/test.cpp:36:29: error: no matching function for call to ‘Minimal<Eigen::Matrix<float, 2, 1> >::Minimal()’
     Minimal<Eigen::Vector2f>C;
                             ^
/home/joe/test/test.cpp:36:29: note: candidates are:
In file included from /home/joe/test/test.cpp:7:0:
/home/joe/test/minimal.h:26:9: note: Minimal<T>::Minimal(const T&) [with T = Eigen::Matrix<float, 2, 1>]
         Minimal(const T& val)
         ^
/home/joe/test/minimal.h:26:9: note:   candidate expects 1 argument, 0 provided
/home/joe/test/minimal.h:23:7: note: Minimal<Eigen::Matrix<float, 2, 1> >::Minimal(const Minimal<Eigen::Matrix<float, 2, 1> >&)
 class Minimal : public MinimalBase<T>
       ^
/home/joe/test/minimal.h:23:7: note:   candidate expects 1 argument, 0 provided
/home/joe/test/test.cpp:36:29: error: cannot declare variable ‘C’ to be of abstract type ‘Minimal<Eigen::Matrix<float, 2, 1> >’
     Minimal<Eigen::Vector2f>C;
                             ^
In file included from /home/joe/test/test.cpp:7:0:
/home/joe/test/minimal.h:23:7: note:   because the following virtual functions are pure within ‘Minimal<Eigen::Matrix<float, 2, 1> >’:
 class Minimal : public MinimalBase<T>
       ^
/home/joe/test/minimal.h:29:22: note:       void Minimal<T>::setZero() [with T = Eigen::Matrix<float, 2, 1>]
         virtual void setZero() = 0;
                      ^

Edit: The resulting minimal-example demonstration finally found its way to github

Was it helpful?

Solution

The type Eigen::Vector2f is not equal to Eigen::MatrixBase<Derived> for any type Derived. It inherits Eigen::MatrixBase<Eigen::Vector2f>, but that's not good enough for template specialization matching.

First, let's define a "type trait" that determines whether a type is an Eigen matrix. In C++11:

#include <type_traits>

namespace is_eigen_matrix_detail {
    // These functions are never defined.
    template <typename T>
    std::true_type test(const Eigen::MatrixBase<T>*);
    std::false_type test(...);
}
template <typename T>
struct is_eigen_matrix
    : public decltype(is_eigen_matrix_detail::test(std::declval<T*>()))
{};

Or in C++03:

namespace is_eigen_matrix_detail {
    typedef char yes_type[2];
    typedef char no_type[1];
    template <typename T>
    yes_type test(const Eigen::MatrixBase<T>*);
    no_type test(...);
}
template <typename T>
struct is_eigen_matrix {
    static const bool value =
        (sizeof(is_eigen_matrix_detail::test(static_cast<T*>(0))) ==
         sizeof(is_eigen_matrix_detail::yes_type));
};

Then, a standard enable_if trick can set up a class template specialization that accepts all types and only those types that satisfy the trait.

// Don't bother to define the primary template, and only your
// specializations can ever be used.
template <typename T, typename Enable = void>
class Minimal;

// When not using enable_if tricks, ignore the Enable parameter.
template<>
class Minimal<int> : public MinimalBase<int>
{
    // Just as before.
};

// This specialization only exists when T is or inherits an Eigen::MatrixBase
// specialization.
template <typename T>
class Minimal<T, typename std::enable_if<is_eigen_matrix<T>::value>::type>
    : public MinimalBase<T>
{
public:
    Minimal() : MinimalBase<T>(T::Zero()) {}
    virtual void setZero() { this->val.setZero(); }
};

std::enable_if is C++11. In C++03, either substitute boost::enable_if or just define and use your own:

template <bool Check, typename T = void>
struct enable_if {};
template <typename T>
struct enable_if<true, T> {
    typedef T type;
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top