Понижение базового класса без шаблона до шаблонного производного класса: возможно ли это? - PullRequest
5 голосов
/ 05 марта 2010

Я внедряю систему событий для игры. Он использует очередь событий и структуру данных для хранения всех зарегистрированных обработчиков событий для данного типа события. До сих пор она работала нормально, регистрируя обработчики, но когда дело доходит до отмены их регистрации (что происходит, например, при уничтожении игрового объекта), у меня возникают небольшие проблемы с шаблонами и приведением типов.

Я определил EventHandler как своего рода функтор, частично основанный на статье Шимона Гатнера о 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;
};

Тогда начальная реализация метода unregisterHandler () в классе EventManager, о котором я думал, будет выглядеть так:

// 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, реализуйте абстрактный метод «равный» следующим образом:

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 ]

2 голосов
/ 05 марта 2010

Как правило, при закрытии шаблонов необходимо убедиться, что> разделены пробелами, чтобы компилятор не анализировал их как оператор сдвига вправо.

Здесь вы пытаетесь выполнить статическое приведениессылка на не-ссылку, которая, даже если бы она работала, могла вызвать нарезку объекта.Вам необходимо выполнить статическое приведение к производной ссылке.

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 голосов
/ 05 марта 2010

Спасибо вам обоим, Марку и Стиву: это привело меня в правильном направлении. Была еще одна проблема, так как я пытался привести констант к неконстантам, но эту проблему было намного легче обнаружить после этого.

Это то, что я получил в итоге, после нескольких настроек:

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

Теперь все работает нормально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...