Reputation: 879
I've looked at the top answers c++ to Visitor pattern and to pointers to member functions; but I still cannot get how the following (rather simple) scenario should be designed.
In a basic example, I want to have an optimizer class. Given a member function of another class, it find some optimal value. Something like this:
class Optimizer{
public:
typedef double (*Func1d)(double);
typedef double (*Func2d)(double,double);
void Optimize1d(Func1d* f1Ptr, double& x);
void Optimize2d(Func2d* f2Ptr, double& x, double& y);
};
and two example classes:
class A {
double A1d(double x){return x;}
};
class B{
double B2d(double x, double y){return x+y;}
};
and the main function, which I would like to be able to use as follows:
void main()
{
Optimizer opt;
double xA_opt, xB_opt, yB_opt;
opt.Optimize1d(&A::A1d,xA_opt);
opt.Optimize2d(&B::B2d, xB_opt, yB_opt);
}
But still, I can't get it to work. I don't want the optimizer to directly hold pointers to objects of type A and B; because then he needs to be familiar with these objects.
I hope this question makes sense. Thanks!
Upvotes: 1
Views: 130
Reputation: 15334
The problem is that typedef double (*Func1d)(double);
is not a member-function-pointer but just a plain function-pointer.
If you used a real member-function-pointer, the function would also have to have an instance of A
or B
which you say you don't want.
If you can't make A1d
and B2d
static the other options are to make your Optimize1d
and Optimize2d
template functions taking a templated functor:
template<typename F>
void Optimize1d(F f1, double& x);
template<typename F>
void Optimize2d(F f2, double& x, double& y);
or a std::function
:
void Optimize1d(std::function<double(double)> f1, double& x);
void Optimize2d(std::function<double(double, double)> f2, double& x, double& y);
Both can be called with a lambda capturing an instance of A
or B
:
A a;
B b;
opt.Optimize1d([&a](double x){return a.A1d(x); }, xA_opt);
opt.Optimize2d([&b](double x, double y){return b.B2d(x, y); }, xB_opt, yB_opt);
Edit:
If you don't have C++11 you could define your own functor using a class which defines an operator()
instead of a lambda. The class will have to store a pointer or reference to an instance of A
or B
in a member variable:
struct A1d {
A* a;
A1d(A& a) : a(&a) {}
double operator()(double x) { return a->A1d(x); }
};
You can then construct an instance of this class and pass it to the templated optimize functions:
A1d a1d(a);
opt.Optimize1d(a1d,xA_opt);
perhaps you could even make your A
or B
classes functors themselves by adding an operator()
function to them?
Upvotes: 2
Reputation: 16204
The problem in the above code is that in C++, pointer-to-member functions are a distinct type, incompatible with "regular" function pointers.
This typedef
typedef double (*Func1d)(double);
is legal in both C and C++ code, and you can use C-style "free" functions with this type.
But at this line in your main function:
opt.Optimize1d(&A::A1d,xA_opt);
you are trying to pass a pointer to member function as a Func1d
and that can't be done. For one thing, you can't invoke a pointer to member function without a pointer to an object of that type as well, and you would have to pass that also.
The simplest thing is to include header <functional>
and use std::function<double(double)>
for this. (Assuming you have C++11, otherwise you could use boost::function
.)
There are other things you could do like use virtual member dispatch as suggested in comments. (IMO that's a little less elegant.)
You could also make the optimize
functions be template functions, and accept an object of the templated type etc. etc. But really std::function
is the way to go here, it will handle all those details for you.
Upvotes: 2