Search code examples
c++eigenfunctor

C++ Apply a functor that is stored in a vector of shared pointers to Eigen unaryExpr


I have a vector of shared pointers of functor objects: std::vector<std::shared_ptr<MyFunctor>> and I would like to apply one of them to all the elements in an eigen MatrixXd object.

The abstract base class MyFunctor defines a member function operator() that takes and returns a double and is const. Derived classes implement the function.

It would seem that the unaryExpr function would be appropriate, but I can't quite get the syntax correct.

I'm trying to do something like:

mat.unaryExpr( *(vectorOfFunctors[0]) );

I am adding an example with the work-around I found to work.
This is for MSVC 2019 (16.2.5) on Windows 10, 64 bit.

#include <Eigen/Dense>
#include <vector>
#include <memory>

class MyFunctorBase {
public:
    virtual double operator()(double) const = 0;
    virtual std::string getName() const = 0;
};

class MyFunctor : public MyFunctorBase {
public:
    MyFunctor() {};
    std::string getName() const { return "MyFunctor"; }
    double operator()(double d) const { return d + 1; }
};

void func() {
    std::shared_ptr<MyFunctor> mf1(new MyFunctor());
    std::vector<std::shared_ptr<MyFunctorBase>> vectorOfFunctors;
    vectorOfFunctors.push_back(mf1);

    Eigen::MatrixXd A(2, 2);
    A << 1, 2,
         3, 4;

    //Method 1
    //Eigen::MatrixXd t0 = A.unaryExpr(*(vectorOfFunctors[0]));    //Does not compile

    //Method 2
    const MyFunctorBase& mfb = *(vectorOfFunctors[0]);       //Compiles
    Eigen::MatrixXd t2 = A.unaryExpr(std::ref(mfb));
}

Solution

  • Eigen tries to instantiate an instance of the abstract functor you pass into it, hence the compile error. Note that because you dereference the pointer, you are passing in a sliced abstract object into the unaryExpr method. You could just pass in the derived functor directly, or dynamic_cast to the derived type before dereferencing.

    The reason you get the expected behaviour with std::ref, is that it wraps the MyFunctorBase in a reference wrapper, so at the point of instantiation, you don't get the error about creating an abstract type. Later, when the functor is invoked, it implicitly converts to MyFunctorBase&, allowing for the virtual operator() call. Following on from this, you could have a std::vector of reference wrapper objs (std::vector<std::reference_wrapper<MyFunctorBase>>), rather than manually wrapping each element in the container with std::ref.


    Note that Eigen matrices provide iterators that are compatible with STL algorithms, and you can achieve the same goal with std::transform.