Question

I am working on an exercise in the textbook, Scientific Computing in C++ by Francis and Whitely and cannot figure out how to correctly implement exercise 7.3.

Exercise: The goal is to learn about class inheritance by creating an abstract class that contains methods for solving ordinary differential equations. The derived classes inherit this abstract class and are associated with specific algorithms for solving ODEs, like explicit Euler, RK4, etc. Two of the methods in the abstract class are pure virtual, SolveEquation and RightHandSide. The exercise requires you to use write the code for the derived class ForwardEuler, which implements the explicit Euler method.

Issue: In the exercise, they ask you to "Derive a class called FowardEulerSolver that allows the user to specify the function RightHandSide", but RightHandSide is a member of a class, and I can't figure out how to let a user specify a class method in the main program, which is what I assume they are asking for.

Question: Can someone explain the proper way in C++, if there is one, to allow a user to specify a class method? Based on the way the way the base class is implemented in the book, it appears there must be some way to have a user define the RightHandSide function in the main program, and then call the SolveEquation method to solve the ODE associated with that RightHandSide function.

Here is the header for the abstract class given in the book.

class AbstractODESolver
{
    public:
        AbstractODESolver();
        double (*RHS)(double, double);
        void SetStepSize(double h);
        void SetTimeInterval(double t0, double t1);
        void SetInitialValue(double y0);
        void setRHS(double (*RHS)(double, double));
        double GetStepSize();
        double GetInitialTime();
        double GetFinalTime();
        double GetInitialValue();
        virtual double RightHandSide(double y, double t) = 0;
        virtual void SolveEquation(std::string filename) = 0;
        virtual ~AbstractODESolver();
    private:
        double stepSize;
        double initialTime;
        double finalTime;
        double initialValue;
};

And here is the header for the derived class.

class ForwardEulerSolver : public AbstractODESolver
{
    public:
        ForwardEulerSolver();
        double RightHandSide(double y, double t);
        void SolveEquation(std::string filename);
        virtual ~ForwardEulerSolver();
    private:
};
Was it helpful?

Solution

there must be some way to have a user define the RightHandSide function in the main program, and then call the SolveEquation method to solve the ODE associated with that RightHandSide function.

It's still a bit unclear for me, what exactly you're asking for, but from your comments it sounds you want to have a user defined function executed within your abstract classes context. I think this is possible, but you'll need to change the interfaces a bit:

class AbstractODESolver
{
    public:
        AbstractODESolver();
        // ...
        void SolveEquation(std::string filename) {
             // Have an abstract implementation of the algorithm involving
             // a call to the RightHandSide() method.
        }
        virtual ~AbstractODESolver();

    protected:
        virtual double RightHandSide(double y, double t) = 0;
    private:
        double stepSize;
        double initialTime;
        double finalTime;
        double initialValue;
};

class ForwardEulerSolver : public AbstractODESolver
{
public:
    typedef double (*FnType)(AbstractODESolver*,double,double);

    ForwardEulerSolver(FnType fn_) : fn(fn_) { assert(fn); }
    virtual ~ForwardEulerSolver();
private:
    virtual double RightHandSide(double y, double t)
    {
        return (*fn)(this,y,t);
    }

    FnType fn;
};

If you're working in a environment you can use an inline lambda function definition to get the necessary function pointer:

int main()
{
    ForwardEulerSolver::FnType rightHandSide = 
        [](AbstractODESolver* solver, double y, double t) { 
            // Replace with reasonable user implementation
            return 0.0; 
        }; 

    ForwardEulerSolver fwEulerSolver(rightHandSide);

    fwEulerSolver.SolveEquation("MyInput.txt");
}

For pre c++11 standard you can use a module private function definition to implement the wanted behavior

namespace {
    double RightHandSide(AbstractODESolver* solver, double y, double t) { 
        // Replace with reasonable user implementation
        return 0.0; 
    }
}

int main()
{

    ForwardEulerSolver fwEulerSolver(&RightHandSide);

    fwEulerSolver.SolveEquation("MyInput.txt");
}

This is more or less replicating, what's called the Template Method Pattern, but handling a special (edge) use case, to allow setting a user defined implementation function for a particular part of the algorithm.

OTHER TIPS

You make your own ForwardEulerSolver::RightHandSide function.

You don't have to make anything special in the main function, just create an instance of the ForwardEulerSolver class and assign it to a pointer to AbstractODESolver and call the function. The compiler will handle the rest for you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top