0
\$\begingroup\$

I have set up a class for input handling that allows me to create key bindings with different key behaviours such as single press action bindings where the action will only happen once if the key is held, and state like actions to make things happen more than once.

In the constructor of the key handler class I pass in the value of the key to bind to and a pointer to a callback function to be called as necessary. My problem comes when I try to pass the pointer to the function in to the constructor since the method I am trying to pass is a member function of my main game class.

I know of solutions that involve making the members static but my problem here is that doing so would require for me to change the signature of every method which has to take the pointer as a parameter and doing so would impact upon the re-usability of my input class for other projects.

Are there any other solutions to this problem that wouldn't involve changing the signature of the methods to accommodate the member function?

\$\endgroup\$
1
  • \$\begingroup\$ An option I completely overlooked was to give the key bindings a pointer to the game object and pass this to the callback when it is called. That means I can use static member functions for the callbacks while keeping the input class less tied up with other classes. If anyone has any advice on other probably more elegant solutions, I'd be willing to hear them out :) \$\endgroup\$
    – ctor
    Commented Mar 16, 2013 at 19:43

3 Answers 3

1
\$\begingroup\$

Sounds like you need delegates. Try boost::bind or FastDelegate.

Essentially, these will allow you to write:

boost::function<void()> f = boost::bind(&MyClass::onEscPressed, myClassPtr);

That is, a delegate can act as a pointer to method + pointer to the object on which the method should be invoked. Boost::bind can actually do much more than that, because it can bind any almost any variable and allow functions with arguments and return types (like a clojure):

float pi = 3.14;
function<float(int)> f = (&MyClass::something, myClassPtr, pi, _1);

Don't get carried away, there is a performence cost :) Unnoticeable for input handling, but no so cool for commonly executed code (for example, I got burned by using boost::bind for collision detection once. Silly idea anyways).

\$\endgroup\$
2
  • \$\begingroup\$ Oh I started reading about delegates about 10 minutes ago and didn't think it was relevant. I'll have a look into them although I'm starting to rethink this whole problem as the way it is at the moment I would have to create separate methods to handle individual keys which is not the most elegant of solutions. I think a simpler way would be for the game class to inherit from a class that forced it to implement a specific function, that function would then take the value of the key pressed. Of course the function would still only be called if it met the requirements of the binding for that key. \$\endgroup\$
    – ctor
    Commented Mar 16, 2013 at 19:47
  • \$\begingroup\$ @ctor - re "I would have to create separate methods to handle individual keys" - look at my second example. With boost::bind, you can functions which accept parameters. \$\endgroup\$
    – Liosan
    Commented Mar 17, 2013 at 20:32
1
\$\begingroup\$

This is the approach i've seen implemented in a number of games, and while it isn't a quick and dirty approach, it is a relatively good design I feel. When reacting to input within the game, you want to provide the ability to bundle functionality into particular events or actions, as well as provide access to raw values for something such as mouse positions.

Dispatching raw input is relatively straightforward, and providing the ability to react to specific events allows you to decouple your application code from the input itself. For instance, if you have your "CharacterController" class (which would implement an input listener interface) listening for jump events, then the character controller doesn't care in the slightest bit whether jump is space bar, or left-ctrl. Without this event based decoupling, any changes to your input map would require additional changes to your game specific code.

Writing an input manager that acts as a central dispatcher, and uses an input map (a list of keys mapped to actions) to distribute events to classes that implement an input listener interface is the summary of my proposal. At its simplest the listener is just a pure virtual function accepting an input event, and the input map a set of key/event-list pairs.

\$\endgroup\$
1
\$\begingroup\$

To solve your function signatures problems, people usually use function adapters and binders, you have different options that I can think off:

Using boost::bind:

  • using boost::bind is convenient you can bind almost any function, you can even use member functions as callbacks.
  • regarding performance boost::bind uses alot of small heap allocations which might not be a good idea in a performance critical applications (e.g. as Liosan mentioned Collision Detection). But anyway you should always profile and measure before judging.
  • Will introduce dependency on boost (might not be a problem for you).

  • Is Compatible with boost::function; which makes it even easier to move function objects around.

  • boost::bind is not compatible (directly) with function pointers, so you won't be able to pass functions using bind as function pointers. both Win32 API and SDL use pure function pointers.

  • boost::bind "might" be inlined by the compiler, though boost::function will prevent inlining.

You can use static functions as Adaptors

  • Need to write alot of static functions.

  • No heap allocation.

  • You can still "bind" member functions using the "unsigned int arg" by sending the pointer "this" of your object example:

class C
{
public:
    void EventHandler(void)
    {
        //Your Logic        
    }

    static void OnHelper(unsigned long context) // you can send this as callback, and internally call your member function.
    {
      C* thisInstance  = reinterpret_cast<C*>(context); // Crash if used the wrong type
      thisInstance->EventHandler();
    }
};

Writing your own function object adapter

  • You can customize it for your specific needs (example make it directly compatible with function pointers, be careful that the standard directly states that function pointers should not be stored in data pointers).

  • You may only need 2-3 parameters avoiding all the complexity boost::bind provides.

  • Will learn a couple of things (in case you have time).

  • Can be hard and/or time consuming.

  • No boost dependency.

as a side note Using STL Solution

  • STL introduced std::mem_fun, std::bind1st, std::2nd which can be useful in your case.

  • C++11 introduced lambda expressions (also part of boost) but I don't have experience with either.

\$\endgroup\$

You must log in to answer this question.

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