My general purpose is to design an optimizer. It's a class that searches for a maximum value for a given function. I intend to use this optimizer within other parts of code including methods of another classes.
I have implemented the optimizer like this:
class MyOptimizer {
public:
typedef double (*FuncType)(double);
MyOptimizer(FuncType f, double a, double b) : _f(f), _a(a), _b(b) {}
double optimize() const {
// do something with _f in order to find the maximum
return maximum;
}
private:
double _a;
double _b;
FuncType _f;
}
I want to use the optimizer as a library in other pieces of my code. For example:
class AnyClass {
public:
...
double func(double x) const {
// using members of the instance
}
void aMethod() {
MyOptimizer opt(func, 0.0, 1.0);
double bestArgument = opt.optimize();
}
...
};
But I'm not allowed to do it in this way because the type signature of AnyClass::func
is different.
What is the best way to design the optimizer to make it convenient to use in other pieces of code? A code example would be appreciated.
I need a solution for C++ 1998/2003 Standard.
The problem is that AnyClass::func
actually takes two parameters, formally x
but it also needs this
. If you can afford C++11, std::function
is your friend here (or a Callable
template):
MyOptimizer::MyOptimizer(std::function<double(double)> f, double a, double b);
You can then wrap an object method in a lambda function :
MyOptimizer opt([this] (double x) -> double { return func(x); }, 0.0, 1.0);
If not, you would need to write your own wrapper class that stores the value of this
and exposes an operator()
and pass such object instead of the function pointer.
template<class AnyClass>
class AnyClassWrapper {
AnyClass *that;
typedef double (AnyClass::*FuncType)(double);
FuncType func;
public:
AnyClassWrapper(AnyClass* _that, FuncType _func): that(_that), func(_func) { }
double operator()(double x) {
return that->*func(x);
}
};
Instances of this class can now be used just like functions: double y = instance(x)
. You'll need to template the constructor (edit: and along with it the whole class, given that you're storing the function) as well, to be able to accept a normal function pointer as well as a generalized function object like this, using the fact that the syntax is the same for both:
template<class Func>
MyOptimizer::MyOptimizer(Func& f, double a, double b);
Then you can use this with non-static functions:
// within AnyClass
AnyClassWrapper<AnyClass> callable(this, &AnyClass::f);
MyOptimizer opt(callable, 0.0, 1.0);
But the same class also accepts normal functions:
// anywhere
MyOptimizer opt2(std::sin, 0.0, 1.0);
NB that the above is basically implementing what the lambda function is doing behind the scenes. You can similarly backport the std::function
if needed - that would allow for the optimizer to not need any templates.
The simplest solution would be to have AnyClass inherit from your optimizer. Like: this . But if your dead set on backCalling, you should know that your C++ declarations have errors. I would have commented your question instead of posting an answer but StackOv. requires at least 50rep to comment!
You can use pointer-to-member function. A good description of that is here .
Here is one implementation.
template <class T>
class MyOptimizer {
public:
typedef double (T::*FuncType)(double) const;
MyOptimizer(T* instance, FuncType f, double a, double b) : _instance(instance),_f(f), _a(a), _b(b) {}
double optimize() const {
// do something with _f in order to find the maximum
return 1.0;
}
private:
double _a;
double _b;
FuncType _f;
T* _instance;
};
class AnyClass {
public:
double func(double x) const {
// using members of the instance
return 0.0;
}
void aMethod() {
MyOptimizer<AnyClass> opt(this, &AnyClass::func, 0.0, 1.0);
double bestArgument = opt.optimize();
}
};
If you have the option of changing MyOptimizer
, change it to a class template or use a std::function
instead of the function pointer, as suggested by @TheVee
If you don't have the option of changing MyMonitor
, I have couple of suggestions.
In order to use AnyClass::func
with MyOptimizer::optimize
, you will need a non-member function or a static
member function. Either of those functions can work if they are able to access the AnyClass
object on which you want to call func
.
Using a non-member function
// Global variable
AnyClass* currentAnyClass = nullptr;
// Non-member function
double funcWrapper(double x)
{
assert(currentAnyClass != nullptr);
return currentAnyClass->fun(x);
}
and then
void aMethod() {
currentAnyClass = this;
MyOptimizer opt(funcWrapper, 0.0, 1.0);
double bestArgument = opt.optimize();
currentAnyClass = nullptr;
}
Using a static
member function
It follows almost the same strategy. I prefer this over the first once since it keeps currentAnyClass
and funcWrapper
in the scope of `AnyClass.
class AnyClass {
public:
...
double func(double x) const {
// using members of the instance
}
void aMethod() {
currentAnyClass = this;
MyOptimizer opt(funcWrapper, 0.0, 1.0);
double bestArgument = opt.optimize();
currentAnyClass = nullptr;
}
static AnyClass* currentAnyClass;
static double funcWrapper(double x)
{
assert(currentAnyClass != nullptr);
return currentAnyClass->func(x);
}
};
AnyClass* AnyClass::currentAnyClass = nullptr;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.