问题
I have created a Timer class that must call a callback method when the timer has expired. Currently I have it working with normal function pointers (they are declared as void (*)(void), when the Elapsed event happens the function pointer is called.
Is possible to do the same thing with a member function that has also the signature void (AnyClass::*)(void)?
Thanks mates.
EDIT: This code has to work on Windows and also on a real-time OS (VxWorks) so not using external libraries would be great.
EDIT2: Just to be sure, what I need is to have a Timer class that take an argument at the Constructor of tipe "AnyClass.AnyMethod" without arguments and returning void. I have to store this argument and latter in a point of the code just execute the method pointed by this variable. Hope is clear.
回答1:
Dependencies, dependencies... yeah, sure boost is nice, so is mem_fn, but you don't need them. However, the syntax of calling member functions is evil, so a little template magic helps:
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)();
};
};
Now you can just use Callback as a callback storing mechanism so:
void set_callback( Callback* callback );
set_callback( new ClassCallback<MyClass>( my_class, &MyClass::timer ) );
And
Callback* callback = new ClassCallback<MyClass>( my_class, &MyClass::timer ) );
(*callback)();
// or...
callback->call();
回答2:
The best solution I have used for that same purpose was boost::signal or boost::function libraries (depending on whether you want a single callback or many of them), and boost::bind to actually register the callbacks.
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
}
回答3:
Maybe the standard mem_fun is already good enough for what you want. It's part of STL.
回答4:
boost::function looks like a perfect fit here.
回答5:
I'm assuming an interface like this:
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 );
}
I think a better solution would be to upgrade call you callback to either use boost::function or a homegrown version (like Kornel's answer). However this require real C++ developers to get involved, otherwise you are very likely to introduce bugs.
The advantage of my solution is that it is just one template function. Not a whole lot can go wrong. One disadvantage of my solution is it may slice your class with cast to void*
and back. Be careful that only AnyClass*
pointers are passes as void*
to the callback registration.
来源:https://stackoverflow.com/questions/2147722/how-to-define-a-general-member-function-pointer