2010-03-05 22 views
5

Sto implementando un sistema di eventi per un gioco. Utilizza una coda eventi e una struttura dati per contenere tutti i gestori di eventi registrati per un determinato tipo di evento. Funziona bene fino ad ora registrare i gestori, ma quando si tratta di annullare la registrazione (qualcosa che si verificherà quando un oggetto di gioco viene distrutto, ad esempio) ho un po 'di problemi per quanto riguarda i modelli e il casting.Downcasting della classe base non-template alla classe derivata basata su modelli: è possibile?

ho definito un EventHandler come una sorta di funtore, in parte sulla base di un articolo di Szymon Gatner su http://www.gamedev.net/reference/programming/features/effeventcpp/. Per essere precisi, ho preso le definizioni HandlerFunctionBase e MemberFunctionHandler di classe e si avvicinò 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; 
}; 

Poi l'implementazione iniziale per il metodo unregisterHandler() sulla classe EventManager Ho pensato sarebbe andata in questo modo:

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

di fare "Rimuovi" lavoro qui ho pensato di sovraccaricare l'operatore == per BaseEventHandler, e quindi utilizzando un metodo virtuale per eseguire il confronto vero e proprio ...

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

e, sulla EventHandler classe template, implementare il metodo astratto 'uguale' come questo:

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

Naturalmente, sto ottenendo un errore di compilazione sulla linea static_cast. Non sono nemmeno sicuro che sia possibile fare quel cast (non necessariamente usando static_cast). C'è un modo per farlo, o almeno qualche soluzione che fa il trucco?

Grazie in anticipo =)

+1

È possibile eseguire il cast di almeno un tipo di riferimento piuttosto che 'derivedOther' cercando di essere una * copia * del parametro" altro ". Temo che la tua domanda sia troppo lunga per consentirmi di capire a fondo proprio in questo momento, quindi non sono sicuro che si tratti di un problema secondario o della risposta giusta. Consiglio generale: ricorda che due diverse classi EventHandler con diversi parametri del modello non sono correlate, ma sono state create con la stessa fonte. Non esiste un tipo come "EventHandler " a meno che non siano specificati T e TEvent, quindi "altro" sarebbe meglio avere la stessa classe di "questo". –

+0

Dovresti includere l'errore di compilazione che ottieni. Un'altra nota: esiste una ragione per cui 'equal()' non è dichiarato nella classe template 'EventHandler'? –

risposta

2

In generale quando si chiude modelli, è necessario fare in modo che> sono separati da spazi in modo che il compilatore non li analizza come operatore di spostamento a destra.

Qui si sta tentando di eseguire il cast statico di un riferimento a un non riferimento, che anche se ha funzionato potrebbe richiamare l'affettamento degli oggetti. È necessario il cast statico su un riferimento derivato.

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

Grazie a entrambi, Mark e Steve: questo mi ha portato nella giusta direzione. C'era un altro problema da quando stavo provando a lanciare un const su un non-const, ma questo era molto più facile da individuare dopo.

Questo è quello che ho, alla fine, dopo un paio di modifiche:

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

ho cambiato rimuovere con remove_if perché boost :: shared_ptr implementa l'operatore == confrontando direttamente la puntatori, invece del loro contenuto. Il terribile nome EventHandlerComparisonFunctor è ora il responsabile per eseguire il controllo di uguaglianza.

E 'implementato in questo modo:

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

E, infine, il metodo uguale a EventHandler (@gf, il metodo è stato infatti dichiarato nel modello EventHandler, ma per qualche ragione ho tagliato fuori per incolla il codice classe qui, errore mio)

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

Tutto funziona OK ora.

Problemi correlati