Reputation: 337
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:
operator=(Scalar)
operator=(Type)
operator+(Type)
operator-(Type)
operator*(Type)
operator/(Type)
operator*(Scalar)
operator/(Scalar)
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
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
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
Am I correct?
Upvotes: -1
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