Domanda

Sto cercando di scrivere un callback sistema di eventi in DirectX9.Sto tentando di utilizzare il metodo di puntatori a funzione per l'attivazione di eventi di click;ma sto avendo alcuni problemi.Il mio gioco utilizza un controllore manager per gestire il rendering.Tutti i miei gamestates sono derivati da una classe base AbstractGameState.

Ho un oggetto sprite con questo metodo:

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

MainMenuState è l'attuale controllore che il mio gioco è in, e StartGame è un metodo void parte di questa classe.Vorrei memorizzare il puntatore a funzione in una variabile all'interno della mia classe sprite in modo che è possibile eseguire quando l'utente fa clic.

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

Ho provato il downcast il puntatore, ma che non ha funzionato.

La mia classe sprite contiene anche queste due variabili

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

Qualsiasi aiuto sarebbe apprezzato

È stato utile?

Soluzione

Io non so davvero perché il tuo compito non è di lavoro, senza dubbio litb sarà lungo poco per spiegare il perché. Boost.Funzione è una bella, generico, typesafe e standard funzione di oggetto che può essere usato come un sostituto per i puntatori a funzione in quasi tutte le circostanze.Vorrei fare questo:

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

Si noti che l'evento classe incapsula lo stato della chiamata di funzione, tra cui l'oggetto che si desidera chiamare su di esso.Normalmente si utilizza anche Boost.Bind per creare una chiusura, ma si può facilmente associare a una funzione o qualche altra funzione oggetto.

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

Chiamata così:

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

Con questo schema, non si ha realmente bisogno di conservare i "collegati stato di gioco" - questo è incapsulato nel caso in oggetto.

Altri suggerimenti

Ho avuto un problema simile.Se ho letto bene si sta tentando di scrivere un sistema quasi come un ActionEvent in Java.Che potrebbe assomigliare a questo :

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

In C++, c'è una cosa simile per fare, come illustrato di seguito.

A mio parere, questo è molto meglio perché si possono riutilizzare le chiamate di metodo e di modificarli a piacimento, che offre una maggiore flessibilità.

Evento.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;
}

Il problema qui è che StartGame non possono essere chiamati con qualsiasi GameState istanza utilizzando il suo this parametro.Si può solo chiamato con un this parametro di tipo MainMenuState.

Per avere un void (GameState::*)() scegliere un metodo definito in MainMenuState, il metodo deve essere un metodo virtuale che è definito anche in GameState.

Vorrei raccomandare che invece di cercare di memorizzare un puntatore a funzione membro, archivio di un comando "oggetto" (o funtore) puntatore, utilizzando qualcosa di simile:

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

E poi la definizione di un'implementazione simile a questo:

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

Si può quindi definire un membro di tipo Callback *.

Perché stai usando un template di funzione? OnClick non compilano, o di lavoro, per i valori di T altri di Controllore.

Tuttavia, in questa situazione io di solito non utilizzare un puntatore a funzione membro.Vorrei creare una funzione oggetto, sovraccaricando operator(), e passare a quelli invece.Questo è meglio:la sintassi è più chiara, e si possono memorizzare alcuni lo stato in funzione di oggetto, si dovrebbe trovare che è necessario.

Si dovrebbe prendere il parametro:

void (GameState::*EventPointer)()

Perché una volta che è un puntatore, non può essere abbattuto come che (non ha le informazioni per se stesso puntatore esiste nella classe base).Le funzioni di Controllore per questo lavoro (potenzialmente virtuale)

Però!!!Dal momento che si sta facendo richiamate (in pratica un pattern observer) si può prendere un lungo sguardo alla boost::segnali.Farà tutto questo (e molto altro).Che non sono legate in avere per aggiungere funzioni di Controllore.

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));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top