简体   繁体   中英

Passing a method of a class as a parameter into another class in C++

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.

C++11 solution (to the question prior to its edit):

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.

C++03 solution:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM