I would like to create a function-object class that numerically integrates some functions on-demand. Its public interface would consist of a method void T::step(double dt)
that computes an integration step (and updates the internal state of the function-object with the new values), and getters to the current calculated values.
While I'm sure this could be done with pure stand-alone functions, the functions I'll be integrating have many intermediate calculation steps in common, and depend on many internal parameters which must be updated on every step (and are largely irrelevant to the user), so I thought of using a function-object to encapsulate this mechanism.
class myIntegrals
{
public:
void step(double dt);
double get_f1_integral() { return f1_integral; };
double get_f2_integral() { return f2_integral; };
private:
// funcs to be integrated (n.b. they share parameter/return types)
double f1(double t); // depends on the state of member attributes;
double f2(double t); // idem, and also on the results of f1
double t;
double f1_integral;
double f2_integral;
// and many internal parameters, updated on every step
}
void myIntegrals::step(double dt)
{
// integrate `f1` from `t` to `t+dt`
// process results, update internal parameters
// integrate `f2` from `t` to `t+dt`, update internal parameters
// process results, update internal parameters
t += dt;
}
But now I've found myself wrestling the type-system, trying to define a function double integrate(double t, double dt, METHOD f)
to actually compute each individual function's integration step, accepting member functions as arguments.
I've come up with
template <typename T>
double integrate(double t, double dt, double (T::*f)(double), T* obj)
{
// Here I can reference `obj->*f(...)`
}
but I'm a bit uneasy, as it seems contrived and some people make it seem like function pointers are the devil when it comes to performance.
Using only C++11 features, is there a cleaner, more performant or more idiomatic approach to this?
Personally I would offload the call to the member function to the call site by having integrate
take a Callable. That forces you to use a lambda expression at the call site or a custom functor. Something like:
template <typename Callable>
double integrate(double t, double dt, Callable c)
{
return c(t * dt);
}
double step(double)
{
return 42.0;
}
struct MyCustomType {
double step(double)
{
return 42.0;
}
};
int main()
{
MyCustomType foo;
// use member function via a lambda wrapper
integrate(1, 2, [&](double param) { return foo.step(param); });
// use regular function
integrate(1, 2, step);
}