views:

259

answers:

4

Hello,

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

+1  A: 

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.

David Seiler
A: 

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.

1800 INFORMATION
Thanks for all of the replies; eventually used this method - Worked like a charm!
Chaoz
A: 

The problem here is that StartGame can't be called with any GameState instance as it's "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 that instead you 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 *.

Scott Wisniewski
A: 

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));
Todd Gardner