Как определить общий указатель на функцию-член

Я создал класс Timer, который должен вызывать метод обратного вызова, когда таймер истек. В настоящее время он работает с обычными указателями на функции (они объявляются как void (*)(void)), когда происходит событие Elapsed, вызывается указатель на функцию.

Можно ли сделать то же самое с функцией-членом, которая также имеет подпись void (AnyClass::*)(void)?

Спасибо товарищи.

РЕДАКТИРОВАТЬ: Этот код должен работать на Windows, а также на ОС реального времени (VxWorks), поэтому было бы здорово не использовать внешние библиотеки.

РЕДАКТИРОВАТЬ 2: Просто чтобы быть уверенным, что мне нужно иметь класс Timer, который принимает аргумент в конструкторе типа "AnyClass.AnyMethod" без аргументов и возвращает void. Я должен сохранить этот аргумент, а последний в точке кода просто выполнить метод, указанный этой переменной. Надеюсь ясно.

5 ответов

Решение

Зависимости, зависимости... да, конечно, повышение это хорошо, как и mem_fn, но они вам не нужны. Тем не менее, синтаксис вызова функций-членов является злым, поэтому немного волшебства шаблона помогает:

   class Callback
   {
   public:
      void operator()() { call(); };
      virtual void call() = 0;
   };

   class BasicCallback : public Callback
   {
      // pointer to member function
      void (*function)(void);
   public:
      BasicCallback(void(*_function)(void))
          : function( _function ) { };
      virtual void call()
      { 
          (*function)();
      };
   };   

   template <class AnyClass> 
   class ClassCallback : public Callback
   {
      // pointer to member function
      void (AnyClass::*function)(void);
      // pointer to object
      AnyClass* object;        
   public:
      ClassCallback(AnyClass* _object, void(AnyClass::*_function)(void))
          : object( _object ), function( _function ) { };
      virtual void call()
      { 
          (*object.*function)();
      };
   };

Теперь вы можете просто использовать Callback как механизм хранения обратного вызова так:

void set_callback( Callback* callback );
set_callback( new ClassCallback<MyClass>( my_class, &MyClass::timer ) );

А также

Callback* callback = new ClassCallback<MyClass>( my_class, &MyClass::timer ) );

(*callback)();
// or...
callback->call();

Лучшим решением, которое я использовал для этой же цели, были библиотеки boost::signal или boost::function (в зависимости от того, хотите ли вы один обратный вызов или их много), и boost::bind для фактической регистрации обратных вызовов.

class X {
public:
   void callback() {}
   void with_parameter( std::string const & x ) {}
};
int main()
{
   X x1, x2;
   boost::function< void () > callback1;

   callback1 = boost::bind( &X::callback, &x1 );
   callback1(); // will call x1.callback()

   boost::signal< void () > multiple_callbacks;
   multiple_callbacks.connect( boost::bind( &X::callback, &x1 ) );
   multiple_callbacks.connect( boost::bind( &X::callback, &x2 ) );
   // even inject parameters:
   multiple_callbacks.connect( boost::bind( &X::with_parameter, &x1, "Hi" ) );

   multiple_callbacks(); // will call x1.callback(), x2.callback and x1.with_parameter("Hi") in turn
}

Может быть, стандартный mem_fun уже достаточно хорош для того, что вы хотите. Это часть STL.

Boost::function выглядит идеально подходит здесь.

Я предполагаю, что такой интерфейс:

void Timer::register_callback( void(*callback)(void*user_data), void* user_data );

template<typename AnyClass, (AnyClass::*Func_Value)(void)>
void wrap_method_callback( void* class_pointer )
{
   AnyClass*const self = reinterpret_cast<AnyClass*>(class_pointer);
   (self->*Func_Value)();
}

class A
{
public:
   void callback()
   { std::cout << m_i << std::endl; }
   int m_i;
};

int main ()
{
   Timer t;
   A a = { 10 };
   t.register_callback( &wrap_method_callback<A,&A::callback>, &a );
}

Я думаю, что лучшим решением было бы обновить функцию обратного вызова, чтобы вы использовали boost::function или доморощенную версию (как ответ Корнеля). Однако это требует участия настоящих разработчиков C++, в противном случае вы, скорее всего, будете вносить ошибки.

Преимущество моего решения в том, что это всего лишь одна функция шаблона. Не все может пойти не так. Одним из недостатков моего решения является то, что он может нарезать ваш класс с приведением к void* и назад. Будьте осторожны, что только AnyClass* указатели проходят как void* на регистрацию обратного вызова.

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