1

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!

raululm
  • 105
  • 1
  • 1
  • 10
  • 4
    What is the signature of `stepper::do_step`? – Botje May 06 '20 at 14:49
  • 3
    Maybe: `stepper.do_step(std::ref(*this), state, t, dt);` – Jarod42 May 06 '20 at 14:52
  • Is a template signature, but in official documentation you can see this: runge_kutta4< state_type > rk; rk.do_step( sys1 , inout , t , dt); nevertheless, this worked perfectly in a similar class with no inheritance implemented (just refactored the code to encapsulate the beahaviour in one base class). – raululm May 06 '20 at 15:06
  • 1
    @Jarod's solution [works](https://wandbox.org/permlink/MkwUK5wCUdbNQ3GR). Without using `std::ref`, passing `*this` evidently causes [object slicing](https://stackoverflow.com/questions/274626/what-is-object-slicing). – Paul Sanders May 06 '20 at 15:08
  • I have no computer now, I’ll try as soon as I can. Thank you both – raululm May 06 '20 at 15:09
  • Jarod, it worked perfectly. Would you like to post it as answer? or I accept the one provided by Daniel Langr? Anyway, thank you both. – raululm May 07 '20 at 14:53

1 Answers1

1

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.

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • I mark this one as the answer, due to the explication. It allowed me to make the Base class abstract, and the overridable method pure virtual. Thank you to Jarod and Daniel Langr. – raululm May 08 '20 at 11:01