Reputation: 91
I'm trying to add a simple messaging system to my project, where events can be invoked by a function, which will lead to all callbacks registered to that event being called.
Now, the logical way to do this is using function pointers. It would be easily possible to pass the pointer to the desired callback function to the events manager, for registering. An event callback function would always return an int
and take a void*
as argument.
However I don't want to register static global functions as my event callbacks - I'd like to do it with class member functions.
Is it even possible to accomplish this with C++? Storing and calling pointers to member functions of different classes but with the same function header.
If this is not possible, do you have any suggestions on how I could work around this? I'd really like to add event listeners directly to my classes.
Upvotes: 9
Views: 10653
Reputation: 4450
I am using lukes answer with SWIG because SWIG does not support all C++11 features... This probably can be improved even further with Parsa Jamshidis approach.
I modified it to cover even more cases (variable amount of arguments and variable return type):
#include <iostream>
template <typename R, typename ...T>
class Callback
{
public:
virtual ~Callback() {}
virtual R operator() (T... args) = 0;
};
template <typename R, typename ...T>
class FreeCallback : public Callback<R, T...>
{
public:
FreeCallback(R(*func)(T...)) : func_(func) {}
virtual R operator() (T... args) { return (*func_)(args...); }
private:
R(*func_)(T...);
};
template <typename tClass, typename R, typename ...T>
class MemberCallback : public Callback<R, T...>
{
public:
MemberCallback(tClass* instance, R (tClass::*memberfunction)(T...)) : instance_(instance), memberfunc_(memberfunction) {}
virtual R operator() (T... args) { return (instance_->*memberfunc_)(args...); }
private:
tClass * instance_;
R (tClass::*memberfunc_)(T...);
};
class foo {
public:
Callback<int, int> *IntCallback;
Callback<int, int, double, double> *IntDoubleDoubleCallback;
};
class blub {
public:
int func1(int i) {
std::cout << "args: " << i << std::endl;
return 1;
}
int func2(int i, double d1, double d2){
std::cout << "args: " << i << " " << d1 << " " << d2 << std::endl;
return 0;
}
};
int freeFunc1(int i) {
std::cout << "args: " << i << std::endl;
return 1;
}
int freeFunc2(int i, double d1, double d2){
std::cout << "args: " << i << " " << d1 << " " << d2 << std::endl;
return 0;
}
int main() {
foo f;
blub b;
f.IntCallback = new MemberCallback<blub, int, int>(&b, &blub::func1);
f.IntDoubleDoubleCallback = new MemberCallback<blub, int, int, double, double>(&b, &blub::func2);
Callback<int, int> *IntFreeCallback = new FreeCallback<int, int>(&freeFunc1);
Callback<int, int, double, double> *IntDoubleDoubleFreeCallback = new FreeCallback<int, int, double, double>(&freeFunc2);
int ret = (*IntFreeCallback)(42);
std::cout << "ret freeFunc1: " << ret << std::endl;
ret = (*IntDoubleDoubleFreeCallback)(42, 3.1415, 2.7182);
std::cout << "ret freeFunc2: " << ret << std::endl;
ret = (*f.IntCallback)(42);
std::cout << "ret func1: " << ret << std::endl;
ret = (*f.IntDoubleDoubleCallback)(42, 3.1415, 2.7182);
std::cout << "ret func2: " << ret << std::endl;
std::cout << "Hello World!\n";
// cleanup not done here...
}
Upvotes: 0
Reputation: 37463
Yes it is possible. C++0x has the function
class that handles this, and as others have pointed out Boost has similar facilities.
You can also roll your own, but the syntax is not for the faint of heart:
#include <iostream>
class Callable
{
public:
virtual ~Callable() {}
virtual int operator() (void* args) = 0;
};
class CallableFreeFunction : public Callable
{
public:
CallableFreeFunction(int (*func)(void*)) : func_(func) {}
virtual int operator() (void* args) { return (*func_)(args); }
private:
int (*func_)(void*);
};
template <typename tClass>
class ClassMemberCallable : public Callable
{
public:
ClassMemberCallable(tClass* instance, int (tClass::*memberfunction)(void*)) : instance_(instance), memberfunc_(memberfunction) {}
virtual int operator() (void* args) { return (instance_->*memberfunc_)(args); }
private:
tClass* instance_;
int (tClass::*memberfunc_)(void*);
};
class Foo
{
public:
int derp(void* args)
{
std::cout << args << '\n';
return 2;
}
};
int freefunctionfoo(void* args)
{
std::cout << "free" << args << '\n';
return 2;
}
int main(int argc, char* argv[])
{
Foo myfoo;
Callable* callable = new ClassMemberCallable<Foo>(&myfoo, &Foo::derp);
(*callable)(0);
delete callable;
callable = new CallableFreeFunction(freefunctionfoo);
(*callable)(0);
delete callable;
std::cin.get();
return 0;
}
This demonstrates a way of handling both free functions, and member functions in an opaque way. This is a simple example, and can be made more generic and robust in a number of ways. I'd refer you to these pages for syntax help:
http://www.newty.de/fpt/index.html
http://www.parashift.com/c++-faq-lite/pointers-to-members.html
I'd also recommend looking at this for more ideas:
http://www.codeproject.com/KB/cpp/FastDelegate.aspx
Upvotes: 8
Reputation: 4028
Of course it's possible ! Have a look at Boost.Signal2 and Boost.Bind.
Boost.Signal2 basically implements a signal and slots system which is exactly what you need.
Then, you can use boost::bind
which is a generalization of std::bind1st
and std::bind2nd
to get function object wrappers to basically anything you can think of (in your case, member methods). It's really powerful.
See this official boost tutorial.
Upvotes: 3
Reputation: 717
Here is my not-so-good attempt for doing a job like that: First of all you need a base event handler class, well let's call it EvtHandler for now:
class Event; //implement this yourself, it shall contain general but good info about event
class EvtHandler
{
public:
virtual void handleEvent (Event & evt);
};
Then every class that is supposed to handle events in a way, should derive from this class, and they can implement new functions as much as they want as far as they return the same data type (void in this case) and recieve the same paramteres (Event in this case). Like this:
class Foo : public EvtHandler
{
public:
void handleFooEvent (Event & event);
};
Then I implemented message centers for each special event, which had to register listeners and dispatch events when needed:
class ShutdownMessageCenter
{
typedef std::map<EventHandler *, event_func> ListenerMap;
public:
void register (EvtHandler * handler, void(EvtHandler::*memFunc)(Event &)) {
m_lmap[handler] = memFunc;
}
void callListeners () {
Event shutdown_event (EM_SHUTDOWN /*just imagine this can mean something, idk*/);
ListenerMap::iterator itr = m_lmap.begin ();
for (; itr != m_lmap.end(); ++itr) {
EvtHandler * handler = itr->first;
void (EvtHandler::*func)(Event &) = itr->second;
(handler->*func)(shutdown_event);
}
}
private:
ListenerMap m_lmap;
};
Then you could register your EvtHandlers to this particular message center for example!
ShutdownMessageCenter message_center;
EvtHandler * some_handler = new EvtHandler ();
Foo * some_foo = new Foo ();
message_center.register (some_handler, &EvtHandler::handleEvent);
message_center.register (some_foo, static_cast<void (EvtHandler::*)(Event &)>(&Foo::handleFooEvent);
message_center.callListeners ();
But once again this is not good at all, just thought I would share! Sorry for the mess, haha!
Upvotes: 2
Reputation: 14119
I am not completely sure what you want to archive but maybe you should look at Boost Signals2
It is quite helpful if you want to create some sort of Signal/Slot mechanism.
Upvotes: 1
Reputation: 1797
The closest that I have managed is to register a static member function as the callback. The static member takes the object (this) pointer as an argument in addition to the arguments sent by the event handler and uses this to call the member function.
class myClass{
public:
static void callback(void *arg, void *obj)
{
if (obj)
reinterpret_cast<myClass*>(obj)->cb(arg);
}
private:
void cb(void *arg);
};
Register myClass::callback
and this
with your handler. You may need to wrap this in the structure that arg references if you are restricted in what can be returned.
Upvotes: 0
Reputation: 8421
No, it is not possible (unless you do c++/cli with .net).
Now, you can still create static functions, pass them an object as a parameter, and the only thing that they'll do is call your member function on that object. (Actually a cast will be required first).
Upvotes: 0