|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
[C++] Ricordatemi una keyword esotica!
Salve,
ho fatto un magheggio di questo tipo: Codice:
class Kernel :
public Task::ListenerImpl<ValueQuery>,
public Task::ListenerImpl<SomeTask>,
public Task::ListenerImpl<SomeOther>
Ora, tutto funziona bene, e posso fare dei metodi callback distinti per ogni tipo; però: se uso this come Listener*, il cast è giustamente ambiguo perchè Listener è ereditato ben 3 volte! Mi ricordo che c'era una keyword assurda ed esotica che faceva ereditare le superclassi una ed una sola volta... come Listener in questo caso. La sapete? O almeno ditemi cosa potrei cercare su Google
|
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: Apr 2006
Messaggi: 22462
|
che io sapessi c'era un "giochino" per evitare l'inclusione ciclica dei file sorgente, ma nulla più
__________________
amd a64x2 4400+ sk939;asus a8n-sli; 2x1gb ddr400; x850 crossfire; 2 x western digital abys 320gb|| asus g1
Se striscia fulmina, se svolazza l'ammazza |
|
|
|
|
|
#4 | |
|
Senior Member
Iscritto dal: Dec 2005
Città: Istanbul
Messaggi: 1817
|
Quote:
Codice:
template <typename T>
class ListenerImpl : public virtual Listener
{
/* */
};
__________________
One of the conclusions that we reached was that the "object" need not be a primitive notion in a programming language; one can build objects and their behaviour from little more than assignable value cells and good old lambda expressions. —Guy Steele Ultima modifica di marco.r : 13-11-2009 alle 23:23. |
|
|
|
|
|
|
#5 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
Si era proprio quello grazie mille
Però ora che ci ho provato, l'unificare la classe base rende impossibile distinguere i metodi di callbacks in base al tipo... quindi mi sembra sensato che l'utente deve specificare a quale "parte" del listener mandare il messaggio. |
|
|
|
|
|
#6 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Boh...a me una cosa del genere puzza un po'. Non è meglio estrarre un'interfaccia comune alle tre classi (Listener appunto) e poi aggiungere un membro di tutte e tre all'interno di Kernel ? Solo vedendo due righe di codice non riesco proprio a vederci una relazione di ereditarietà...
Ultima modifica di cionci : 15-11-2009 alle 11:17. |
|
|
|
|
|
#7 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
Beh, la classe Kernel in realtà è solo un test, è una "classe ipotetica" che potrebbe richiedere un callback di completamento di un Task...
ho pensato che in questo modo venisse "stiloso" perchè, dato che per usare l'interfaccia Task c'è bisogno di farne una sottoclasse, usare un elenco di Listener<T> permette di avere un metodo di callback per ogni tipo, con il puntatore passato già del tipo giusto, tipo nell'esempio di prima: Codice:
taskCompleted( ValueQuery* ); taskCompleted( SomeTask* ); taskCompleted( SomeOther* ); Poi certo si può anche ereditare il metodo generico che passa Task* ma poi devi procedere autonomamente ai cast (spesso molto poco "solidi")... |
|
|
|
|
|
#8 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Imho non si usa l'ereditarietà per risparmiare la scrittura di alcuni metodi, si usa quando c'è una relazione fra classe base e derivata.
In questo caso non mi sembra che ci sia, sarebbe come ereditare una classe macedonia dalle classi pera, mela e banana... |
|
|
|
|
|
#9 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
Non capisco la critica onestamente
La relazione in questo caso è solo che devo offrire all'utente un modo per ricevere un callback quando il Task (che è asincrono!) viene completato... non vedo quale altro modo più semplice ci sia se non permettere all'user di ereditare il Listener che preferisce... E poi non è che l'ereditarietà serve solo quando A is B eh... |
|
|
|
|
|
#10 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Se ne hai voglia ricapitolami bene la situazione...non sto dicendo che l'unica relazione che si può esprimere con l'ereditarietà sia a is b, sto dicendo che molte volte in cui si fanno questi pastrocchi (una ereditarietà triplada classi non astratte per me lo è) non è necessario.
Vediamo cosa ho capito: ci sono questi Listener che sono delle classi che si aspettano di ricevere un messaggio di completamento da una operazione asincrona. Vuoi che Kernel abbia la possibilità di stare in ascolto su tre tipi di eventi distinti. Ovviamente mi immagino che dovrai registrare questi listener presso l'operazione asincrona... Premetto che sto derivando questi discorsi dai nomi delle classi e dalla semplice intestazione che hai proposto, quindi posso benissimo essere smentito. Tu vuoi registrare direttamente un'istanza di Kernel presso gli eventi asincroni in modo che venga chiamato direttamente un metodo della classe. Prima di tutto mi immagino che il compito principale della classe Kernel, dato il nome, non sia di ricevere le notifiche, ma di fare molte altre cose, fra cui la ricezione delle notifiche. Derivando una classe se ne caratterizza il comportamento, quindi se il compito principale di Kernel non è ricevere le notifiche per me l'hai caratterizzata male. Io separerei la classe Kernel dalla ricezione delle notifiche, creerei una classe KernelListener, addirittura una per ogni tipo di notifica o un template, che prende come parametro l'istanza della classe kernel e si occupa di eseguire le operazioni necessarie su Kernel al momento della ricezione dell'evento. Ad esempio: Codice:
template <typename T>
class KernelListener: public ListenerImpl<T>
{
private:
Kernel *kernel;
public:
KernelListener(Kernel *k):kernel(k){};
virtual void taskCompleted(T *info)
{
ListenerImpl<T>::taskCompleted(info);
kernel->notifyTaskCompleted(info);
}
};
class Kernel
{
private:
KernelListener<ClassA> listener1;
KernelListener<ClassB> listener2;
KernelListener<ClassC> listener3;
public:
void registerListener(AsyncEvent1 evt1, AsyncEvent2 evt2, AsyncEvent3 evt3)
{
evt1.register(listener1);
evt2.register(listener2);
evt3.register(listener3);
}
void notifyTaskCompleted(ClassA info);
void notifyTaskCompleted(ClassB info);
void notifyTaskCompleted(ClassC info);
};
Ultima modifica di cionci : 15-11-2009 alle 17:43. |
|
|
|
|
|
#11 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
Grazie della spiegazione
Allora; premetto che ci hai preso sulle funzioni di più o meno tutte le classi dal loro nome Tuttavia Kernel da quando ho iniziato a "smanettare" con i Task è rimasto privo di funzioni proprie se non lanciare il task iniziale, quindi per evitare forzature pensavo proprio di toglierlo. E poi ListenerImpl<T> in realtà è una classe astratta formata da 2 metodi... in pratica è un'interfaccia generica stile Java, e per questo non mi sembrava brutto più di tanto ereditarne più d'una (caso comunque molto raro in teoria). Codice:
class Listener : public BaseObject
{
public:
virtual void taskCompleted( Task* t )=0;
};
template <class T>
class ListenerImpl : public Listener
{
public:
void taskCompleted( Task* t )
{
taskCompleted( static_cast<T*>(t) );
}
virtual void taskCompleted( T* t )=0;
};
Poi ovvio che sta cosa non sarà mai usata da nessuno, ma lo scopo è didattico ![]() La tua idea mi piace, il fatto è che non capisco bene come potrei implementarla; gli eventi sono sostanzialmente *lo stesso* cioè Task Complete; dal Dispatcher di questi famosi task posso ottenere giusto questa informazione, e tratto qualsiasi listener come Listener, il tipo più astratto... non sono a conoscenza del tipo del task che viene completato, dato che il tipo è esteso dall'user. Quello che cambia è il <T> del Listener passato al Task, tipo Codice:
dispatcher->schedule( new TaskQualsiasi( (ListenerImpl<TaskQualsiasi>*)this)); Per cui, data la scarsissima riflessività del C++ non saprei bene come legare una sottoclasse "sconosciuta" di Task ad un dato callback se non domandando all'user di specificare tutte quante le chiamate come nel tuo esempio... cosa che almeno imo, anche se più corretta non vale troppo la candela... tantopiù che il template cast all'assegnazione rimarrebbe in ogni caso. O altrimenti levo tutto, metto un bel campo void* userData e lascio tutto all'esterno
Ultima modifica di Tommo : 15-11-2009 alle 18:23. |
|
|
|
|
|
#12 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Quindi in pratica il problema è passare dei dati al listener mantenendo la genericità dello stesso...
Fatti una classe base astratta dei valori passati a listener !!! L'utente per passare un dato dovrà implementare un'interfaccia. In questo modo potrai gestire tutto con la classe astratta |
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 17:39.




















