0

I'm trying to implement a numerical ODE solver in c++ but I'm having troubles with function pointers (I'm still trying to understand how they works inside classes). I have a parent class (ODEint) and subclasses in which I will implement different possible algorithms to solve an equation. I pass a function pointer to the parent class (the function represents the equation which is independent of the solver) but I need that function in the child class (different solvers threat that equation in different ways).

When I call the function via pointer in the child class I get the error

odeint.cpp:38:13: error: ‘((Euler*)this)->Euler::.ODEint::field’ cannot be used as a member pointer, since it is of type ‘pfunc {aka std::vector ()(double)}’ (this->*field)(y);

Here are classes definitions

typedef vector<double> (*pfunc)(double*);

class ODEint {
protected:
    double h;
    int neq;
    double* init_cond;
    int nsteps;
    string method;
    vector<vector<double>> y;
    pfunc field;
public:
    ODEint(int neq, int nsteps, pfunc);
    void setInitCond(double* init_cond);
    void solveEq();
    virtual vector<double> advance(double h, double *y);
};

class Euler: public ODEint {
public:
    Euler(int neq, int nsteps, pfunc, double h);
    vector<double> advance(double h, double *y);
};

And here is part of the classes implementation

ODEint::ODEint(int neq, int nsteps, pfunc field){
this->neq = neq;
this->nsteps = nsteps;
this->y.resize(nsteps);
this->field = field;
for (int i = 0; i < nsteps; i++){
    this->y[i].resize(neq);
}
}

Euler::Euler(int neq, int nsteps, pfunc field, double h) : ODEint(neq, nsteps, field){
this->h = h;
}

void ODEint::solveEq(){
int n;
cout << "Strarting solver..." << endl;
vector<double> x;
for (n = 0; n < this->nsteps; n++){
    x = y[n];
    y[n+1] = this->advance(this->h, &x[0]);
}
cout << "Solution termined. Nsteps: " << n << endl;
}

vector<double> Euler::advance(double h, double *y){
vector<double> ynext; ynext.resize(this->neq);
vector<double> f; f.resize(this->neq);
(this->*field)(y); <----------------------------------------------  here is the problem
for (int i = 0; i < this->neq; i++){
    ynext[i] = y[i] + h*f[i];
}
}

Finally here is the main

vector<double> field(double *y){
vector<double> vf;
vf[0] = -y[0];
vf[1] = -y[1];
return vf;
}

int main(){

double init_cond[2] = {1.0, 2.0};
const int neq = 1;

Euler prova(neq, (int)1e4, field, 1e-4);
prova.setInitCond(&init_cond[0]);
prova.solveEq();
return 0;
}

I know there may be other problems but I'm still learning c++ and actually the priority is to understand the reason of this error. Thank you in advance and sorry if the code is a bit confused but as I said previously I'm a kind of beginner.

  • 3
    FYI -- 90% or more of your code has nothing to do with the error you're receiving. Create a simple class, a function pointer, a member that points to that function, and attempt to call that function. That's how you create a [mcve], like [this](http://coliru.stacked-crooked.com/a/73f74c7602523146) – PaulMcKenzie May 29 '20 at 13:02
  • 2
    `pFunc` is not a pointer-to-member-function type, so you shouldn't use pointer-to-member syntax. `(this->*field)(y);` should be `(this->field)(y);` or just `this->field(y);` – Yksisarvinen May 29 '20 at 13:04
  • 2
    Use lambda expressions and `std::function` ; read [more about C++](https://en.cppreference.com/w/cpp) – Basile Starynkevitch May 29 '20 at 13:06
  • If you rename and change the declaration to `std::vector(*NAME_OF_THE_POINTER)(double*);` you'll have a bigger chance of success. – Ted Lyngmo May 29 '20 at 13:12
  • Try to avoid using ``pointer`, besides it isn't really needed. Use ``reference` instead. You could also check this [post](https://stackoverflow.com/questions/114180/pointer-vs-reference) – dboy May 29 '20 at 13:25
  • Thank you everyone, I understood better the situation and solved the problem. I'm sorry for the length of the code, it was my first post here. – Matteo Zortea May 29 '20 at 21:02

1 Answers1

1

Your example is a bit large, I didn't use it as-is. But I can spot a fix, with a smaller repro: (I kept your style)

#include <vector>
typedef std::vector<double> (*pfunc)(double*);

class Foo
{
public:
pfunc field;
};

std::vector<double> Bar(double*)
{
    return std::vector<double>{};
}

int main()
{
    Foo f;
    double x;

    f.field = &Bar;
    (&f)->field(&x);
}

The only meaningful change I needed is to remove the * in front of the call to field().

Now, I will advise not using this pattern at all. The OOP way, IMO would be way cleaner here:

class BaseODE
{
    public:
        virtual std::vector<double> field(double*) = 0;

    // put the rest of the code here. 
    // when field is called, the Euler version will be called.
};

class Euler:public BaseODE
{
    public:
        virtual std::vector<double> field(double*) override;        
};

Basically, you have no need yet for function pointers, lambdas, std::function or anything complex.

Jeffrey
  • 11,063
  • 1
  • 21
  • 42