2009-06-25 11 views
1

J'essaye d'écrire un système d'événement de rappel dans DirectX9. J'essaye d'employer des pointeurs de fonction de méthode pour déclencher des événements aux aperçus de souris; mais j'ai quelques problèmes. Mon jeu utilise un manager de jeu pour gérer le rendu. Tous mes gamestats sont dérivés d'une classe de base AbstractGameState.Pointeur de fonction de membre polymorphe

J'ai un objet Sprite avec cette méthode spécifique:

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

MainMenuState est le gamestate courant que mon jeu est en et StartGame est une partie de la méthode de vide de cette classe. Je voudrais stocker le pointeur de fonction dans une variable dans ma classe d'image-objet afin que je puisse l'exécuter lorsque l'utilisateur clique. J'ai essayé le downcasting du pointeur, mais cela n'a pas vraiment fonctionné.

Ma classe de sprite contient également ces deux variables

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

Toute aide serait appréciée

+2

Quelle est l'erreur que vous obtenez? – heavyd

Répondre

2

Je ne sais pas vraiment pourquoi votre mission ne fonctionne pas, il ne fait aucun doute que Litb sera bientôt là pour vous expliquer pourquoi. Boost.Function est un bel objet générique, typeafe et standard qui peut être utilisé en remplacement des pointeurs de fonction dans presque toutes les circonstances. Je ferais ceci:

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

Notez que la classe d'événements encapsule maintenant l'état de l'appel de fonction, y compris l'objet que vous voulez l'appeler sur. Normalement, vous utilisez également Boost.Bind pour créer une fermeture, mais vous pouvez également lier facilement une fonction libre ou un autre objet fonction.

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

appel comme ceci:

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

Avec ce régime, vous ne vraiment pas besoin de stocker l ' « état de jeu lié » - c'est encapsulé dans l'objet de l'événement.

+0

Merci pour toutes les réponses; finalement utilisé cette méthode - Travaillé comme un charme! – Charles

0

Pourquoi utilisez-vous une fonction de modèle il? OnClick ne compilera pas, ou ne travaillera pas, pour des valeurs de T autres que GameState.

Cependant, dans cette situation, je n'utiliserais généralement pas de pointeur vers la fonction membre. Je créerais une fonction objet, en surchargeant operator(), et passer les autour à la place. C'est mieux: la syntaxe est plus claire, et vous pouvez stocker un certain état dans l'objet fonction, si vous trouvez que c'est nécessaire.

1

Le problème ici est que StartGame ne peut pas être appelée avec n'importe quelle instance GameState utilisant son paramètre this. Il ne peut être appelé qu'avec un paramètre this de type MainMenuState.

Pour avoir un point void (GameState::*)() à une méthode définie dans MainMenuState, la méthode doit être une méthode virtuelle qui est également défini dans GameState.

Je recommande que, au lieu d'essayer de stocker un pointeur de fonction membre, stocker un pointeur « objet de commande » (ou foncteur), en utilisant quelque chose comme:

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

Et puis définir une mise en œuvre comme ceci:

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)(); 
}; 

Vous pouvez ensuite définir un membre de type Callback *.

0

Vous devez prendre le paramètre comme:

void (GameState::*EventPointer)() 

Parce qu'une fois qu'il est un pointeur, il ne peut pas être transtyper comme ça (il n'a pas d'informations quant à savoir si le même pointeur existe dans la classe de base) . Les fonctions devront être sur GameState pour que cela fonctionne (potentiellement virtuel)

Cependant! Puisque vous effectuez des rappels (essentiellement un modèle d'observateur), vous pouvez jeter un coup d'œil à boost::signals. Il fera tout cela (et plus). Vous n'êtes pas obligé d'ajouter des fonctions à 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)); 
1

J'ai eu un problème très similaire. Si je lis ceci correctement, vous essayez d'écrire un système presque comme un ActionEvent en Java. Qui pourrait ressembler à ceci:

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

En C++, il y a une chose très similaire à faire comme démontré ci-dessous. À mon avis, c'est beaucoup mieux parce que vous pouvez réellement réutiliser les appels de méthode et les modifier à volonté, ce qui vous donne beaucoup plus de flexibilité.

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; 
} 
+0

Bienvenue sur SO. Je vous suggère de jeter un oeil à notre FAQ: http://stackoverflow.com/faq pour en savoir plus sur les questions et réponses sur SO. Je ne sais pas si vous l'avez lu, mais j'ai trouvé que la réponse de Scott Wisniewski est plus axée sur la question de l'OP puisqu'il utilise des modèles. De plus, les principes sont les mêmes, alors je vois votre réponse comme un doublon. ;) – ForceMagic

Questions connexes