Newsgroup: it.comp.lang.c++
Titolo: suggerimenti per design

Salve,
Sto cercando di realizzare delle classi per risolvere questo problema:
Al verificarsi di alcuni eventi, durante l'esecuzione di un mio programma,
devo intraprendere determinate azioni. L'azione da intraprendere dipende da
un regolamento collegato all'oggetto considerato. Un regolamento e' costituito
da un insieme di coppie  < evento, azione da svolgere >.
Alla partenza del programma costruisco un certo numero di regolamenti leggendo da
un data base le varie azioni da intraprendere per ogni evento. (Potrei utilizzare 
una factory)
Poi avro' un certo numero di oggetti su cui operare, ognuno collegato a un regolamento.
(Posso avere piu' oggetti collegati allo stesso regolamento).
Al verificarsi di certi eventi andro' a vedere sul regolamento che azione intraprendere.
La classe regolamento deve essere molto flessibile, infatti possono uscire nuove regole
e (sopratutto) nuovi eventi in qualsiasi momento e il mio obiettivo e' mimimizzare 
la quantita' di codice da scrivere ad ogni nuova regola aggiunta. 
(nel progetto a cui lavoro e esercizio e sviluppo vanno di pari passo
La mia idea e' di tenere una classe Regolamento contenente un vettore di coppie
< evento, azione da svolgere >. Questa classe avra' un metodo per aggiungere elementi
a questo vettore e per svolgere l'azione opportuna al verificarsi dell'evento.
L'azione avra' un metodo virtuale.  (l'evento puo' essere un semplice enum)
Il mio problema e' passare dei parametri al metodo virtuale dell'azione.
Infatti a parita' di evento, gli argomenti da passare saranno dello stesso tipo,
ma per eventi diversi i tipi saranno diversi. Il problema principale e' che oggi
non so che argomenti mi serviranno per gli eventi che aggiungero' domani.
Pensavo di usare una classe base Argomenti vuota o quasi e di usare delle classi
derivate con i parametri veri e propri. Chi invoca l'azione sa che evento si e' 
verificato e istanziera' la classe derivata opportuna. Chi gestisce l'evento sa
che parametri gli verranno passati e fara' un opportuno cast dalla classe base a
quella derivata. Pero' questa soluzione non mi piace molto.
Avete qualche suggerimento/idea ?
*Fabio*
class Argomenti
{
};
class Azione
{
  public:
    Azione() {}
    virtual ~Azione() {}
    virtual  int agisci( Argomenti *args ) = 0;
};
class Regolamento
{
    vector < pair < int , Azione * > > azioni;
  public:
    Regolamento ();
   ~Regolamento ();
    void addAzione( int evento, Azione *azione); // aggiungo la coppia al vettore
    int agisci( int evento, Argomenti *args ) 
    {
	Azione * azione;
	/* cerco  nel vettore l'azione associata all'evento */
	return azione->agisci( args );	
    }     		
};

 domani.
Difinisci una classe derivata differente per ogni catrgoria differente
di azione.
-- 
Maurizio Loreti                      
http://wwwcdf.pd.infn.it/~loreti/mlo.html
Un. of Padova, Dept. of Physics - Padova, Italy          
lor...@padova.infn.it

 di azione.
E' proprio quello che ho usato nell' esempio in fondo al mio post.
Il punto e' come gestire i parametri.
Ad esempio , per l'evento 1 potrei dover passare due interi all'azione, per l'evento
2 un double e cosi via.
quindi, seguendo l'esempio del post precedente, avrei:
class Argomenti_1 : public Argomenti
{
    public: // tanto per semplificare
	int i;
	int j;
};
class Azione_1 : public Azione 
{
    public:
	Azione_1() {}
	~Azione_1() {}
	int agisci ( Argomenti *args )
	{
	   Argomenti_1 *args1 = dynamic_cast<Argomenti_1 *> (args); 
	   if (! args1 ) return -1;
           cout << "Argomenti : " << args1->i << "," << args1->j << endl;
           return 0;
	}	
}

non so che argomenti mi serviranno per gli eventi che aggiungero' domani.
Una possibile soluzione sarebbe quella di inserire i parametri in un buffer
e passare al chiamato solo il puntatore al buffer, chiamato e chiamante
accedono al buffer mappandovi sopra un tipo struttura attraverso cui
inserire e recuperare i parametri.
E` una soluzione macchinosa, ma dovrebbe funzionare.
Per la verita` avevo pensato ad una soluzione decisamente piu` OO,
piu` mantenibile e meno faticosa:
utilizzare uno stream come tramite per i parametri e passare solo un
riferimento allo stream. L'idea era di utilizzare un iostream collegandolo
ovviamente ad un buffer in memoria. Quest'ultima operazione che mi
e` sembrata possibile non sono pero` riuscito ad eseguirla.
Qualcuno per caso saprebbe come fare (sempre che sia possibile
con gli stream standard)?
Paolo
-----------------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------------------------

Sto cercando di realizzare delle classi per risolvere questo problema:
[omissis]
Stavo pensando... potresti utilizzare una soluzione tipo
printf(), ovvero utilizzare le ellissi:
void Azione::Esegui(int ArgC, char* ArgT, ...);
dove
ArgC  = Numero di parametri aggiuntivi
ArgT = puntatore a stringa asciiz al cui interno vengono
              descritti i tipi dei parametri aggiuntivi.
Pero` non ho mai usato le ellissi e quindi non saprei come
estrarre i vari argomenti.


 
*Fabio*
-- 
Grelb's Reminder:
	Eighty percent of all people consider themselves to be above
	average drivers.

un dynamic_cast<> alla classe opportuna.
Questa mi sembra una soluzione piu` laboriosa della mia, inoltre il dynamic_cast
avrebbe alcune controindicazioni:
a) overhead non nullo a run-time
b) dovresti gestire una eventuale situazione di fallimento
     (anche se presumibilmente cio` non avverra` mai).
Saluti,


 Paolo
Ciao
Fabio

Se puo' essere un suggerimento, a suo tempo ho realizzato una applicazione
che realizzava quanto segue:
Data una serie di eventi di varia natura (segnali digitali, temporizzazioni
assolute o periodiche, risultati di semplici operazioni logiche su variabili
in memoria etc) che singolarmente o combinati generavano dei cosidetti
trigger, e una serie di azione predefinite parametrizzabili da eseguire
quando uno di questi trigger si settava ....
Da quel progetto ho ritagliato il codice che segue che forse ti puo' dare
qualche suggerimento.
Ho scritto la gerarchia di classi partendo da una classe base denominata
Trigger con i due metodi astratti Is e IsNot
class    Trigger
{
public:
    virtual    bool    Is()  = 0;
    virtual    bool    IsNot() = 0;
};
una denominata Action con una funzione membro denominata action con la lista
di argomenti variabili,
class    Action
{
public:
    virtual bool    action(Trigger* ...) = 0;
};
Infine una classe base denominata regola
class    Regola
{
protected:
    Trigger*    p_trigger;
    Action*    p_action;
public:
    Regola(Trigger* p_Trigger, Action* p_Action)
    :
     p_trigger(p_Trigger),
    p_action(p_Action)
    {
    }
    virtual bool    risolvi()
    {
     if(p_trigger->Is())
            return p_action->action(p_trigger);
      return false;
    }
};
Con questa logica ho costruito una serie di insiemi di regole che chiamavano
le azioni con diversi parametri, alcune vengono valutate periodicamente,
alcune in corrispondenza di eventi etc....
Per esempio:
class    RegolaInt
:
 public Regola
{
    int   i_arg;
public:
    RegolaInt(Trigger* p_Trigger, Action* p_Action, int i_Arg)
    :
     Regola(p_Trigger, p_Action),
     i_arg(i_Arg)
    {
    }
    virtual bool    risolvi()
    {
     if(p_trigger->Is())
            return p_action->action(p_trigger, i_arg);
      return false;
    }
};
class    RegolaDouble
:
 public Regola
{
    double  d_arg;
public:
    RegolaDouble(Trigger* p_Trigger, Action* p_Action, double d_Arg)
    :
     Regola(p_Trigger, p_Action),
     d_arg(d_Arg)
    {
    }
    virtual bool    risolvi()
    {
     if(p_trigger->Is())
            return p_action->action(p_trigger, d_arg);
      return false;
    }
};
e le rispettive specializzazioni (rispetto ai parametri) delle azioni...
class    ActionInt
:
 public Action
{
public:
   virtual bool    action( Trigger* p_trigger, ...)
   {
  va_list ap;
    va_start(ap, p_trigger);
      int arg = va_arg(ap, int);
      arg = arg;
    va_end(ap);
     return true;
   }
};
class    ActionDouble
:
 public Action
{
public:
   virtual bool    action( Trigger* p_trigger, ...)
   {
  va_list ap;
    va_start(ap, p_trigger);
      double arg = va_arg(ap, double);
      arg=arg;
    va_end(ap);
     return true;
   }
};
Un trigger predefinito..
class    TestTrigger
:
 public Trigger
{
 bool b_value;
public:
 TestTrigger(bool b_v=true)
   :
  b_value(b_v)
      {
      }
    virtual    bool    Is()  { return b_value; }
    virtual    bool    IsNot() { return !b_value; }
};
Il codice che valuta i trigger ed eventualmente esegue le azioni segue piu'
o meno la logica...
   TestTrigger Trigger;
   ActionInt  ActionInt;
   ActionDouble ActionDouble;
   RegolaInt regola1 (&Trigger, &ActionInt, 1);
   RegolaDouble regola2 (&Trigger, &ActionDouble, 1.0);
    regola1.risolvi();
    regola2.risolvi();
Infine essendo tutte le regole derivate da Regola, una volta istanziate le
puoi mettere all'interno di un container di <Regola*> e valutarle
progressivamente (con una lista o un vettore), con una priorita' (con su un
etc...
Saluti.

Quant' e' l'overhead per il dynamic_cast ?
Non ti saprei dire, sicuramente non nullo e tanto
maggiore quanto piu` complessa e la gerarchia di classi,
in particolare in presenza di ereditarieta` multipla.
Secondo me utilizzando funzioni virtuali ed evitando i
cast dinamici alla fine ottieni un risultato che difficilmente
potrebbe essere peggiore, dato che i calcoli che l'operazione
di cast deve compiere non sono poi tanto banali.
Ciao,


 
L'idea a me piacerebbe, ma non piace al mio compilatore :
cxx: Error: str.cpp, line 28: no operator ">>" matches these operands
            operand types are: strstream >> TOggetto *
            Stream >> Obj;
-------------------^                                                  
l'errore riportato dal compilatore Gnu e' ancora piu' pittoresco:
str.cpp:28: warning: initializing non-const `bool &' with `TOggetto *' will use a temporary
/usr/include/g++/iostream.h:216: warning: in passing argument 1 of `istream::operator >>(bool &)'
(Ha provato a convertire il puntatore a Toggetto in un reference a bool)
Il problema e' che i puntatori non sono "streammabili".
Come minimo dovrei fare un overload dell' operatore >> per i puntatori ai tipi 
che mi interessano. Ma non so come.
Infatti, una volta inserito un puntatore nello stream, viene persa ogni informazione sul suo tipo
e non ce piu' modo di tirare questo puntatore fuori dallo stream per farci qualcosa.
Credo che lo stesso discorso valga anche se provassi a usare dei metodi ad argomenti
variabili (come suggerito da Marco Savegnago)
*Fabio*
-- 
There are a few things that never go out of style, and a feminine woman
is one of them.
		-- Ralston

 
Non ho letto tutto il thread e quindi magari l'hai gia' scartata come 
soluzione.. ma perche' non usi gli argomenti variabili? E' una delle poche 
cose del C/C++ che *veramente* in altri linguaggi sono quasi impossibili da 
fare!! :)
Usa un Azione::Esegui(...) e le funzioni di <stdarg.h>. L'uso e' 
semplicissimo, e lo trovi documentato perfettamente, ad esempio, nel K&R. In 
piu' e' portabile e supportato da tutti i compilatori (essendo una 
caratteristica essenzialmente C).

 caratteristica essenzialmente C).
Generalmente non amo gli argomenti variabili (anche se mi e' capitato spesso
di usarli): sono poco "Object Oriented" e pochissimo sicuri, infatti non e' possibile
controllare che i tipi passati siano gli stessi che ci aspettiamo.
Mi chiedevo se esisteva un'alternativa piu' elegante.
*Fabio*
-- 
God created a few perfect heads.  The rest he covered with hair.

variabili (come suggerito da Marco Savegnago)
Usando il metodo ad argomenti variabili non c'e' il problema di ridefire
nessun operatore, anche di eventuali classi usate come argomenti. Il metodo
di estrazione dallo stack degli argomenti viene eseguito da semplici macro.
L'uso di una classe di streaming per la gestione degli argomenti variabili
mi sembra abbastanza dispendioso e va' valutato in termini di costi
benefinici ma puo' essere un buon esercizio di programmazione.
Saluti.

            Stream >> Obj;
[omissis]
In effetti il problema sembra essere l'estrazione...
Una soluzione (non esattamente portabile, ma comunque
semplice e su cui puoi scrivere uno strato software) e`:
Per l'inserzione, converti il puntatore a TOggetto in void*
Args << reinterpret_cast<void*>(Obj) << endl;
per l'estrazione pare che non sia definito operator>> sui void* e
quindi bisogna ricorrere ad un trucco non portabile:
long Ptr;
Args >> Ptr;
TOggetto* OBj = reinterpret_cast<TOggetto*>(Ptr);
Pare che funzioni (almeno su win32).


benefinici ma puo' essere un buon esercizio di programmazione.
Come gia` detto, i costi andrebbero valutati in base alle esigenze
dell'applicativo, sulla base di quanto richiesto non credo ci siano le
condizioni per poter scartare la soluzione a priori (visto oltretutto
che essa e` gia` pronta e non costa fatica).
