Как определить общий указатель на функцию-член
Я создал класс 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.
Я предполагаю, что такой интерфейс:
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*
на регистрацию обратного вызова.