'Covariant return type on Eigen Matrix for base class method

Suppose that I have two different solvers that both will be called at run time.

  • I want to call solvers' api and get resulted Eigen matrix through the base class pointer.
  • The solved matrix size are selected from a few known values depending on some runtime variable. I need to use compiled time fixed size matrix in this case.

The solver derived class definitions are as follow.

template <int VarSize>
class SolverA : Solver {
 public:
  SolverA() {}

  bool Solve() override {
    // SolverA specific implementations.
  }
  const Eigen::Matrix<double, VarSize, 1>& solution() override {
    return solution_;
  }
 private:
  Eigen::Matrix<double, VarSize, 1> solution_;
}


template <int VarSize>
class SolverB : Solver {
 public:
  SolverB() {}

  bool Solve() override {
    // SolverB specific implementations.
  }
  const Eigen::Matrix<double, VarSize, 1>& solution() override {
    return solution_;
  }
 private:
  Eigen::Matrix<double, VarSize, 1> solution_;
}

Now the problem is how I can write the base class solution() method to get the resulted Eigen matrix, since the template parameter VarSize are not known in the base declaration.

class Solver {
 public:
  virtual bool Solve() = 0;
  // not covariant type, won't compile.
  // virtual const Eigen::VectorXd& solution() = 0;
}

constexpr int kEasyProbSize = 5;
constexpr int kHardProbSize = 20;

int main() {
  Solver* solver;

  if (GetComplexity() > 30) {
    solver = &SolverB<kHardProbSize>();
  } else {
    solver = &SolverA<kEasyProbSize>();
  }
  solver->Solve();
  std::cout << solver->solution() << std::endl;
}

Some thoughts:

  • Eigen::Matrix<double, VarSize, 1> does not derive from Eigen::VectorXd so it cannot override.
  • I also cannot use const Eigen::MatrixBase<Derived>& since there is no that derived information and virtual method won't allow template.
  • I need to call from base class pointer so I cannot make the base class a template class.
  • The solved solution_ is already allocated and doesn't make sense to return a copy or converted to a dynamic size matrix.

Is this fixed size matrix getting from base class pointer even possible?



Solution 1:[1]

The easiest solution would be to just store a VectorXd solution_; inside Solver itself. But if you insist on storing the actual solution vector only in the derived classes, you can have solution() return an Eigen::Ref<const Eigen::VectorXd> which can be created with just moving a pointer and a few integers:

class Solver {
 public:
  virtual bool Solve() = 0;
  // not covariant type, won't compile.
  using VecRef = Eigen::Ref<const Eigen::VectorXd> const;
  virtual VecRef solution() = 0;
};


template <int VarSize>
class SolverA : public Solver {
 public:
  SolverA() {}

  bool Solve() override {
    // SolverA specific implementations.
    return true;
  }
  Solver::VecRef solution() override {
    return solution_;
  }
 private:
  Eigen::Matrix<double, VarSize, 1> solution_;
};


template <int VarSize>
class SolverB : public Solver {
 public:
  SolverB() {}

  bool Solve() override {
    // SolverB specific implementations.
    return true;
  }
   VecRef solution() override {
    return solution_;
  }
 private:
  Eigen::Matrix<double, VarSize, 1> solution_;
};

Godbolt demo: https://godbolt.org/z/MqaofsoK9

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 chtz