Method pointer in define_key

I’ve been trying to migrate a little panda project of mine from python to c++ for performance and maintainability reasons. Being a Java-programmer, i try to keep things as object-oriented as i can.

However, i’m stuck trying to migrate my main game class which basically defines event handlers as methods. This works well in python, but seems to cause troubles in C++.

Is there a way to pass a method as a parameter, rather than a function, when adding a keyboard-event?

Thank you.

There are two ways to do this that I am aware of, depending on the type of method.

In the code examples below, I’ll assume our class that has the callbacks is

class EventHandlers
{
public:
	void newButtonCallback(const Event *ev, void * data);
	static void ExitProgram(const Event *ev, void * data);
};

void EventHandlers::newButtonCallback(const Event *ev, void * data)
{ 			
	nout << "Non Static Event Handler";	
}

void EventHandlers::ExitProgram(const Event *ev, void *data)
{ 	
	nout << "request to quit";
	//framework.close_all_windows();
	framework.close_framework();	
}

Static Methods
The easiest and most straight-foward way is to use static methods - For static methods, it is pretty simple to make a call - just use the qualified name (in this case, EventHandlers::ExitProgram):

framework.define_key("escape","QuitTheProgram",EventHandlers::ExitProgram,NULL);

If you need to pass an object to the handler, you can always include it in the data variable

EventHandlers ObjectToUse;
framework.define_key("escape","QuitTheProgram",EventHandlers::ExitProgram,&ObjectToUse);

Non-Static Methods
If, however, you want to use non-static methods, it gets a little more complicated. There are several ways to do this, I show one below.

First we define a class to contain the static method that is actually called and that can make the call to the non-static method. Here we will define a generic class that can be used for any callback method.

template <class T>
class StaticCallback	
{
public:
  //This defines the type of the method that we are going to call.
  //Note that Although I define the exact same arguments as the EventHandler, this is not a requirement
  //For those unfamiliar with the syntax, this line defines a new type called "MethodToCall" that is a method that
  // is a member of class T that returns a void and takes the arguments (const Event*, void*).  
 typedef void (T::*MethodToCall)(const Event*, void *);

 //This is a structure we use to pass the actual data about which method and class to call.
 struct CallbackDef
 {
	 // Object to call
	 T * TargetObject;
	 // Method to call
	 MethodToCall TargetMethod;
	 // This is arbitrary data to pass
	 void * TargetData;
	 
	 //Constructor
	 CallbackDef(T* pTarget, MethodToCall fMethod, void * pData)
	 {
		 TargetObject = pTarget;
		 TargetMethod = fMethod;
		 TargetData = pData;
	 }	 
 };

 //This is the actual callback that we will be calling - it makes the call to our object.
 static void Execute(const Event * ev, void * Data)
 {
	 //The only valid data is a callbackDef structure
	 CallbackDef * pDef = (CallbackDef*)Data;

	 //This line can be broken down into 3 parts.
	 // (pDef->TargetObject)->			We are calling a method using the TargetObject object.
	 // *(pDef->TargetMethod)			We are calling the TargetMethod method
	 // (ev,pDef->TargetData)			Arguments to our method call.
	 ((pDef->TargetObject)->*(pDef->TargetMethod))(ev,pDef->TargetData);
 }
};

In the code that sets up the define_key, there is a little extra work that needs to be done as well.

//Create an object, so we have something to call
	EventHandlers EH;

	//Here we define our CallbackDef object.  This is an object of type
	//StaticCallback<EventHandlers>::CallbackDef  which will define a call to a method in an EventHandlers class
	//Our TargetOject is EH
	//Our TargetMethod is EventHandlers::newButtonCallback
	//And we are not passing any extra data
	StaticCallback<EventHandlers>::CallbackDef oDefinition(&EH,&(EventHandlers::newButtonCallback),NULL);
	
	//Define our call to the StaticCallback<T>::Execute method and pass in our CallbackDef object.
	framework.define_key("space","GotA",StaticCallback<EventHandlers>::Execute,&oDefinition);

One nice thing about this implementation is that it is possible to change the object and even the method assigned to that event on the fly (by just changing (in this example), the pointers in oDefinition.). On the other hand, it is definately more involved than the other method, although there are several ways to make this simpler from a usage standpoint.