Question

I'm trying to write a callback event system in DirectX9. I'm attempting to use method function pointers to trigger events to mouseclicks; but I'm having some problems. My game uses a gamestate manager to manage the rendering. All of my gamestates are derived from a base class AbstractGameState.

I have a sprite object with this specific method:

m_NewGameSprite->OnClick(this, &MainMenuState::StartGame);

MainMenuState is the current gamestate that my game is in, and StartGame is a void method part of this class. I'd like to store the function pointer in a variable within my sprite class so that I can execute it when the user clicks.

template <typename T>
void OnClick(GameState* LinkedState, void (T::*EventPointer)())
{
    m_LinkedGameState = LinkedState;
    m_EventPointer = EventPointer; // <- Doesnt compile
}

I've tried downcasting the pointer, but that didn't really work.

My sprite class also contains these two variables

void                (GameState::*m_EventPointer)();
GameState*          m_LinkedGameState;

Any help would be appreciated

Was it helpful?

Solution

I don't really know why your assignment is not working, no doubt litb will be along shortly to explain why. Boost.Function is a beautiful, generic, typesafe and standard function object that can be used as a replacement for function pointers in almost all circumstances. I would do this:

typedef boost::function0<void> Event;
Event m_Event;

Note that the event class now encapsulates the state of the function call, including the object you want to call it on. Normally you also use Boost.Bind to create a closure, but you can easily bind to a free function or some other function object as well.

void OnClick(Event &event)
{
  m_Event=event;
}

Call it like this:

OnClick(boost::bind(&MainMenuState::StartGame, this));

With this scheme, you don't really need to store the "linked game state" - this is encapsulated in the event object.

OTHER TIPS

I had a very similar issue. If i'm reading this correctly you are attempting to write a system almost like an ActionEvent in Java. Which could look like this :

Component.addActionListener( new ActionEventListener( new ActionEvent(
            public void execute() { /* Component clicked Do Something */ } )));

In C++, there is a very similar thing to do as demonstrated below.

In my opinion, this is far better because you can actually reuse the method calls and modify them at will, which gives you much more flexibility.

Event.hpp

#pragma once

class Event 
{
    public:
        Event(void) { }
        ~Event(void) { }

    public:
        //Stores a function to callback. Example shown in Main.cpp
        void operator += (void (*callback)())
        {
            this->callback = callback;
        }

        //Executes our stored call back method at any given time.
        void execute(void)
        {
            callback();
        }

        //Variable of type VOID* -> This is a function which is stored + executed.
        private:
            void (*callback)();
};

Main.cpp

#include <iostream>
#include "Event.hpp"

using namespace std;

void print(void)
{
    cout << "Hello This Works" << endl;
}

void print2(void)
{
    cout << "Success again!" << endl;
}

int main()
{
    Event task_event;
    task_event += print;
    task_event.execute();
    task_event += print2;
    task_event.execute();
    system("pause");
    return 0;
}

The problem here is that StartGame can't be called with any GameState instance using its this parameter. It can only by called with a this parameter of type MainMenuState.

To have a void (GameState::*)() point to a method defined in MainMenuState, the method must be a virtual method that is also defined in GameState.

I would recommend that instead of trying to store a member function pointer, store a "command object" (or functor) pointer, using something like:

class Callback
{
public:
    virtual void Execute() = 0;
};

And then defining an implementation like this:

template <typename TClass>
class MemberCallback : public Callback
{
public:

    MemberCallBack(TClass * pThis, void (TClass::*pFunc)() )
    {
        m_pThis = pThis;
        m_pFunc = pFunc;
    }
    void Execute()
    {
        m_pThis->*m_pFunc();
    }

private:
    TClass * m_pThis;
    void (TClass::*m_pFunc)();
};

You can then define a member of type Callback *.

Why are you using a template function there? OnClick won't compile, or work, for values of T other than GameState.

However, in this situation I generally wouldn't use a pointer to member function. I'd create a function object, overloading operator(), and pass those around instead. This is better: the syntax is clearer, and you can store some state in the function object, should you find that you need to.

You should take the parameter as a:

void (GameState::*EventPointer)()

Because once it's a pointer, it can't be downcast like that (it has no information as to if the same pointer exists in the base class). The functions will have to be on GameState for this to work (potentially virtual)

However! Since you are doing callbacks (basically an observer pattern) you might take a long look at boost::signals. It will do all this (and more). You aren't tied into having to add functions to GameState.

class Foo {
 // ...
 template <typename T>
 boost::signals::connection OnClick(const boost::function<void ()>& func)
 {
    return sig.connect(func);
 }

 void FireClick() { sig_(); }
private:
  boost::signal<void ()> sig_;
};

int this_will_be_called() { }

Foo f = // ...;

f.OnClick(boost::bind(&this_will_be_called));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top