Pergunta

Eu estou tentando escrever um sistema de evento de retorno de chamada em DirectX9. Eu estou tentando Função Método de utilização ponteiros para eventos de disparo para mouseclicks; mas eu estou tendo alguns problemas. Meu jogo usa um gerente gamestate para gerenciar a prestação. Todos os meus gamestates são derivados de uma AbstractGameState classe base.

Eu tenho um objeto sprite com este método específico:

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

MainMenuState é o gamestate atual que meu jogo está dentro, e StartGame é uma parte método vazio desta classe. Eu gostaria de armazenar o ponteiro de função em uma variável dentro do meu classe Sprite para que eu possa executá-lo quando o usuário clica.

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

Eu tentei downcasting o ponteiro, mas isso realmente não funciona.

Meu classe Sprite também contém essas duas variáveis ??

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

Qualquer ajuda seria apreciada

Foi útil?

Solução

Eu realmente não sei por que a sua atribuição não está funcionando, não litb dúvida não demorará a chegar a explicar porquê. Boost.Function é uma bela, genérico, typesafe e objeto de função padrão que pode ser usado como um substituto para ponteiros de função em quase todas as circunstâncias. Eu faria isso:

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

Note que a classe de evento agora encapsula o estado da chamada de função, incluindo o objeto que você quiser chamá-lo por diante. Normalmente você também usar Boost.Bind para criar uma fechamento, mas você pode facilmente ligar-se a uma função livre ou algum outro objeto de função também.

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

chamá-lo assim:

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

Com este esquema, você não realmente precisa para armazenar o "estado do jogo ligado." - este é encapsulado no objeto de evento

Outras dicas

Eu tive um problema muito semelhante. Se eu estou lendo isso corretamente você está tentando escrever um sistema quase como um ActionEvent em Java. Que poderia ficar assim:

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

Em C ++, não é uma coisa muito semelhante a fazer como demonstrado abaixo.

Na minha opinião, este é muito melhor porque você pode realmente reutilizar as chamadas de método e modificá-los à vontade, o que lhe dá muito mais flexibilidade.

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

O problema aqui é que StartGame não pode ser chamada com qualquer instância GameState usando seu parâmetro this. É apenas por pode chamado com um parâmetro this do tipo MainMenuState.

Para ter um ponto void (GameState::*)() a um método definido na MainMenuState, o método deve ser um método virtual que também é definido na GameState.

Eu recomendaria que, em vez de tentar armazenar um ponteiro função membro, armazenar um "objeto de comando" (ou functor) ponteiro, usando algo como:

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

E, em seguida, definir uma implementação 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)();
};

Você pode, então, definir um membro do tipo Callback *.

Por que você está usando uma função de modelo lá? OnClick não irá compilar, ou trabalho, para valores de T outros que gamestate.

No entanto, nesta situação eu geralmente não iria usar um ponteiro para função de membro. Eu criar uma função objeto , sobrecarregando operator(), e passar aqueles em torno vez. Isto é melhor:. A sintaxe é mais claro, e você pode armazenar algum estado no objeto de função, você deve achar que você precisa

Você deve ter o parâmetro como:

void (GameState::*EventPointer)()

Porque uma vez que é um ponteiro, que não pode ser abatido assim (ele não tem nenhuma informação a respeito de se o mesmo ponteiro existe na classe base). As funções terão de estar em estado de jogo para que este trabalho (potencialmente virtual)

No entanto! Desde que você está fazendo chamadas de retorno (basicamente um padrão de observador) que você pode dar uma boa olhada em boost :: sinais . Ele vai fazer tudo isso (e muito mais). Você não está amarrado em ter que adicionar funções para 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top