2010-03-05 14 views
5

Estoy implementando un sistema de eventos para un juego. Utiliza una cola de eventos y una estructura de datos para contener todos los controladores de eventos registrados para un tipo de evento determinado. Hasta ahora funciona bien registrando controladores, pero cuando se trata de anular el registro (algo que sucederá cuando se destruye un objeto de juego, por ejemplo) tengo problemas con las plantillas y el molde.Downcasting clase base no de plantilla a clase derivada de plantilla: ¿es posible?

He definido un manejador de eventos como un tipo de functor, parcialmente basado en el artículo de Szymon Gatner en http://www.gamedev.net/reference/programming/features/effeventcpp/. Para ser precisos, tomé las definiciones de clase y HandlerFunctionBase MemberFunctionHandler y se acercó con:

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

A continuación, la aplicación inicial del método unregisterHandler() en la clase EventManager he pensado iría así:

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

para hacer "eliminar" trabajo aquí pensé de sobrecargar el operador == para BaseEventHandler, y luego usando un método virtual para realizar la comparación real ...

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

y, por el manejador de sucesos clase de plantilla, implementar el método abstracto 'igual' de esta manera:

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

Por supuesto, estoy recibiendo un error de compilación en la línea static_cast. Ni siquiera estoy seguro de que sea posible hacer ese lanzamiento (no necesariamente usando static_cast). ¿Hay alguna manera de llevarlo a cabo, o al menos alguna solución alternativa que solucione el problema?

Gracias de antemano =)

+1

Al menos podría convertir a un tipo de referencia en lugar de 'derivedOther' tratando de ser una * copia * del parámetro" otro ". Me temo que su pregunta es demasiado larga para que yo la entienda completamente en este momento, por lo que no estoy seguro de si esto es un problema secundario menor o la respuesta correcta. Consejo general: recuerde que dos clases diferentes de EventHandler con diferentes parámetros de plantilla no están relacionadas, sino que se crean con la misma fuente. No hay ningún tipo como 'EventHandler ' a menos que se especifiquen T y TEvent, por lo que 'other' sería mejor que sea de la misma clase que' this'. –

+0

Debe incluir el error de compilación que obtiene. Otra nota: ¿hay alguna razón por la cual 'igual()' no está declarado en la clase de plantilla 'EventHandler'? –

Respuesta

2

En general, cuando el cierre de las plantillas, lo que necesita para asegurarse de que> están separados por espacios para que el compilador no analiza como un operador de desplazamiento a la derecha.

Aquí está tratando de transmitir una referencia estática a una no referencia, que incluso si funcionara podría invocar el corte de objetos. Necesita conversión estática a una referencia derivada.

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

Gracias a los dos, Mark y Steve: eso me condujo en la dirección correcta. Hubo otro problema ya que estaba tratando de lanzar un const a un no-const pero este fue mucho más fácil de detectar después de eso.

Esto es lo que tengo al final, después de algunos ajustes:

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

me cambió eliminar con remove_if porque impulso :: shared_ptr implementa el operador == comparando directamente la punteros, en lugar de sus contenidos. El terriblemente nombrado EventHandlerComparisonFunctor es ahora el responsable de realizar la verificación de igualdad.

Se implementa como esto:

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

Y, por último, el método iguales en manejador de sucesos (@gf, el método fue declarado de hecho en la plantilla manejador de sucesos, pero por alguna razón por la que ya basta de pegar el código de clase aquí, mi error)

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

Todo está funcionando bien ahora.

Cuestiones relacionadas