2012-04-02 10 views
6

Sto tentando di implementare un bus eventi di tipo typesable. Sono bloccato con la funzione EventBus::subscribe perché non accetta il mio gestore di eventi concreti. In una versione precedente avevo il mio AbstractEventHandler implementato solo come una classe astratta, senza che fosse un modello. Non ho avuto problemi con questa implementazione. Ecco perché presumo che il problema reale sia con il modello astratto.La funzione C++ non accetta l'implementazione concreta

Il codice seguente è una versione ridotta della mia implementazione. Il primo blocco è costituito dallo "scheletro" del bus eventi e dalle sue classi richieste mentre il secondo blocco mostra un'implementazione effettiva dell'evento, del gestore di eventi e del main.

Il enum contiene tutti i diversi eventi disponibili. L'evento astratto è la base della quale derivano tutti gli eventi concreti. Il gestore eventi è un modello astratto con un evento come classe modello per garantire la sicurezza del tipo. Il bus eventi è responsabile della distribuzione di tutti gli eventi pubblicati ai rispettivi gestori.

enum EVENT_TYPE 
{ 
    ON_EVENT_1, 
    ON_EVENT_2 
}; 

class AbstractEvent 
{ 
public: 
    AbstractEvent() {}; 
    virtual ~AbstractEvent() {}; 

    virtual EVENT_TYPE type() = 0; 
}; 

template<class T> 
class AbstractEventHandler 
{ 
public: 
    AbstractEventHandler() {}; 
    virtual ~AbstractEventHandler() {}; 

    virtual void on_event(T *event) = 0; 
}; 

class EventBus 
{ 
public: 
    EventBus() {}; 
    virtual ~EventBus() {}; 

    void subscribe(EVENT_TYPE type, 
        AbstractEventHandler<AbstractEvent> *eventHandler) { 
     // Add handler to vector for further use 
    } 

    void publish(AbstractEvent *event) { 
     // send event to each handler in respective vector 
    } 
}; 

Qui di seguito sono il mio gestore di eventi e l'evento concreto e il principale()

class ConcreteEvent : public AbstractEvent 
{ 
public: 
    ConcreteEvent() {}; 
    virtual ~ConcreteEvent() {}; 

    EVENT_TYPE type() { 
     return ON_EVENT_1; 
    }; 
}; 

class ConcreteEventHandler : public AbstractEventHandler<ConcreteEvent> 
{ 
public: 
    ConcreteEventHandler() {} 
    virtual ~ConcreteEventHandler() {}; 

    void on_event(ConcreteEvent *event) { 
     // Do something 
    }; 
}; 

int main() 
{ 
    EventBus *eventBus = new EventBus(); 

    ConcreteEventHandler handler = ConcreteEventHandler(); 

    // This failes! 
    eventBus->subscribe(ON_EVENT_1, &handler); 
} 

il compilatore ritorna con un errore dicendo che non v'è alcuna funzione di corrispondenza per la chiamata al

EventBus::subscribe(EVENT_TYPE, ConcreteEventHandler*) 

e che gli unici candidati sono

void EventBus::subscribe(EVENT_TYPE, AbstractEventHandler<AbstractEvent>*) 

Come posso implementare il mio metodo EventBus :: subscribe per accettare implementazioni concrete della mia classe astratta?

Aggiornamento: Soluzione

ho cambiato la descrizione del metodo di EventBus::subscribe al seguente ed ora funziona bene:

template<typename T> 
void subscribe(EVENT_TYPE type, AbstractEventHandler<T> *eventHandler) { 

} 

Grazie, Rohan, per i vostri suggerimenti! Mi hanno aiutato a trovare questa soluzione.

+0

+1 per una domanda ben fatta. –

risposta

5

La ragione è che, ConcreteEventHandler è una sottoclasse di AbstractEventHandler<ConcreteEvent> e non AbstractEventHandler<AbstractEvent>.

questo potrebbe sembrare sorprendente, ma AbstractEventHandler<ConcreteEvent> non può essere una sottoclasse di AbstractEventHandler<AbstractEvent> anche se ConcreteEvent è una sottoclasse di AbstractEvent.

Il motivo è perché, con i modelli, i modelli come si desidera non garantiscono la sicurezza del tipo. Vediamo un esempio. Andiamo oltre il paradigma standard di una classe base e sottoclassi Cat e Dog.Diciamo che abbiamo una lista di animali:

std::list<Animals>* animals; 

e un elenco di gatti:

std::list<Cat> cats; 

Di seguito, non è un cast valida:

animals = &cats; 

La ragione è, perché , se devo farlo,

animals->add(new Dog("Ben")); 

I wo In realtà aggiungere un Dog a un elenco di Cat s. cats.last() qui restituirebbe effettivamente un Dog. Quindi, in questo caso, stai essenzialmente aggiungendo uno Dog a un elenco di Cat s. Ho visto abbastanza Looney Tunes episodi di sapere che questo non è una buona idea:

cats.last().meow(); 

Quanto sopra non è assolutamente vero, come tutti sappiamo che un Dog può solo bowbow().

EDIT

Per rispondere alla tua domanda, qui è quello che suggerisco di fare; Lasciare ConcreteEventHandler ereditato da AbstractEventHandler<AbstractEvent> e all'interno del codice, ovunque si usi un ConcreteEvent, utilizzare un dynamic_case per trasmettere AbstractEvent a ConcreteEvent. Ciò utilizzerà l'introspezione run-time, che potrebbe influire un po 'sulle prestazioni (inoltre ho visto un bel po' di persone contrarie all'utilizzo di un cast dinamico), ma sarete in grado di eseguire un upcast valido del datatype.

+0

Ok, è vero, ma non risponde alla domanda - Come posso implementare il mio metodo EventBus :: subscribe per accettare implementazioni concrete della mia classe astratta? –

+0

Grazie per la tua rapida risposta! Capisco meglio il problema ora. Il problema è che ho bisogno di AbstractEventHandler in oder per forzare i gestori concreti ad implementare il metodo on_event() corretto. – Fabian

+1

Sei come la stessa persona? –

-2

La classe AbstractEventHandler<T> deve ereditare AbstractEvent. Questo era probabilmente il tuo intento, ti sei semplicemente dimenticato di scriverlo.

template<class T> 
class AbstractEventHandler 
    :public AbstractEvent 
{ 
public: 
    AbstractEventHandler() {}; 
    virtual ~AbstractEventHandler() {}; 

    virtual void on_event(T *event) = 0; 
} 
+1

-1 non solo per essere sbagliato, ma nemmeno per provare a compilare il codice. –

+0

semanticamente, un event handler non dovrebbe mai ereditare un evento. Non è un evento, è ciò che sta gestendo quell'evento. – stefaanv

1

Rohan ha già risposto perché il codice non viene compilato, tuttavia vorrei suggerire un altro approccio.

È possibile implementarlo in modo che l'Eventhandler si iscriva direttamente a EventGenerator. In questo modo esiste un collegamento diretto tra la generazione e la gestione dell'evento.
L'evento dovrebbe quindi contenere un riferimento al proprio generatore per consentire l'accesso ai gestori sottoscritti e l'eventbus chiama un metodo sull'evento per consentirne la gestione autonoma.

In questo modo, l'eventbus non è a conoscenza di eventhandler e non è nemmeno necessario un enumerato di tipo di evento.
Tuttavia, sono necessari diversi generatori di eventi che devono essere accessibili da diversi gestori di eventi rispetto a un eventbus. Ogni eventhandler può gestire solo un evento, quindi se sono necessari più eventi, i gestori di eventi devono essere aggregati (per delega o ereditarietà).

+0

Grazie per il suggerimento!Non voglio seguire questa rotta per due motivi: 1/Il bus degli eventi fa parte di un'API e non voglio esporre le classi che generano gli eventi. 2/Con la crescita del progetto temo di finire in una rete incontrollabile di interconnessioni tra gestori e generatori di eventi. – Fabian

+0

Abbastanza buono, è il tuo progetto. Circa 2 /, il rischio è ridotto al minimo con un approccio a più livelli, in cui i moduli superiori conoscono i moduli inferiori e possono iscriversi e gli eventi vengono inviati solo dai moduli inferiori a quelli più alti. – stefaanv

Problemi correlati