Frage

Ich versuche, ein Callback-Ereignissystem in DirectX9 zu schreiben. Ich versuche Methode Funktionszeiger zu verwenden, um Ereignisse zu Mausklicks auszulösen; aber ich habe einige Probleme. Mein Spiel verwendet einen gamestate Manager die Wiedergabe zu verwalten. Alle meine gamestates von einer Basisklasse AbstractGameState abgeleitet werden.

Ich habe ein Sprite-Objekt mit dieser speziellen Methode:

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

MainMenuState ist die aktuelle gamestate, dass mein Spiel in ist, und StartGame ist ein void-Methode ein Teil dieser Klasse. Ich mag die Funktionszeiger in einer Variablen innerhalb meiner Sprite-Klasse speichern, so dass ich es ausführen kann, wenn der Benutzer klickt.

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

Ich habe versucht, den Zeiger einziehenden, aber das ist nicht wirklich funktioniert.

Meine Sprite-Klasse enthält auch diese beiden Variablen

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

Jede mögliche Hilfe geschätzt

War es hilfreich?

Lösung

Ich weiß wirklich nicht, warum Ihre Zuordnung nicht funktioniert, kein Zweifel litb in Kürze zusammen sein wird, zu erklären, warum. Boost.Function ist ein schönes, generisch, typsicher und Standard-Funktionsobjekt, das als Ersatz für Funktionszeiger in fast allen Umständen verwendet werden. Ich würde dies tun:

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

Beachten Sie, dass die Ereignisklasse jetzt den Zustand der Funktionsaufruf kapselt, einschließlich des Objekts, das Sie es nennen wollen auf. Normalerweise verwenden Sie auch Boost.Bind eine erstellen Verschluss, aber Sie können leicht binden an eine Freisprechfunktion oder eine andere Funktion Objekt als auch.

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

Nennen Sie es wie folgt aus:

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

Mit diesem Schema Sie nicht wirklich brauchen, die „verbunden Spielzustand“ speichern - dies im Ereignisobjekt verkapselt ist

.

Andere Tipps

Ich hatte ein sehr ähnliches Problem. Wenn ich dies gelesen habe richtig versuchen Sie, ein System zu schreiben fast wie ein ActionEvent in Java. Welche könnte wie folgt aussehen:

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

In C ++ gibt es eine sehr ähnliche Sache zu tun, wie unten gezeigt.

Meiner Meinung nach, ist dies viel besser, weil Sie tatsächlich die Methodenaufrufe wiederverwenden und ändern können sie nach Belieben, die Sie viel mehr Flexibilität.

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

Das Problem hierbei ist, dass StartGame kann nicht mit GameState Instanz mit seinen this Parametern aufgerufen werden. Sie kann nur durch mit einem this Parameter des Typs MainMenuState genannt.

Um einen void (GameState::*)() Punkt auf ein Verfahren in MainMenuState definiert zu haben, muss die Methode eine virtuelle Methode, die auch in GameState definiert ist.

Das würde ich empfehlen, anstatt zu versuchen, einen Mitglied Funktionszeiger zu speichern, speichern Sie ein „Befehlsobjekt“ (oder Funktors) Zeiger, etwas mit wie:

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

Und dann eine Implementierung wie folgt definieren:

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

Sie können dann definieren Sie ein Mitglied des Typs Callback *.

Warum verwenden Sie eine Template-Funktion gibt? OnClick wird nicht kompiliert, oder Arbeit, für Werte von T andere als GameState.

Doch in dieser Situation würde ich im Allgemeinen nicht einen Zeiger auf Member-Funktion verwenden. Ich würde eine Funktion Objekt , Überlastung operator(), erstellen und die um stattdessen passieren. Das ist besser. Die Syntax ist klarer, und Sie können einigen Zustand in der Funktion Objekt speichern, sollten Sie feststellen, dass Sie brauchen, um

Sie sollten die Parameter als einen nehmen:

void (GameState::*EventPointer)()

Denn wenn es ein Zeiger ist, kann es nicht so niedergeschlagen werden (es hat keine Angaben darüber, ob der gleiche Zeiger besteht in der Basisklasse). Die Funktionen werden auf GameState sein müssen für diese (möglicherweise virtuell) zu arbeiten

Allerdings! Da Sie Rückrufe tun (im Grunde ein Beobachter-Muster) Sie könnten einen langen Blick auf boost :: Signale . Es wird dies alles (und mehr). Sie sind nicht gebunden in mit Funktionen zu GameState hinzuzufügen.

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));
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top