vsoftco vsoftco - 1 month ago 8
C++ Question

Writing a type trait for detecting Matrix Expressions in Eigen

I am trying (and failing) to write a type trait that detects Eigen expressions. In other words, I'd like to be able to detect things like

A * A + B
etc. where
A
and
B
are
Eigen
matrices/vectors. Currently I'm doing this:

template<typename T>
struct is_matrix_expression : std::false_type
{
};

template<typename Derived> // specialization
struct is_matrix_expression<Eigen::MatrixBase<Derived>> :
std::true_type
{
};


Note that
Eigen::MatrixBase<Derived>
is a (template) base for all possible Eigen expressions (such as
decltype(A * A + B)
etc). However, the general template is being picked out, as it is a better match for something like
decltype(A * A + B)
, and not the
MatrixBase<Derived>
specialization.

How can I somehow enforce the specialization to be picked out? Or, in other words, enable the specialization for all possible children of
Eigen::MatrixBase<Derived>
? I played a bit with SFINAE on
std::is_base_of
, but that requires an explicit type and not a template where the type of the expression (in this case
Derived
) is not known in advance.

Equivalently, how can I detect whether a type
X
is a child of
Base<T>
, for some type
T
?

Answer

This detects if something inherits from bob_template<T>:

template<class T>
struct bob_template {};

template<class T>
constexpr std::true_type is_bob_f( bob_template<T> const& ) { return {}; }

namespace details {
    template<template<class...>class Z, class, class...Ts>
    struct can_apply:std::false_type{};
    template<template<class...>class Z, class...Ts>
    struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;

template<class T>
using is_bob_r = decltype( is_bob_f( std::declval<T const&>() ) );

template<class T>
using is_bob = can_apply< is_bob_r, T >;

live example.

C++20 has is_detected which is similar to can_apply above.

std::void_t is C++14, but easy to write in C++11.

To read the above in english:

is_bob<T> is true if and only if you can invoke is_bob_r<T>.

is_bob_r can be invoked if is_bob_f( T const& ) is a valid call.

is_bob_f has an overload for is_bob_f( bob_template<T> const& ) only.

can_apply<Z, T> is (derived from) true_type if Z<T> is valid, and (derived from) false_type otherwise.

So is_bob<T> is true if and only if T can be deduced into bob_template<U> for some U. Which basically means bob_template<U> is a (public) base class of T.