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.

6
  • 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 minimal reproducible example, like this Commented May 29, 2020 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); Commented May 29, 2020 at 13:04
  • 2
    Use lambda expressions and std::function ; read more about C++ Commented May 29, 2020 at 13:06
  • If you rename and change the declaration to std::vector<double>(*NAME_OF_THE_POINTER)(double*); you'll have a bigger chance of success. Commented May 29, 2020 at 13:12
  • Try to avoid using pointer`, besides it isn't really needed. Use reference` instead. You could also check this post Commented May 29, 2020 at 13:25

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.