Callbacks to member functions

Started by
5 comments, last by Katie 14 years, 4 months ago
I'm adding callback capability to a windows wrapper class I'm working on for handling mouse events, buttons, and so on and I'm trying to figure out the best way to go about implementing registering callbacks. I started off using simple function pointers, but I didn't like that as I'd like to keep this somewhat object oriented as opposed to having to deal with global variables for global functions - I wanted some way to actually pass user specified data to the event handler. So after some googling, I read up and implemented a functionoid type system where you would have to derive a handler classes from specific base classes I created. They would have to define the interface method ( void notifty() ) which would be called, but they could at least pass other data into their derived class that would be retained at the time of the event occurring and notify being called. I didn't like that much because, well, I would like to be able to define the methods that are called themselves. Also, I disliked having to create a new derived class for every single event. I would prefer to have one class that could handle all events on the window and I can specify which method is called for each event. So back to google again, and I found some templated ways of registering member functions. So I'm looking into using templates to hold a set of function pointers to user specified member functions, but this seems somewhat messy (and I realize this is C++, so it's going to be messy), but it still leaves me wondering if there might be an easier way to do this. An example of what I'm looking at doing (as I don't want to paste the entire wrapper class) follows:

#include <iostream>
#include <sstream>
#include <vector>
using namespace std;

class EventHandler { };

struct EventNode
{
   EventNode(void (EventHandler::*funcPtr)(), EventHandler *obj) : funcPtr(funcPtr), obj(obj) { };
   void (EventHandler::*funcPtr)();
   EventHandler *obj;
};

vector<EventNode> funcs;

template<class T>
void notify( void (T::*funcPtr)(), EventHandler *obj )
{
   void (EventHandler::*f)() = static_cast<void (EventHandler::*)()>(funcPtr);
   funcs.push_back(EventNode(f, obj));
}

class Test : public EventHandler
{
public:
   Test(string s) : str(s) { }
   void func1() { cout << "Test.func1: " << str << endl; }
   void func2() { cout << "Test.func2: " << str << endl; }
   string str;
};

class Test2 : public EventHandler
{
public:
   Test2(string s) : str(s) { }
   void func1() { cout << "Test2.func1: " << str << endl; }
   void func2() { cout << "Test2.func2: " << str << endl; }
   string str;
};

int main()
{
   Test a("a");
   Test2 b("b");
   notify<Test>(&Test::func1, &a);
   notify<Test>(&Test::func2, &a);
   
   notify<Test2>(&Test2::func1, &b);
   notify<Test2>(&Test2::func2, &b);
   
   for ( size_t i = 0; i < funcs.size(); i++ )
   {
      EventHandler *obj = funcs.obj;
      (obj->*funcs.funcPtr)();
   }
}





The user would still need to derive their handler class from the "EventHandler" class, but they can define whatever method they wish for each callback. This will allow them to create a single class that could contain different methods for different buttons on the window of their choosing. This is, ultimately, my goal. So is this a good route to go down or is there another design pattern out there that I'm missing?
Advertisement
The general term is 'bind' to adapt member functions into a general function pointer (or better yet, boost::function). There are a few methods in the standard library to do it, and boost has some better/more general binding adapters. Though they're all rather nasty, even for C++. But that's the good flexible way to do it.
Try googling "impossably fast delegates" this is what I am using for my callbacks
Game development blog and portfolio: http://gamexcore.co.uk
I posted something about that too a few days ago : http://www.gamedev.net/community/forums/topic.asp?topic_id=554792

It solves the problem you seem to have with the derived classes.

Hope this helps you a bit.
"Try googling "impossably fast delegates" "

void* pointers?

If you want to avoid allocating memory, what's wrong with using a template type with the appropriate dispatching, but using an inplace-constructor to build the derived typematched object into a suitably sized buffer inside your delegate object?

That way you don't have to faff about with a call to "new", your delegate can be copied and compared properly and the whole thing is typesafe and doesn't have void*s floating about the place.

Let's assume for this question it is also an academic exercise (actually, not much assumption is needed there, it largely is), so I would like to refrain from using boost for now - instead, I would like to learn exactly how you would use the currently built in features of C++ to do this. Though my glance at boost::bind and boost::function seem to indicate those would be the ideal candidates for this job. My member functions will have well defined sets of arguments, so that shouldn't be a concern (i.e. mouse handler will need an X,Y coordinate and mouse event type, buttons will need a button ID, etc).

After Telastyn's comment, I also located std::mem_fun which I thought looked like something I could use. But I wasn't sure entirely how to use it to do what I would like to do. I would need to store it's returned value in a container. And the classes each member function pointer points to may not necessarily be the same.

Keep in mind that I need to maintain a set of these member function pointers in my class. And I would like the set to be non-type specific. Meaning you can assign a callback to one button from ClassA and a callback to another button from ClassB with no relationship between those two classes (the code I posted illustrates that, though ClassA and ClassB need to both derive from my empty class EventHandler).

Also, I don't have a problem with the method in my original post, I was just curious if there were better ways of handling what I'm attempting to accomplish.

It looks like the code in Eddycharly's link accomplishes this. I'll definitely look into that after work tonight.

[Edited by - CaspianB on December 2, 2009 10:25:05 AM]
"I would like to learn exactly how you would use the currently built in features of C++ to do this"

OK, it works like this;

Lets assumed for a moment that we want to call a function which takes a string and returns an integer on some arbitrary class.

You have a wrapper class. Let's call it a "CThunk" for a minute. CThunk wants to have two main operations on it. "Assign it" and "use it". Assign it is to give it a destination, "use it" means to call the destination.

So, assigning it needs to take an arbitrary type ref and a member function into that type. So that sounds like it should be a templated function.

"Use it" should just be a plain function. However, it somehow needs to get to a templated type... so you need an inner class which can be templated on the type of the object that we're pointing at. So we probably want an interface (which we can call on) and also a templated inheritor of that interface (which gives us the type safety.

Lets call those IInner and TInner.

So, IInner gives us a "virtual int call(const string &)=0;" member.

TInner is templated by a type (let's call it TYPE), and has two data members; a TYPE* and an int TYPE::*(const string&)

It descends from IInner, and it needs to implement call. So it'll look something like this;

template<class TYPE> class TInner
{
TYPE *object;
int TYPE::*memfunc(const string&);

int call(const string&s) { return obj->*memfunc(s); }
};

(boilerplate class stuff omitted for brevity.)

Now the trick is that the CThunk contains a pointer to an IInner, let's call it "inner". That means that it too can have an int call(const string&s) function which looks like this;

int call(const string &s) { return inner->call(s); }

That's easy. That's the "use it" side done. All we need to do then is sort out the assigning. It's a templated member of CThunk which makes the correct sort of TInner. Something like this;

template<class OBJTYPE>
void assign(OBJTYPE &obj,int OBJTYPE::*memfunc(const string &))
{
TInner<OBJTYPE> *tinner = new TInner<OBJTYPE>;
tinner -> object = &obj
tinner -> memfunc = memfunc;
inner = tinner;
}

(Obviously, if you make constructors take the right sorts of parameters, you can do this more neatly.)

Management of the memory, copy constructors etc is left as an exercise for the reader :-)

(A hint -- if you make the IInner and TInner classes implement an inner *clone() method, you can delegate the work to a class which knows the correct type to make for your new copy).

Once you have a copy constructor and assignment working, the CThunks will go in normal STL containers.

Bonus points for making CThunk contain a suitably sized buffer and using in-place construction for the inner class to save a call to new.

That gets you a callback whose overhead is basically only a virtual function dispatch over and above a regular member function pointer.

This is roughly what boost implements, but with a whole load of complex plumbing so that you can supply the member function signature with template params.

Boost::bind goes on to add storage for parameters in the thunk -- in our case here, it would add a string member to the CThunk, so that the signature of CThunk::call would be "int call(void);" but it can still point to an "int::*(string)" and will be given the string at creation time instead of at call time.

This topic is closed to new replies.

Advertisement