Matteo Zortea
Matteo Zortea

Reputation: 1

Problem passing function pointer to a class c++

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.

Upvotes: 0

Views: 101

Answers (1)

Jeffrey
Jeffrey

Reputation: 11410

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.

Upvotes: 1

Related Questions