将非模板基类向下转换为模板化派生类:是否可能?

时间:2010-03-05 17:27:59

标签: c++ templates event-handling casting

我正在为游戏实施一个事件系统。它使用事件队列和数据结构来保存给定事件类型的所有已注册事件处理程序。到目前为止注册处理程序工作正常,但是当取消注册它们时(例如,当游戏对象被销毁时会发生这种情况)我对模板和转换有点麻烦。

我已将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);
}

并且,在模板类EventHandler上,实现抽象方法'equal',如下所示:

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)。有没有办法执行它,或者至少有一些解决办法?

提前致谢=)

2 个答案:

答案 0 :(得分:2)

通常,在关闭模板时,您需要确保&gt;由空格分隔,因此编译器不会将它们解析为右移运算符。

在这里,您尝试静态转换对非引用的引用,即使它工作也可以调用对象切片。您需要静态强制转换为派生引用。

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

答案 1 :(得分:0)

谢谢你们,马克和史蒂夫:这让我朝着正确的方向前进。还有另一个问题,因为我试图将一个const转换为非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 更改删除,因为boost :: shared_ptr通过直接比较指针而不是其内容来实现==运算符。现在,极其命名的EventHandlerComparisonFunctor负责执行相等性检查。

它是这样实现的:

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

最后,在EventHandler中使用了相等的方法( @gf ,该方法确实是在EventHandler模​​板中声明的,但出于某种原因我将其删除以将类代码粘贴到此处,我的错误)

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

现在一切正常。