2
\$\begingroup\$

For the past few days I've been trying to come up with a robust event handling system for the game (using a component based entity system, C++, OpenGL) I've been toying with.

class EventDispatcher {
  typedef void (*CallbackFunction)(Event* event);

  typedef std::unordered_map<TypeInfo, std::list<CallbackFunction>, hash_TypeInfo > TypeCallbacksMap;

  EventQueue* global_queue_;

  TypeCallbacksMap callbacks_;

  ...
}

global_queue_ is a pointer to a wrapper EventQueue of std::queue<Event*> where Event is a pure virtual class. For every type of event I want to handle, I create a new derived class of Event, e.g. SetPositionEvent.

TypeInfo is a wrapper on type_info.

When I initialize my data, I bind functions to events in an unordered_map using TypeInfo(typeid(Event)) as the key that corresponds to a std::list of function pointers. When an event is dispatched, I iterate over the list calling the functions on that event. Those functions then static_cast the event pointer to the actual event type, so the event dispatcher needs to know very little.

The actual functions that are being bound are functions for my component managers. For instance, SetPositionEvent would be handled by

void PositionManager::HandleSetPositionEvent(Event* event) {
   SetPositionEvent* s_p_event = static_cast<SetPositionEvent*>(event);
   ...
}

The problem I'm running into is that to store a pointer to this function, it has to be static (or so everything leads me to believe.) In a perfect world, I want to store pointers member functions of a component manager that is defined in a script or whatever. It looks like I can store the instance of the component manager as well, but the typedef for this function is no longer simple and I can't find an example of how to do it.

Is there a way to store a pointer to a member function of a class (along with a class instance, or, I guess a pointer to a class instance)?

Is there an easier way to address this problem?

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Yes you can store member function pointers, and here's a program demonstrating the syntax for that.

#include <iostream>

using namespace std;

struct X
{
    void f(int i) { cout << "X::f " << i << endl; }
};

int main()
{
    void (X::*pf)(int) = &X::f;

    X x;
    X *px = &x;

    (x.*pf)(4);
    (px->*pf)(5);
}

But it comes with many caveats. For example, you'll notice that the pointer embeds the class type, which means all callback targets would have to derive from the same base class. And that's just the beginning of the list.

The truth is, however, that your callback source really shouldn't care who it's calling, as long as it's been told to call back. For this purpose, you really should read up on Boost.Bind and Boost.Function (if you're using C++11, that would be std::bind and std::function).

\$\endgroup\$
3
\$\begingroup\$

With C++11, it's built in to the standard library, or if that's not an option, boost::bind might be what you are looking for.

I don't bother and use a free function that takes a void pointer for user data for my callbacks.

\$\endgroup\$
3
  • \$\begingroup\$ It looks like a combination of std::function and std::bind can get me the desired functionality... I didn't think such things were in the standard library. Can you elaborate on how you use a free function? \$\endgroup\$
    – derivative
    Commented Oct 2, 2012 at 23:16
  • \$\begingroup\$ @derivative The free function approach is just using function pointers. What Neil is saying is to include a userdata parameter, e.g. have a static member function static void MyClass::Handler( Event* event, void* userdata ). Inside it, cast userdata to MyClass. It will then have access to MyClass's internals. But you'll also need to store the userdata argument along with the callback: dispatcher->setCallback( &MyClass::Handler, (void*)myClassObj );. You can even have the static callback invoke a private member: ((*MyClass)userdata)->RealHandler(event);. It's ugly but it works \$\endgroup\$ Commented Oct 4, 2012 at 18:17
  • 1
    \$\begingroup\$ Thanks QuasarDonkey, you beat me to it. You're right, it's pretty ugly but it's commonly used in the Windows API, pthreads and lower level Mac OS X APIs and... everywhere! I usually hide the free functions that handles the callbacks in my implementation files in the anonymous namespace. C# style delegates calling back to members looks nicer, but it just got in the way of getting things done for me. \$\endgroup\$
    – Neil M
    Commented Oct 4, 2012 at 20:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .