题
我试图写在DirectX9的回调事件系统。我试图使用方法函数指针来触发事件鼠标点击;但我有一些问题。我的游戏采用了游戏状态的经理来管理渲染。所有我的gamestates都从基类AbstractGameState的。
我有与此特定方法的sprite对象:
m_NewGameSprite->OnClick(this, &MainMenuState::StartGame);
MainMenuState是当前游戏状态,我的游戏是在,和StartGame是这个类的一个空隙方法的一部分。我想在一个变量的函数指针存储我的精灵类内,使得当用户点击我可以执行它。
template <typename T>
void OnClick(GameState* LinkedState, void (T::*EventPointer)())
{
m_LinkedGameState = LinkedState;
m_EventPointer = EventPointer; // <- Doesnt compile
}
我试着向下转换的指针,但没有真正发挥作用。
我的子画面类还包含这两个变量
void (GameState::*m_EventPointer)();
GameState* m_LinkedGameState;
任何帮助,将理解的
解决方案
我真的不知道为什么你的任务是不工作,无疑litb将沿着很快解释为什么。 来自Boost.Function 是一个美丽的,通用的,类型安全和标准函数对象可以用作函数指针在几乎所有的情况下的替代品。我会做这样的:
typedef boost::function0<void> Event;
Event m_Event;
请注意,该事件类现在封装了函数调用的状态,包括你要调用它的对象。通常情况下,你还可以使用 Boost.Bind 创建封闭,但可以很容易地结合到游离函数或一些其他函数对象以及
void OnClick(Event &event)
{
m_Event=event;
}
通话这样的:
OnClick(boost::bind(&MainMenuState::StartGame, this));
通过这个方案,你并不真正需要存储的“链接游戏状态” - 这是封装在事件对象
。其他提示
我有一个非常类似的问题。如果我正确地读这一点,你正试图写一个系统几乎像Java中的ActionEvent
。这可能是这样的:
Component.addActionListener( new ActionEventListener( new ActionEvent(
public void execute() { /* Component clicked Do Something */ } )));
在C ++中,有做如下表现出非常类似的事情。
在我看来,这是更好,因为你其实可以重用的方法调用和随意修改,这为您提供了更大的灵活性。
的 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;
}
这里的问题是,StartGame
不能与使用它的GameState
参数任何this
实例调用。它只能通过调用类型this
的MainMenuState
参数。
要具有void (GameState::*)()
指向在MainMenuState
定义的方法,该方法必须是在也GameState
定义的虚拟方法。
我建议,而不是试图存储一个成员函数指针,存储“命令对象”(或仿函数)的指针,使用类似:
class Callback
{
public:
virtual void Execute() = 0;
};
和然后定义一个这样的实施方式:
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)();
};
可以再定义类型Callback *
的成员。
为什么要使用模板功能呢? OnClick
不会编译,或工作,对于除游戏状态其它T
的值。
然而,在这种情况下,我通常不使用指针来成员函数。我想创建一个函数的对象,超载operator()
,并通过周围的人,而不是。这是更好:语法更清晰,并且可以存储在函数对象的一些状态,你会发现,你需要
您应该采取的参数作为:
void (GameState::*EventPointer)()
由于一旦它的一个指针,它不能像向下转换(它没有信息以如果在基类中存在相同的指针)。功能将必须是对游戏状态这个工作(可能虚拟)
然而!既然你在做回调(基本上是一个观察者模式),你可能需要一个长时间看的的boost ::信号的。它会做这一切(甚至更多)。您没有绑成具有到函数添加到游戏状态。
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));