Newsgroup: it.comp.lang.c++
Titolo: Condivisione di codice con ereditarietà multipla e pro lemi di "cast"

Sto scrivendo una libreria per gestire le GUI (più che altro per gioco) ma
ho dei problemi a livello di organizzazione delle strutture dati.
Qui ci sono alcune classi che schematizzano (semplificandola molto) la
struttura della libreria.
Da Window derivano le classi Frame (finestra top level che contiene altri
controlli) e Control (finestra figlia come pulsanti o simili). La classe
Panel è un controllo ma è anche in grado di contenere altri controlli, per
questo motivo c'è la ripetizione di una parte di codice (quella per gestire
i figli).
struct Window{
    virtual Window * GetParent(){
        return 0;
    }
};
struct Control : public Window{
    Control(Window * parent_) : Window(){
        parent = parent_;
        parent->AddChild(this); //!!!
    }
    virtual Window * GetParent(){
        return parent;
    }
    Window * parent;
};
struct Frame : public Window{
    Frame() : Window(){}
    void AddChild(Control * c){
        l.push_back(c);
    }
    std::list< Control* > l;
};
struct Panel : public Control{
    Panel(Window * parent_) : Control(parent){}
    void AddChild(Control * c){
        l.push_back(c);
    }
    std::list< Control* > l;
};
Il miei problemi sono:
- Come  non riscrivere tutto il codice per gestire i figli?
- La chiamata a parent->AddChild non può funzionare in quanto AddChild non 
  è un metodo di Window. Sono però sicuro che ci sia questo metodo in 
  quanto il genitore deve essere un pannello o un frame.
Con l'ereditarietà multipla posso parzialmente risolvere il problema:
struct Window{
    virtual Window * GetParent(){
        return 0;
    }
};
struct Control : public Window{
    Control(Window * parent_);
    virtual Window * GetParent(){
        return parent;
    }
    Window * parent;
};
struct Container{
    void AddChild(Control * c){
        l.push_back(c);
    }
    std::list< Control* > l;
};
Control::Control(Window * parent_) : Window(){
    parent = parent_;
    parent->AddChild(this);
}
struct Frame : public Window, public Container{
    Frame() : Window(), Container(){}
};
struct Panel : public Control, public Container{
    Panel(Window * parent_) : Control(parent), Container(){}
};
La gestione dei figli è gestita da Container che viene ereditato sia da
Panel sia da Frame.
Però così non si risolve il problema di parent->AddChild(). Io sono sicuro
che parent sia derivato da Container ma come posso fare a "dirlo al
compilatore"?
Mi vegono in mente delle soluzioni (tipo mettere una AddChild virtuale che
non fa niente in Window) ma tutte non sono il massimo a livello di design.
-- 
Marco Barisione

 ha scritto nel messaggio
news:11kj8kdy...@marco.barisione...

Non so se questo ti risolve il problema... io ho pensato di fare così, anche
se
credo che esista qualche pattern per questo problema in maniera sana:
class Window
{
public:
    virtual Window* getParent() = 0;
};
template <typename T>
class Control: public Window
{
public:
    Control(T* parent)
    {
        _parent = parent;
        _parent -> addChild(this);
    }
    Window* getParent()
    {
        return _parent;
    }
private:
    T* _parent;
};
class Container
{
public:
    void AddChild(Window* c)
    {
        _sons.push_back(c);
    }
private:
    list<Window*> _sons;
};
template <typename T>
class Frame: public Window, public Container
{
public:
    Window* getParent()
    {
        return NULL;
    }
};
template <typename T>
class Panel: public Control<T>, public Container
{
public:
    Panel(T* parent): Control(parent),Container()
    {
 }
    Window* getParent()
    {
        return NULL;
    }
};

 compilatore"?
questo e` *esattamente* lo stesso problema che mi ha portato ad "amare"
l'ereditarieta` multipla: lo avevo avuto in Turbo Pascal 7 (with objects)
ma, dato che il compilatore non supportava l'ereditarieta` multipla mi ero
dovuto accontentare di un copia-incolla del codice.
Direi che la tua soluzione che usa "Container" e` giusta: per far capire al
compilatore che tutti i tuoi "parent" hanno un metodo AddChild, basta
dirglielo :-)
struct Container;
struct Window {
  virtual Container* GetParent() { ... }; // non e` meglio farla astratta?
};
struct Control {
  Control(Container* parent) { ... }
};
struct Frame: public Window, public Container {
   ....
};
struct Panel: public Control, public Container {
   Panel(Container* parent) { ... }
};
struct Container {
   void AddChild(Control*);
private:
   list<Window*> children;
};
ciao Anto
-- 
"Computer science is not about computers any more than astronomy
is about telescopes." -- EW Dijkstra

 dirglielo :-)
[Che bello quando hai scritto un bel post lungo e con tutte le spiegazioni
e, mentre lo stai per mandare, salta la corrente...]
Se GetParent restituisce un Container* ho comunque il problema che io
vorrei usare questo valore trattandolo come un Window* (ad esempio per
rendere isibile la finestra richiamando il metodo Show).
struct Window;
struct Control;
struct Container {
    void AddChild(Window * c){
        children.push_back(c);
    }
private:
    list<Window*> children;
};
struct Window {
    Window(){}
    virtual Container * GetParent(){
        return 0;
    }
    void Show(){
        cout << "Finestra visibile!" << endl;
    }
};
struct Control : Window{
    Control(Container* parent_) : Window(){
        parent = parent_;
        parent->AddChild(this);
    }
    virtual Container * GetParent(){
        return parent;
    }
private:
    Container * parent;
};
struct Frame: public Window, public Container {
    Frame() : Window(), Container(){}
};
struct Panel: public Control, public Container {
    Panel(Container* parent) : Control(parent), Container(){}
};
int main(){
    Frame f;
    Panel p(&f);
    // Non può funzionare perché GetParent non restituisce una Window?
    // p.GetParent()->Show();
    // Funziona sempre?
    reinterpret_cast< Window* >(p.GetParent())->Show();
}
L'unica soluzione che mi è venuta in mente è usare un cast. Con gcc su
Windows e VC++ funziona, ma funziona sempre sau qualsiasi compilatore o non
è un cast valido (assumendo che Container sia ereditata sempre in unione
con Window)?
Se è così adotterò questa tecnica. Per non costringere l'utente a dover
fare i cast espliciti potrei aggiungere un metodo ToWindow a Container:
Window * Container::ToWindow(){
#if RTTI_ABILITATA
    Window * ret = dynamic_cast< Window* >(this);
    assert(ret);
    return ret;
#else
    return reinterpret_cast< Window* >(this);
#endif

 e, mentre lo stai per mandare, salta la corrente...]
no ups no party :-p
ciao

 }
quest'uso di reinterpret_cast non e` portabile e quindi sarebbe meglio non
usarlo: anzi, probabilmente se usi qualche costrutto un po' piu` esotico
tipo l'ereditarieta` multipla non funziona con nessun compilatore.
Il cast corretto da fare e` dynamic_cast.


 Il cast corretto da fare e` dynamic_cast.
Preferisco evitare i dynamic_cast, in modo che funzioni anche con RTTI
disabilitata.
-- 
Marco Barisione
http://spazioinwind.libero.it/marcobari/

 };
Mi son perso un qualche vincolo a non usare i template? Perche' non fare invece
template <typename BaseT> class Control : public BaseT {
    Control(Container* parent); // punto 8
    Container* GetParent();
    ....
};
Dove BaseT e' Window o Container, e non hai bisogno dell'eredita' multipla.
Ciao,
Nicola

 };
Perché un Container contiene una lista di Control*. Se Control diventa
template come faccio?
struct Window{
};
template < typename BaseT > struct Control : public BaseT {
};
struct Container : Window{
    list< Control< Window >* > children;
};
struct Panel : Control< Container >{
};
struct Button : Control< Window >{
};
int main(){
    Panel p;
    Button b;
    p.children.push_back(&b);
    Panel pc;
    p.children.push_back(&pc); // !!!
}
Tendo a ragionare alla Python, quindi magari ho fatto errori di
progettazione. Se qualcuno ha idee alternative su come progettare il tutto
per me va bene anche se dovessi riscrivere ogni cosa da capo.
Quanto mi manca Python, il C++ non fa per me... sigh :-(


 Quanto mi manca Python, il C++ non fa per me... sigh :-(
Un altro giovane promettente rovinato... ;-)
Ciao,
Nicola

 [...]
E che fine ha fatto la classe Frame che è un Container ma non un Control?
:-)
Mi sembra che tutti i requisiti siano nel messaggio di Antonio Cuni. Però
non posso usare l'ereditarietà virtuale perché allora non va più bene il
sistema che uso per gestire gli eventi.


 metodi da richiamare alla generazione di un evento.
Forse ho trovato una soluzione:
template < class C, void (C::* m)() >
void EvtCaller(void * obj){
    (reinterpret_cast< C* >(obj)->*m)();
}
struct EventHandler{
    void (*callback)(void*);
    void Generate(){
        callback(this);
    }
};
struct Window : public EventHandler{
};
struct Container : public virtual Window{
};
struct Frame : public virtual Window, public Container{
};
struct MyFrame : public Frame{
    MyFrame(){
        callback = EvtCaller< MyFrame, &MyFrame::OnQualcosa >;
    }
    void OnQualcosa(){
        cout << "Ciao!\n";
    }
};
 
int main(){
    Frame * f = new MyFrame;
    f->Generate();
}
Il reinterpret_cast non dovrebbe dar problemi perché sto riconvertendo al
tipo originale.
Ma con VC++ non compila (con gcc perfettamente), perché?
error C2783: 'void EvtCaller(void *)' : could not deduce template argument for 'C'
error C2783: 'void EvtCaller(void *)' : could not deduce template argument for 'm'
error C2563: mismatch in formal parameter list
error C2568: '=' : unable to resolve function overload
        could be 'void EvtCaller(void *)'
Cosa sto sbagliando?
Perché se faccio:
EvtCaller< MyFrame, &MyFrame::OnQualcosa >(0);
Compila senza problemi?


 Ma con VC++ non compila (con gcc perfettamente), perché?
Semplificando:
template< class C >
void f(){
    cout << "ciao!\n";
}
class Classe{};
int main(){
    void (*foo)() = f< Classe >; // Ok!
    void (*bar)();
    bar = f< Classe >;           // Errore!
}
Mi sto perdendo qualcosa di ovvio e stupido o è un bug?


 Mi sto perdendo qualcosa di ovvio e stupido o è un bug?
Sa tanto di bug. Non mi pare ragionevole che i puntatori a funzione
supportino l'inizializzazione e non l'assegnamento.
Ciao,
Nicola

 sistema che uso per gestire gli eventi.
Mi sembrava troppo facile...
Un'altra strada, che pero' non so se soddisfa le tue necessita' puo' essere questa:
class Window {};
class Container : public Window {};
class Control {};  // aspetta a lamentarti :-)
class SimpleControl : public Window, public Control {};
class ContainerControl : public Container, public Control {};
class Frame : public Container {};
Cosi' Window non e' ereditata virtualmente e Control non e' un template.
Ciao,
Nicola

 multipla.
beh, pero` poi non puoi piu` trattare tutti i controlli in modo uniforme.
Come potresti, ad esempio, scrivere 'std::list<Control*>'?


 Lo immaginavo, ma perché?
per via della diversa rappresentazione in memoria delle strutture che fa il
compilatore. Ad esempio, supponiamo di dover generare codice C a partire
dal C++ (un po' come faceva il primo compilatore, a quanto ne so); in caso
di ereditarieta` singola:
class A {
  int a;
  int b;
};
class B: public A {
  int c;
};
per rappresentare le due classi il compilatore genera qualcosa del tipo:
struct A {
  int a;
  int b;
};
struct B {
  int a;
  int b;
  int c;
};
a questo punto in C possiamo benissimo fare cose del genere:
B myobject;
B* pb = &myobject;
pb->a = 3; pb->b = 3; pb->c = 3; // tutto OK
A* pa = (A*) pb;
pa->a = 4; pa->b = 4;
Quest'ultima riga funziona perche` gli offset di 'a' e 'b' rispetto a 'pb' e
'pa' sono gli stessi. Se fai un reinterpret_cast<> in C++ e` probabile che
il codice generato sia lo stesso che ho scritto qua sopra.
Se usiamo l'ereditarieta` virtuale le cose si complicano; credo (ma non ne
sono certo - se qualcuno lo sa meglio di me mi corregga pure) che i
compilatori memorizzino un puntatore all'unica base virtuale; mi spiego
meglio:
class A {
  int a;
};
class B: virtual public A {
  int b;
};
class C: virtual public A, virtual public B {
  int c;
};
le strutture C corrispondenti dovrebbero essere qualcosa del genere:
struct A {
  int a;
};
struct B {
  A* base;
  int b;
};
struct C {
  A* base;
  int b;
  int c;
};
dove il membro "base" rappresenta l'istanza di A "in comune" tra B e C.
Quando il compilatore C++ incontra questo:
<C++>
C myobject;
C.a = 3;
</C++>
genera codice C grosso modo cosi`:
<C>
C myobject;
C.base->a = 3;
</C>
Ora, se tu fai un reinterpret_cast da A a C:
<C++>
A* pa = ...;
C* pc = reinterpret_cast<C*>(pa);
pc->a = 3;
</C++>
il compilatore obbedise e fa il cast anche nel codice C:
<C>
A* pa = ...;
C* pc = (C*)pa;
pc->base->a = 3; // BUM!
</C>
Nota cosa e` successo nell'ultima linea di codice: il compilatore accede al
primo campo della struttura e lo tratta come se fosse un puntatore, ma in
realta` la nostra struttura e` di tipo A, quindi il primo campo e` un
intero!
Ripeto, potrei avere detto castronerie, quindi non prendete per oro colato
quel che ho scritto...


"Marco Barisione" ha scritto 
: 
: Forse ho trovato una soluzione:
: 
: template < class C, void (C::* m)() >
: void EvtCaller(void * obj){
:     (reinterpret_cast< C* >(obj)->*m)();
: }
: 
: struct EventHandler{
:     void (*callback)(void*);
:     void Generate(){
:         callback(this);
:     }
: };
: 
: struct Window : public EventHandler{
: };
: 
: struct Container : public virtual Window{
: };
: 
: struct Frame : public virtual Window, public Container{
: };
: 
: struct MyFrame : public Frame{
:     MyFrame(){
:         callback = EvtCaller< MyFrame, &MyFrame::OnQualcosa >;
:     }
:     void OnQualcosa(){
:         cout << "Ciao!\n";
:     }
: };
:  
: int main(){
:     Frame * f = new MyFrame;
:     f->Generate();
: }
: 
Puoi invertire la parentela tra MyFrame e l'implementazione 
dell'event handler, cosi` non c'e` piu` bisogno di reinterpret_cast:
#include <stdio.h>
struct EventBase
{
  virtual void Generate() = 0;
  virtual ~EventBase() {}
};
struct    Window : EventBase {};
struct Container : virtual Window {};
struct     Frame : Container, virtual Window {};
template <class T> struct EventHandler : T
  { void Generate() { (this->*callback_)();}};
struct MyFrameImpl : Frame
{
  MyFrameImpl()
    : callback_(&MyFrameImpl::OnQualcosa) { }
  void OnQualcosa()
    { puts("Ciao!"); }
protected: void (MyFrameImpl::*callback_)();
};
typedef EventHandler<MyFrameImpl> MyFrame;
int main()
{
  Frame* f = new MyFrame;
  f->Generate();
  delete f;
}
Massimo

 Cosi' Window non e' ereditata virtualmente e Control non e' un template.
mmm... Potrebbe andare ma non penso che funzionerà in futuro.
Control potrebbe necesistare di essere derivato da Window per fare qualcosa
di comune a tutti i controlli.
Inoltre penso che potrebbero servirmi nuovi classi come Container per
aggiungere caratteristiche a classi diverse fra loro (ad esempio una class
DropTargetWindow, per gestire il drag and drop):
class DropTargetWindow : virtual public Window{
};
class DropPanel : public Panel, public DropTargetWindow{
};
class DropFrame : public Frame, public DropTargetWindow{
};


 }
Ecco, stavo per suggeririti una cosa del genere. Se ricordi, tempo fa ci
furono alcune discussioni su come implementare closures/delegates in C++.
Una delle proposte (la mia, in effetti) usava precisamente questa tecnica.
Un'altra (suggerita da Massimo Valentini) richiedeva il cast di puntatore a
funzione membro, proprio come il tuo precedente approccio. Quest'ultima
soluzione č piů efficiente, ma (temo) non conforme allo Standard.
Max

 dell'event handler, cosi` non c'e` piu` bisogno di reinterpret_cast:
È un'idea e non mi semra avere controindicazioni (se no la complicazione
del codice) ma serve davvero? Quel reinterpret_cast mi sembra totalmente
innocuo, o sbaglio?


 [...]
Non ho ancora guardato bene il tuo codice ma mi sembra che abbia le stesse
idee del mio.
Il mio ha il vantaggio di non usare funzioni virtuali ma mi č venuto un
dubbio, per rimuovere gli eventi confrontavo i puntatori a metodi e alla
classe.
Con i template posso farlo, cioč sono sicuro che
template< class T > void f(){}
f< int > == f< int >
f< long > == f< long >
f< MiaClasse* > == f< MiaClasse* >
f< int > != f< long >
f< MiaClasse* > != f< MiaClasseDerivata* >
siano sempre vere?


 soluzione è più efficiente, ma (temo) non conforme allo Standard.
Il codice che si occupa di queste cose nel mio programma è poco e
localizzato in un file c++ e un header. L'utente non usa queste funzioni
direttamente ma macherate da una macro tipo:
#define LINK(src_object, evt_name, dest_class, dest_method) ...
...
class MyFrame : Frame{
    MyFrame() : Frame(){
        Button * btn = new Button();
        LINK(button, "Click", MyFrame, OnClick);
    }
    void OnClick(){
        cout << "CIAO\n";
    }
}
Quindi non avrei problemi a mantenere due versioni in base al compilatore
usato.



Boh. 'delegate' è un costrutto del C#, ereditato dal J++ (credo). Le closure
di Delphi e BCB dovrebbero essere qualcosa di molto simile.
Max

"Marco Barisione" ha
:
: > Puoi invertire la parentela tra MyFrame e l'implementazione
: > dell'event handler, cosi` non c'e` piu` bisogno di reinterpret_cast:
:
: È un'idea e non mi semra avere controindicazioni (se no la complicazione
: del codice) ma serve davvero? Quel reinterpret_cast mi sembra totalmente
: innocuo, o sbaglio?
:
Il reinterpret_cast e` sempre rischioso.
Perdi un'informazione. Il tipo. Per esempio se consideri le API che
costringono a reinterpret_cast, tipo CreateThread, nulla ti impedisce
di passarle una funzione che vuole un puntatore ad una certa struttura
e il puntatore ad un'altra.
Bisogna vedere dove nel tuo framework perdi questa informazione, e quanto
sia probabile che un utilizzatore possa usare un handler di un button
con un puntatore a listbox, piuttosto che assegnare un handler che vuole
tre parametri ad uno che ne richiede due.
Poi in presenza di ereditarieta` multipla la cosa e` ancora piu` complicata
perche` non e` detto che un cast implicito (derivata a base) sia equivalente
ad un reinterpret_cast. Il cast implicito puo` cambiare il valore del puntatore,
mentre un reinterpret_cast puo` non cambiarlo, come puo` capitare l'opposto.
Massimo

 tre parametri ad uno che ne richiede due.
Memorizzo l'EventHandler e l'oggetto che sarą poi passato insieme e sono
sicuro all 100% di passare l'oggetto giusto. Quindi non vedo problema.


 siano sempre vere?
dipende dal livello di astrazione della risposta.
In generale possiamo dire che f<T> e` sempre diversa da f<G> (quando T e G
sono due tipi diversi, ovviamente), quindi a bassso livello hanno indirizzi
diversi.
Pero` occhio che in C++ non puoi scrivere quel codice: in generale f<int> e
f<long> sono due puntatori di tipo diverso e quindi *non* si possono
confrontare, a meno di fare reinterpret_cast<>; ma, come ti ho gia` detto,
con reinterpret_cast puoi avere un comportamento implementation defined se
sei fortunato o undefined behaviour se ti va male, quindi IMHO dovresti
evitarlo come la peste.


 f<long> sono due puntatori di tipo diverso
Perché?
Io ho definito f come:
template< class T > void f(){}
Quindi f<long> e f<int> sono due puntatori a funzioni che non ritornano
nulla e che non hanno argomenti.


 nulla e che non hanno argomenti.
Nota che ho scritto "in generale" - il che non significa che sia sempre vero
anche per tutti i casi particolari.
In effetti avevo pensato allo stesso caso che hai citato tu, ma sinceramente
non riesco proprio ad immaginarmi un motivo per scrivere un template che
non usa il proprio parametro...


 si tratta della stessa funzione.
Il linker deve essere un po' "magico". Non so precisamente cosa esiga 
lo Standard (mi documenterò), ma il linker del gcc a fronte di diverse 
instanziazioni di funzioni template elimina i duplicati e lascia  una sola 
funzione. (I simboli associati a template hanno di fatto un trattamento 
speciale, altrimenti in fase di linking avresti errori di "multiple 
definition".) 
Nota che, per certi tipi di costrutti template, il linker *deve* 
necessariamente lasciare un'unica instanziazione. Considera, ad esempio, i 
membri statici di classi template: per ovvie ragioni, non puoi permettere 
che ogni unità di compilazione veda un membro statico diverso.
Max

 };
Come posso scrivere un operator== per questa roba?
Io memorizzo tutto come EventHandler* quindi non so il tipo.
Due istanze di EventHandler e derivati sono uguali se sono dello stesso
tipo (entrambi Function o Method) e se:
- Function: f == f;
- Method: istance == istance && f == f.
L'unica idea che mi č venuta č stupida.


>[SNIP]
http://www.boost.org/doc/html/signals.html
dato un occhio qui?
sbirish.

 dato un occhio qui?
No ma lo farò appena posso (ora devo finire di cuocere le meringhe e finire
la panna cotta al caffé :-) ).


 che ogni unità di compilazione veda un membro statico diverso.
forse allora ricordavo male... ma i template non hanno linkage interno?


 Io memorizzo tutto come EventHandler* quindi non so il tipo.
sei proprio sicuro che ti interessa avere un operatore di uguaglianza?
Magari ti puo` bastare confrontare l'identita` tra i due oggetti, ovvero
confrontare i rispettivi puntatori.


 confrontare i rispettivi puntatori.
Sì, altrimenti come faccio a scollegare un evento ad un EventHandler? Non
voglio essere obbligato a memorizzare l'istanza dell'EventHandler ogni
volta.
Esempio, un frame con un pulsante che mostra un messaggio solo la prima
volta che è cliccato:
class MyFrame : Frame{
    Button * btn;
public:
    MyFrame(...) : Frame(...){
        btn = new Button(...);
        // Collega un click su btn al metodo OnCLick di MyFrame.
        LINK(btn, "Click", MyFrame, OnClick);
        // Dopo l'espansione della macro si avrebbe qualcosa come:
        // btn->Link("Click", EventHandler< MyFrame >(&MyFrame::OnClick));
    }
    void OnClick(){
        MsgBox("Ciao!");
        // Lo scollega.
        UNLINK(btn, "Click", MyFrame, OnCLick);
        // Dopo l'espansione della macro:
        // btn->Unlink("Click", EventHandler< MyFrame >(&MyFrame::OnClick));
    }
};
Come faccio se non è definito l'operatore ==?
Dovrei memorizzare un puntatore all'EventHandler, ma questo renderebbe
tutto più complicato.    


 dato un occhio qui?
Molto bello, temo però che non risolva le mie attuali limitazioni.


 tutto più complicato.
se il codice e` questo hai perfettamente ragione, solo che non e` banale
implementarlo: in pratica devi implementare un meccanismo di multiple
dispatching (vedi ad esempio la libreria Loki di Alexandrescu); uno dei
tanti modi e` questo:
[per inciso: in questi casi e` meglio non usare operator==, dato che e` un
operatore che in genere si usa per oggetti che hanno la semantica di valore
(tipo int e std::string) e non semantica di riferimento, come in questo
caso; meglio usare una funzione con un nome adeguato, come 'compare']
struct DerivedA;
struct DerivedB;
struct Base {
  virtual bool compare(Base*) = 0;
private:
  virtual bool compare_impl(DerivedA*) = 0;
  virtual bool compare_impl(DerivedB*) = 0;
};
struct DerivedA: Base {
  bool compare(Base*);
private:
  bool compare_impl(DerivedA*) { ... }
  bool compare_impl(DerivedB*) { return false; }
};
struct DerivedB: Base {
  bool compare(Base*);
private:
  bool compare_impl(DerivedA*) { return false; }
  bool compare_impl(DerivedB*) { ... }
};
bool DerivedA::compare(Base* b) {
  // il compilatore sa che this e` di tipo DerivedA*
  // quindi chiama la versione giusta
  b->compare_impl(this);
}
bool DerivedB::compare(Base* b) {
  b->compare_impl(this);
}
Ovviamente non e` il massimo per quanto riguarda la manutenibilita`, dato
che ogni classe deve conoscere tutte le altre; la libreria Loki semplifica
*notevolmente* il lavoro, ma non so fino a che punto vale la pensa di
complicarsi la vita in questo modo.
Mi viene da porti due domande:
1) hai veramente bisogno di gestire una *lista* di eventi anziche` un
singolo evento? In tutti i toolkit che ho visto io ad ogni evento era
associata una procedura, che se necessario chiamava "in cascata" quelli
precedenti. Mi fai un esempio in cui sia utile questa features?
2) hai veramente bisogno di poter scollegare gli eventi? A me sembra
abbastanza artificioso: di nuovo, non riesco a pensare a nessun caso in cui
questo possa essere utile; il tuo esempio si risolve facilmente
memorizzando uno stato e facendo variare il comportamento della procedura a
seconda dello stato (ad esempio un booleano che indica se dobbiamo ancora
scrivere il testo).


 scrivere il testo).
Non mi venivano in mente esempi più intelligenti.
Di nuovo, tutti gli altri toolkit permettono di scollegare gli eventi, se
il mio non lo permette rischio di averne bisogno quando tutto è stato
finito e dover modificare un'enormità di codice.
Un esempio in cui sono obbligato a scollegare gli eventi è quando l'oggetto
di destinazione non esiste più, cioè può essere distrutto prima
dell'oggetto che genera questo evento. In questo caso se non lo scollegassi
si avrebbe un crash probabilmente.
Pensandoci bene neanche questo è un caso che serve realmente perch;e ci
penserebbe il toolkit a scollegare e non avrebbe problemi.


 caso; meglio usare una funzione con un nome adeguato, come 'compare']
Perché?


         *v2 = f;
Perché non va? Non dovrebbe essere possibile convertire qualsiasi tipo in
un void* (con un apposito cast)?
