C++ хранит указатель на функцию-член неизвестного класса

Я хочу сохранить указатель на объект и указатель на его метод известной подписи. Если я знаю класс, то этот указатель имеет тип:

int (MyClass::*pt2Member)(float, char, char)

Но как я могу сохранить указатель, если я не знаю тип?

Я хочу сделать что-то вроде этого:

myObject.callThisFuncLater(&otherObject, &otherObject::method)

Как я могу сохранить указатель на метод method в myObject и позвонить позже?

4 ответа

Решение

Ты можешь использовать boost::function (а также boost::bind) хранить фрагмент кода для последующего вызова.

class MyClass
{
public:
    void callThisFuncLater( boost::function< int (float, char, char) > callBack );
};
...
myObject.callThisFuncLater( boost::bind( &otherObject::method, &otherObject ) );

Самый простой способ сделать это, если у вас есть доступ к расширениям библиотеки TR1 STL (доступным в gcc и Visual Studio 2008 и далее, - это. Std:: function и std:: bind могут использоваться для переноса вызова, который может быть вызван позже. Эта функциональность также доступна в функции boost и boost bind:

#include <functional>

class MyClass {
public:
  template<typename T> callThisFuncLater(T& otherObject,
                                         int(T::*)(float, char, char) method) {
    return storedInvocation_ = std::bind(otherObject, 
                                  method, 
                                  std::placeholders::_1,   // float
                                  std::placeholders::_2,   // char
                                  std::placeholders::_3);  // char
  }

  int callStoredInvocation(float a, char b, char c) {
    storedInvocation_(a, b, c);
  }

private:
  std::function<int(float, char, char)> storedInvocation_;
};

Нет простого способа сделать это так, как он изначально встроен в язык или стандартную библиотеку (хотя он был недавно добавлен). Если вы знакомы с Boost, они включают решение для этого - Boost.Function.

Однако если по какой-то причине вы не можете или не хотите использовать Boost, существует общий способ сделать это с помощью шаблонов (что, по общему признанию, довольно похоже на решение Boost):

class FncPtr
{
public:
    virtual int call(float, char, char) = 0;
};

template <typename T>
class ClassFncPtr : public FncPtr
{
     int (T::*pt2Member)(float, char, char);
     T *inst;
public:
     ClassFncPtr(T* who, int (T::*memfunc)(float,char,char))
         : inst(who), pt2Member(memfunc)
     {
     }
     int call(float a, char b, char c)
     {
         return (inst->*pt2Member)(a,b,c);
     }
};

template <typename T>
FncPtr * makeFuncPointer(T* who, int (T::*memfunc)(float,char,char))
{
    return new ClassFncPtr<T>(who,memfunc);
}

Вы также можете подкласс FncPtr чтобы иметь возможность использовать не классовые функции, если хотите.

Лично я бы выбрал другой дизайн. Просто потому, что с указателями на функции-члены в C++ работать нелегко. Лично я бы предпочел использовать интерфейсы и наследовать от них и анализировать их.

Одна из проблем с указателями на функции-члены заключается в том, что они реализованы по-разному в разных компиляторах. Если вы используете компиляторы Borland/Embarcardero и хотите ограничиться этим, вы можете использовать __closure ключевое слово, однако, скорее всего, это не так, и поэтому вам придется либо использовать какую-то другую специфическую для компилятора реализацию, либо использовать один из вспомогательных классов boost, таких как function.

Но если вы оказались в ситуации, когда вам полезно использовать указатели на функции-члены в C++, пересмотрите свой дизайн.

Другие вопросы по тегам