Pregunta

Estoy tratando de escribir un sistema de eventos de devolución de llamada en DirectX9. Estoy tratando de usar punteros de función de método para activar eventos para hacer clic con el mouse; Pero estoy teniendo algunos problemas. Mi juego usa un administrador de estado de juego para administrar el renderizado. Todos mis juegos se derivan de una clase base AbstractGameState.

Tengo un objeto sprite con este método específico:

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

MainMenuState es el estado de juego actual en el que se encuentra mi juego, y StartGame es un método nulo que forma parte de esta clase. Me gustaría almacenar el puntero de función en una variable dentro de mi clase de sprite para poder ejecutarlo cuando el usuario haga clic.

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

He intentado bajar el puntero, pero eso realmente no funcionó.

Mi clase de sprite también contiene estas dos variables

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

Cualquier ayuda sería apreciada

¿Fue útil?

Solución

Realmente no sé por qué su tarea no está funcionando, sin duda Litb estará en breve para explicar por qué. Boost.Function es una hermosa, genérica, segura de escribir y objeto de función estándar que se puede usar como reemplazo de punteros de función en casi todas las circunstancias. Yo haría esto:

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

Tenga en cuenta que la clase de evento ahora encapsula el estado de la llamada a la función, incluido el objeto al que desea llamarla. Normalmente también utiliza Boost.Bind para crear un cierre, pero también puede enlazar fácilmente a una función libre o algún otro objeto de función.

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

Llámalo así:

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

Con este esquema, realmente no necesitas almacenar el " estado del juego vinculado " - esto está encapsulado en el objeto de evento.

Otros consejos

Tuve un problema muy similar. Si estoy leyendo esto correctamente, está intentando escribir un sistema casi como un ActionEvent en Java. Que podría verse así:

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

En C ++, hay algo muy similar a hacer como se muestra a continuación.

En mi opinión, esto es mucho mejor porque puedes reutilizar las llamadas a métodos y modificarlas a voluntad, lo que te da mucha más flexibilidad.

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

El problema aquí es que no se puede llamar a StartGame con ninguna instancia de GameState usando su parámetro this. Solo se puede llamar con un parámetro MainMenuState de tipo void (GameState::*)().

Para tener un punto Callback * a un método definido en <=>, el método debe ser un método virtual que también se define en <=>.

Recomendaría que en lugar de intentar almacenar un puntero de función miembro, almacene un " objeto de comando " (o functor) puntero, usando algo como:

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

Y luego definiendo una implementación como esta:

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

Luego puede definir un miembro de tipo <=>.

¿Por qué estás usando una función de plantilla allí? OnClick no compilará, ni funcionará, para valores de T que no sean GameState.

Sin embargo, en esta situación, generalmente no usaría un puntero a la función miembro. Crearía una función objeto , sobrecargando operator(), y pasaría esos en su lugar. Esto es mejor: la sintaxis es más clara y puede almacenar algún estado en el objeto de función, en caso de que lo necesite.

Debe tomar el parámetro como:

void (GameState::*EventPointer)()

Porque una vez que es un puntero, no puede ser abatido de esa manera (no tiene información sobre si existe el mismo puntero en la clase base). Las funciones deberán estar en GameState para que esto funcione (potencialmente virtual)

Sin embargo! Como está realizando devoluciones de llamada (básicamente un patrón de observador), puede echar un vistazo a boost :: señales . Hará todo esto (y más). No está obligado a tener que agregar funciones a 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));
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top