Jump to content
  • Advertisement
Sign in to follow this  
CaspianB

Callbacks to member functions

This topic is 3183 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
"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.

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
"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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!