Search code examples
c++staticvirtual

Static Virtual workaround in GSL


Hello I am trying to write a small program to simulate dynamical systems using the differential equation package from the GNU Scientific Library. The problem is not specific to the GSL but I am just giving you all the details

In the current design I want to have an abstract Experiment class, where all the complex functions will be called by the gsl library. The dynamics of the explicit system will be defined by two functions, i.e., func and jacob, that define the specific motion equations and the jacobian respectively. Thus, I want to do all the simulation in the Experiment class and only override the two virtual functions with the specific class which will be inherited by the Experiment.

The problem I have is that as virtual these methods do not compile

error: argument of type ‘int (Experiment::)(double, const double*, double*, void*)’ does not match ‘int (*)(double, const double*, double*, void*)’

If I make these two functions static the program compiles but I am losing the functionality that I want to achieve for specific problems.

Apparently, they cannot be both static and virtual, so does anyone know any workaround to this problem? Are there any suggestions to better approach it?

Thanks in advance.

EDIT: THe code below compiles but they are not virtual

class Experiment
{
public:
    Experiment();
    ~Experiment();

    void setupExperiment();
    static int func(double t, const double y[], double f[], void *params);
    static int jac (double t, const double y[], double *dfdy, double dfdt[], void *params);
};

void Experiment::setupExperiment(){

    double mu = 10;

    gsl_odeiv2_system sys = {func, jac, 2, &mu}; //Here is the problem with virtual functions
}

class aSpecificProblem: public Experiment{

   // I want to implement just the func and jac function which should be virtual above
};

Solution

  • I presume the void* in your function definition is a user-specified callback parameter. In that case, use this parameter to pass a pointer to your object and make your callback a static function. Inside this static function, cast this pointer back to the proper type (Experiment*) and call the non-static version of the function.

    class Experiment
    {
    public:
        Experiment();
        ~Experiment();
    
        void setupExperiment();
        static int static_func(double t, const double y[], double f[], void *params);
        static int static_jac (double t, const double y[], double *dfdy, double dfdt[], void *params);
        virtual int func(double t, const double y[], double f[]);
        virtual int jac (double t, const double y[], double *dfdy, double dfdt[]);
    };
    
    void Experiment::setupExperiment()
    {
        gsl_odeiv2_system sys = {static_func, static_jac, 2, this}; //Here is the problem with virtual functions
    }
    
    int Experiment::static_func(double t, const double y[], double f[], void *params)
    {
      return ((Experiment*)params)->func(t, y, f);
    }
    
    int Experiment::static_jac (double t, const double y[], double *dfdy, double dfdt[], void *params)
    {
      return ((Experiment*)params)->jac(t, y, dfdy, dfdt);
    }
    
    class aSpecificProblem: public Experiment
    {
    public:
        virtual int func(double t, const double y[], double f[]);
        virtual int jac (double t, const double y[], double *dfdy, double dfdt[]);
    };