2010-03-05 38 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(); 
} 

一切工作正常。