2010-03-05 43 views
5

我正在实现一个游戏的事件系统。它使用事件队列和数据结构来保存给定事件类型的所有注册事件处理程序。它到目前为止工作正常,注册处理程序,但是当涉及到注销它们(例如,当游戏对象被销毁时将发生的事情),我在模板和铸造方面有点麻烦。将非模板基类向下转换为模板派生类:是否有可能?

我已经定义了一个EventHandler作为某种函数,部分基于Szymon Gatner的文章http://www.gamedev.net/reference/programming/features/effeventcpp/。准确地说,我把HandlerFunctionBase和MemberFunctionHandler类的定义和想出了:

class BaseEventHandler 
{ 
public: 
    virtual ~BaseEventHandler(){} 
    void handleEvent(const EventPtr evt) 
    { 
     invoke(evt); 
    } 
private: 
    virtual void invoke(const EventPtr evt)=0; 
}; 

template <class T, class TEvent> 
class EventHandler: public BaseEventHandler 
{ 
    public: 
    typedef void (T::*TMemberFunction)(boost::shared_ptr<TEvent>); 
    typedef boost::shared_ptr<T> TPtr; 
    typedef boost::shared_ptr<TEvent> TEventPtr; 

    EventHandler(TPtr instance, TMemberFunction memFn) : mInstance(instance), mCallback(memFn) {} 

    void invoke(const EventPtr evt) 
    { 
     (mInstance.get()->*mCallback)(boost::dynamic_pointer_cast<TEvent>(evt)); 
    } 
    TPtr getInstance() const{return mInstance;} 
    TMemberFunction getCallback() const{return mCallback;} 

    private: 
     TPtr mInstance; 
    TMemberFunction mCallback; 
}; 

然后对我想过的eventmanager进行类unregisterHandler()方法,初步实现会是这样的:

// EventHandlerPtr is a boost::shared_ptr<BaseEventHandler>. 
// mEventHandlers is an STL map indexed by TEventType, where the values are a std::list<EventHandlerPtr> 
void EventManager::unregisterHandler(EventHandlerPtr hdl,TEventType evtType) 
{ 
    if (!mEventHandlers.empty() && mEventHandlers.count(evtType)) 
    { 
     mEventHandlers[evtType].remove(hdl); 
     //remove entry if there are no more handlers subscribed for the event type 
    if (mEventHandlers[evtType].size()==0) 
     mEventHandlers.erase(evtType); 
    } 
} 

使“删除”在这里工作我想重载==操作符BaseEventHandler,然后使用虚拟方法来执行实际比较的......

bool BaseEventHandler::operator== (const BaseEventHandler& other) const 
{ 
    if (typeid(*this)!=typeid(other)) return false; 
    return equal(other); 
} 

,并在模板类事件处理程序,实现抽象方法“平等”是这样的:

bool equal(const BaseEventHandler& other) const 
{ 
    EventHandler<T,TEvent> derivedOther = static_cast<EventHandler<T,TEvent>>(other); 
    return derivedOther.getInstance() == this->getInstance() && derivedOther.getCallback()==this->getCallback(); 
} 

当然,我上线的static_cast得到一个编译错误。我甚至不确定是否有可能做到这一点(不一定使用static_cast)。有没有一种方法来执行它,或者至少有一些解决方法可以做到这一点?

在此先感谢=)

+1

您至少可以转换为引用类型而不是'derivedOther',试图成为“其他”参数的*副本*。我担心你的问题对于我现在彻底明白这一点有些太长了,所以我不确定这是一个小问题还是正确的答案。一般建议:请记住,具有不同模板参数的两个不同的EventHandler类是不相关的,它们恰好是使用相同的源创建的。除非指定T和TEvent,否则没有'EventHandler '这样的类型,所以'other'最好与'this'具有相同的类。 – 2010-03-05 17:31:01

+0

你应该包含你得到的编译错误。另一个注意:为什么'equal()'没有在模板类EventHandler中声明? – 2010-03-05 17:42:16

回答

2

一般来说关闭模板时,你需要确保>用空格分开所以编译器不分析它们作为右移位运算符。

在这里你试图静态转换一个引用到一个非引用,即使它的工作可以调用对象切片。您需要将静态转换为派生参考。

bool equal(const BaseEventHandler& other) const 
{ 
    EventHandler<T,TEvent>& derivedOther = static_cast<EventHandler<T,TEvent>&>(other); 
    return derivedOther.getInstance() == this->getInstance() && derivedOther.getCallback()==this->getCallback(); 
} 
0

谢谢马克和史蒂夫两人:这让我朝着正确的方向前进。还有一个问题,因为我试图将一个const强制转换为一个非常量,但是在这之后这个更容易被发现。

这是我到底已经有了,一些调整后:

void EventManager::unregisterHandler(EventHandlerPtr hdl,TEventType evtType) 
{ 
    if (!mEventHandlers.empty() && mEventHandlers.count(evtType)) 
    { 
     TEventHandlerList::iterator newEnd=remove_if(mEventHandlers[evtType].begin(), 
      mEventHandlers[evtType].end(),EventHandlerComparisonFunctor(hdl)); 
     mEventHandlers[evtType].erase(newEnd,mEventHandlers[evtType].end()); 
     if (mEventHandlers[evtType].size()==0) 
      mEventHandlers.erase(evtType); 
    } 
} 

改变删除的remove_if因为升压:: shared_ptr的通过直接比较实现==操作符指针,而不是它们的内容。可怕的EventHandlerComparisonFunctor现在负责执行相等性检查。

它的实现是这样的:

class EventHandlerComparisonFunctor 
{ 
private: 
    EventHandlerPtr mInstance; 
public: 
    EventHandlerComparisonFunctor(EventHandlerPtr instance):mInstance(instance){} 
    bool operator()(EventHandlerPtr& other) const 
    { 
     return *(mInstance.get())==*(other.get()); 
    } 
}; 

最后,在事件处理程序的平等法(@gf,该方法确实在事件处理程序模板声明,但由于某些原因,我把它剪出来粘贴类代码在这里,我的错误)

bool equal(const BaseEventHandler& other) const 
{ 
    EventHandler<T,TEvent>& derivedOther = static_cast<EventHandler<T,TEvent>&>(const_cast<BaseEventHandler&>(other)); 
    return derivedOther.getInstance() == this->getInstance() && derivedOther.getCallback()==this->getCallback(); 
} 

一切工作正常。