Search code examples
c++classinheritanceboostodeint

Base class virutal method is being called, instead of child's override implementation


I am writting an ODE integrator with BOOST::ODEINT. It requires that the class with the logic has the operator() overloaded with this signature "void operator()(const std::vector &x, std::vector &dxdt, double t)".

I pretended to write an abstract class which encapsulates the common model logic and the integrator logic, and base child will implement some specific aspects (like the system needed by ODEINT).

I've found myself that could not implement as an abstract class, because the code is not compiling, as could not write the template of the integrator. So I removed all pure virtual functions, the code then compiled well, but I always get the base class method called instead of child's implementation.

#include <vector>
#include <iostream>
#include <boost/numeric/odeint.hpp>

class BaseClass {
public:
    virtual void ode(const std::vector<double> &x, std::vector<double> &dxdt, double t) {
        std::cout << "Not expected." << std::endl;
    };

    void operator() (const std::vector<double> &x, std::vector<double> &dxdt, double t) {
        std::cout << "BaseClass" << std::endl;
        this->ode(x, dxdt, t);
    };

    void integrate() {
        double t = 0, dt = 0.5;
        std::vector<double> state(2, 0.0);
        boost::numeric::odeint::runge_kutta4<std::vector<double>> stepper;
        stepper.do_step(*this, state, t, dt);
    };
};

class DerivedClass : public BaseClass {
public:
    virtual void ode(const std::vector<double> &x, std::vector<double> &dxdt, double t) {
        std::cout << "Expected." << std::endl;
    };
};

void main() {
    DerivedClass d;
    d.integrate();
}

This produces

Base Class
Not expected

I also tried to make virtual de method operator(), but produces same output.

While I am writting this, I think I am going to separate the logic of the integrator from the model, but I would like to know what is this happening.

Thank you all!


Solution

  • Quoting from the relevant documentation:

    In odeint all system functions and observers are passed by value. For example, if you call a do_step method of a particular stepper or the integration functions, your system and your stepper will be passed by value.

    The problem is that dynamic polymorphism does work only when you invoke virtual functions via pointers/references. To accomplish this, wrap *this with std::ref as suggested in comments (or boost::ref as suggested by the original documentation).

    Note that this work since std::reference_wrapper defines a function-call operator() that invokes the same for the underlying object. (With a named member function, this approach wouldn't work.)


    The exemplary simple live demo that compares both cases is here.