gianluca
gianluca

Reputation: 337

Check if type is Eigen3 type

Eigen3 and built-in type inter-compatibility

Hello guys. I'm stuck with the problem of writing routines which can handle both Eigen3 types (Matrixes and Arrays) and built-in types. I can best explain this with an example: let's suppose I have a Meter<Type> template class which has the ability to collect statistics during runtime.

The Type class should support the following operators:

Eigen3 types provides all of these operators with two exceptions: first, the operator*(Type) represents a dot-preduct if Type is some subclass of Eigen::MatrixBase and represents a coefficient-wise product if Type is some subclass of Eigen::ArrayBase. I can easily workaround this; second, neither one implements an operator=(Scalar) required for ensuring the correct initialization to zero.

I tried implementing the following functor classes to help me take care of the distinction but I can't get them to work:

some structs to handle the distrinction between built-in types and Eigen3 types:

template < class _Type > struct is_scalar : true_type {
    using Scalar = _Type;
    using Type = _Type;

    static constexpr bool value = true;
};

template < class _Matrix >
struct is_scalar<Eigen::MatrixBase<_Matrix>> : false_type {
    using Scalar = typename Matrix::Scalar;
    static constexpr bool value = false;
};

template < class _Array >
struct is_scalar<Eigen::ArrayBase<_Array>>  : false_type {
    using Scalar = typename Array::Scalar;
    static constexpr bool value = false;
};

the function implementation itself

template < class Scalar, bool is_scalar = Math::is_scalar<Scalar>::value > 
struct set_const_impl;

template < class Scalar >
struct set_const_impl< Scalar, true > {
    static const void run(Scalar &_x, Scalar _y) noexcept { _x = _y; }
};

template < class EigenType >
struct set_const_impl<EigenType, false> {
    template < class Scalar >
    static const void run(Eigen::EigenBase<EigenType> &_x, Scalar _y) noexcept {
        _x.derived().setConstant(_y);
    }
};

template < class Type, class Scalar > void set_const(Type &_x, Scalar _y) noexcept {
    set_const_impl<Type>::run(_x, _y);
}

template < class Type > void set_zero(Type &_x) noexcept {
    set_const_impl<Type>::run(_x, 0);
}

The specialized version set_const_impl<EigenType> never gets instantiated. For example, if I call

Eigen::Matrix<double, 3, 1> m1; 
set_zero(m1);

I get the compiler to complain at the 0 on the line

set_const_impl<Type>::run(_x, 0);

saying that 0 is not implicitly convertible to Eigen::Matrix<double, 3, 1>, which means it picked the set_const_impl<Scalar, true> version of the functor (where both arguments share a the common type Scalar). This also means that my is_scalar construction isn't working in this case, even if I've already used it and tested it on other classes without problems.

I need this behaviour in several other classes and I don't want to explicitly specialize each one of them! Anyone knows what should I do to fix this?

Thanks in advance for any help!

Upvotes: 1

Views: 1246

Answers (3)

taroxd
taroxd

Reputation: 11

I encounted the same problem, and tried to solve it with C++17. Here is my solution.

template<typename Derived>
constexpr bool is_eigen_type_f(const EigenBase<Derived> *) {
    return true;
}
constexpr bool is_eigen_type_f(const void *) {
    return false;
}

template<typename T>
constexpr bool is_eigen_type = is_eigen_type_f(reinterpret_cast<T *>(NULL));

Upvotes: 1

gianluca
gianluca

Reputation: 337

@Jarod42

Thank you, your suggestion has brought some light but I found another option which I think will be pretty solid: I found an implementation of is_scalar<Type> in the namespace std::__1. Now my code reads

template < class Type, bool _is_scalar = std::__1::is_scalar<Type>::value > struct is_scalar;

template < class Type >
struct is_scalar<Type, true> : true_type {
    using Scalar = Type;
};

template < class Type >
struct is_scalar<Type, false> : false_type {
    using Scalar = typename Type::Scalar;
};

and I am correctly able to distinguish built-in and Eigen types! Thank you anyway!

Edit:

By looking at the source code of std::__1::is_scalar I also note that this solution might stand for any kind of container object as long as it provides a type Scalar which is

  • an arithmetic type OR
  • a member pointer OR
  • a pointer OR
  • a null pointer OR
  • an enumeration constant

Am I correct?

Upvotes: -1

Jarod42
Jarod42

Reputation: 217235

Your problem is your traits is_scalar which takes only base classes and not derived classes.

You may try something like:

namespace Helper
{
    template <typename T> std::false_type is_scalar(const Eigen::MatrixBase<T>*);
    template <typename T> std::false_type is_scalar(const Eigen::ArrayBase<T>*);
    std::true_type is_scalar(...);
}

template<typename T>
struct is_scalar : decltype(Helper::is_scalar(std::declval<T*>()))
{};

Upvotes: 1

Related Questions