Newsgroup: it.comp.lang.c++
Titolo: Un mod un po' piu' furbo

if (MyVar_WithVeryLongAndAnnoingName_And_SubName == 1
    || MyVar_WithVeryLongAndAnnoyingName_And_SubName == 44
    || MyVar_WithVeryLongAndAnnoyingName_And_SubName == 121
    || MyVar_WithVeryLongAndAnnoyingName_And_SubName == 553
    || MyVar_WithVeryLongAndAnnoyingName_And_SubName == 743
    || MyVar_WithVeryLongAndAnnoyingName_And_SubName == 777
    }
    MessageBeep(-1);
Non c'e' un modo un po' piu' intelligente di scrivere una cosa cosi'?
Tipo:
if (MyVar_WithVeryLongAndAnnoyingName_And_SubName == (1 || 44 || 121
|| 553 || 743 || 777) )
  MessageBeep(-1);
oppure
if (MyVar_WithVeryLongAndAnnoyingName_And_SubName in (1, 44, 121, 553,
743,777) )
  MessageBeep(-1);
???
please remove NOSPAM from email address to write me.
-------------------------------------------------------
Free download software: Post-it, Scheduler, Slide-show, Calendar, 
  Network Chat, Network Mail, Phone book, Apps Laucher... at:
http://come.to/oddera

 Non c'e' un modo un po' piu' intelligente di scrivere una cosa cosi'?
switch(MyVar_WithVeryLongAndAnnoingName_And_SubName)
{
  case 1:
  case 44:
  ....
  case 777:
    MessageBeep(-1);
    break;
}

 Non c'e' un modo un po' piu' intelligente di scrivere una cosa cosi'?
Prova con:
switch (MyVar_WithVeryLongAndAnnoingName_And_SubName)
{
    case: 1
    case: 44
    case: 121
    case: 553
    case: 743
    case: 777
        MessageBeep(-1);
        break;
}
EMG

...


    || MyVar_WithVeryLongAndAnnoyingName_And_SubName == 121
....

Non c'e' un modo un po' piu' intelligente di scrivere una cosa cosi'?
Che ne dici di questo?
int i = MyVar_WithVeryLongAndAnnoingName_And_SubName;
if (i == 1 || i == 44 || i == 121 || i == 553 || i == 743 || i == 777)
Seriamente... potresti dichiarare un array
int valori[6] = { 1, 44, 121, 553, 743, 777 };
e verificare se la variabile assume uno di questi valori:
int index = 0;
while (index < 6 && var != valori[index++]);
if (index < 6) MessageBeep(-1);
--
Byez
Stefano Tempesta
temp...@tin.it
http://tempesta.freeweb.org

Grazie per le idee, quella del case, e quella del for.
Sono trucchi, pero' funzionano. Certo che considero questa una vera
mancanza del linguaggio, cosi' come l'impossibilita' di avere un
codice di uscita dalle funzioni senza usare un goto.


 codice di uscita dalle funzioni senza usare un goto.
Cosa intendi?  
Di solito per uscire dalle funzioni si usa return.
	Mauro

 codice di uscita dalle funzioni senza usare un goto.
In che senso? Per uscire da una funzione devi utilizzare l'istruzione
return.
EMG

cosi' come l'impossibilita' di avere un codice di uscita dalle funzioni
senza usare un goto.
Che dici?!?!? E i vari return, break e continue che sarebbero allora? Al
contrario, dovresti evitare di usare goto per non "infrangere" le buone
regole di programmazione strutturata!


> 
http://www.geocities.com/sunsetstrip/stage/7787
 <<
<<<<<<<<<<<< 
DucaCr...@iol.it
 >>>>>>>>>>>>>>
*** nel NordOvest dell'India, per tradizione ogni anno si fa del vittimismo.
                                (by Elio & storie tese)

Di solito per uscire dalle funzioni si usa return.
Intendo questo:
void fun()
{
char *ch = new char(255);
dai_un_valore_a_ch (ch);
...
if (qualcosa)
  exitcode;
... // un sacco di codice
if (qualcosaltro)
  exitcode;
...
exitcode:
delete []ch;
}
invece di questo:
void fun()
{
char *ch = new char(255);
dai_un_valore_a_ch (ch);
...
if (qualcosa)
  {delete [] ch; 
  return;
  }
... // un sacco di codice
if (qualcosaltro)
  {delete [] ch; 
  return;
  }
...
delete []ch;
}
il che evita di ripetere inutilmente un sacco di volte il delete ch,
se le cose da distruggere in uscita sono tante e' noioso ripeterle per
ogni return, inoltre e' fonte di errori, infatti se in seguito si
aggiunge qualcosa, e' difficile ricordarsi di inserire in tutti i
return tutte le distruzioni necessarie.
Un exitcode del genere potrebbe essere anche piu' furbo di un goto,
per esempio in un caso cosi':
void fun()
{
char *ch = new char(255);
dai_un_valore_a_ch (ch);
...
if (qualcosa) // PRIMA uscita
  exitcode;
char *ch2 = new char(255); // dichiarazione DOPO un precedente
exitcode
... // un sacco di codice
if (qualcosaltro) // SECONDA uscita
  exitcode;
...
exitcode:
delete []ch;
delete []ch2;
}
se si esce alla PRIMA uscita in exitcode ci si ritrova con un delete
ch2, mentre ch2 NON e' ancora stato dichiarato, poiche' viene
dichiarato DOPO PRIMA uscita, in tal caso il compilatore potrebbe
generare automaticamente un codice che non considera ch2 per le uscite
precedenti la dichiarazione.


 return tutte le distruzioni necessarie.
Il C++ offre un modo 'pulito' per fare tutte queste cose : basta utilizzare
delle variabili automatiche. Per cose piu' complesse ci sono i distruttori.
Ad esempio se invece di allocare con new usi un array o, meglio ancora un
vector<> , esso verra' automaticamente liberato all'uscita dal blocco in
cui esso e' stato istanziato.
Per cose piu' complesse pui usare una classe con costruttore e distruttore
non banali.
Ad esempio
class Foo
{
   char *fred;
   int  *barney;
 
   // altre cose
public:
   Foo() { fred = new char(100); barney = new int(50); /* altro */ }
  ~Foo() { if(fred) delete fred; if(barney) delete barney; /* altro */ }
};
void Bar()
{
	Foo  foo; // il costruttore alloca tutto
	...
	if ( qualcosa )
		return; // viene invocato automaticamente il distruttore di foo
	else
	{
		Foo foo2; 
		...
		if ( qualcosaltro )
			return; // vengono distrutti sia foo che foo2
	}
	...
// viene distrutto foo
}
La tua classe puo' anche fare cose piu' complesse che richiedano e rilascino
altre risorse come aprire e chiudere un file o bloccare e liberare dei semafori,
il meccanismo funziona allo stesso modo.
Ciao
Fabio
-- 
Princeton's taste is sweet like a strawberry tart.  Harvard's is a subtle
taste, like whiskey, coffee, or tobacco.  It may even be a bad habit, for
all I know.
		-- Prof. J.H. Finley '25

 char *ch = new char(255);
EMG

    Foo() { fred = new char(100); barney = new int(50); /* altro */ }
Anche tu fai lo stesso errore. Si scrive
Foo() { fred = new char [100]; barney = new int [50]; /* altro */ }
EMG

 EMG
Hai ragione,
Ormai uso cosi' raramente new e delete che non ne ricordo piu' neanche
la sintassi (nel mio posting ho pure sbagliato le delete, dimenticando le [])
Fabio
-

il meccanismo funziona allo stesso modo.
Purtroppo non funziona affatto, e anche con le classi ci sono un sacco
di casi in cui non ottieni proprio nulla. L'esempio del new char era
volutamente semplice. 
Intanto dovresti considerare che anche le classi devi pur scriverle,
quindi se stai scrivendo una CString devi a un certo punto pur usare
la new char, cosi' ti ritrovi al punto di prima.
Usando le classi gia' esistenti, per esempio puoi considerare lo
stesso codice che ho postato, con questa variante di classe, e ti
trovi ancora con gli stessi problemi:
void MyClass::fun()
{
TPalette *pal=0;
TWindowDC dc (HWindow);
dc.SelectStockObject(DEFAULT_PALETTE);
...
if (qualcosa)
  {
  try
    { pal= newTPalette(*MyDib); }
  catch (TGdiObject::TXGdi&)
    {pal=0;}
  if (pal)
    {dc.SelectObject(*pal);
    qualcosa();
    ...
    exitcode;
    }
  }
... // un sacco di codice
if (qualcosaltro)
  {
  try
    { pal= newTPalette(*MyDib_2); }
  catch (TGdiObject::TXGdi&)
    {pal=0;}
  if (pal)
    {dc.SelectObject(*pal);
    qualcosa();
    ...
    exitcode;
    }
  }
...
dc.SelectStockObject(DEFAULT_PALETTE);
qualcosa();
...
exitcode:
dc.RestoreObjects();
if (pal) delete pal;
}
Potresti sempre usare il GOTO, non e' peccato mortale usare il goto
ogni tanto, ricordiamoci che il peccato consisteva nell'uso smodato
del goto, goto NON e' il diavolo. Purtroppo pero' in C non puoi'
dichiarare variabili dopo un goto, cosi' una cosa come questa non
funziona:
void pippa();
{
bool xx= test(xx);
if (xx) goto fine;
for (int i=0; i<10; i++) // la dichiarazione di i infastidisce il goto
  qualcosa();
fine:;
pulizia();
}
Non sono certo che il goto sia cosi' restrittivo per motivi
tecnico-logici, mi pare che in altri linguaggi non ci sia alcun
problema ad avere variabili dopo il goto, propendo piuttosto a credere
che siccome alla nascita del C il goto imperversava tragicamente
(ricordiamo pero' che le macchine avevano magari 1kb di memoria), si
sia voluto dissuadere in modo estremo il suo uso. Il che non
corrisponde piu' alle esigenze dei tempi, visto che la goto_pratica e'
pressoche' abbandonata.
Un altro esempio potrebbe essere:
all'interno di una funzione di classe, crei (con un new) un certo
elemento locale alla funzione, poi ci fai alcuni test, se ok crei
altri elementi con i loro test. Supponi che ogni test presupponga di
dover avere l'elemento precedente, cosi' non puoi fare tutto assieme.
Se tutto ok aggiungi questi elementi a un vector globale per la
classe, e devi guardarti bene dal distruggerli, visto che se lo
facessi poi tenterebbe di distruggerli anche il vector alla
distruzione della classe stessa (a parte che avrebbe dei puntatori
invalidi, se lo usi). Se strada facendo incontri problemi, salti al
codice di clean-up, che distrugge tutti gli elementi, senza
aggiungerli al vector.
Per questo ritengo che una nuova istruzione C sarebbe molto comoda,
potrebbe proprio usare le keyword cleanup ed exitproc ;
{
...
exitproc; // salta a cleanup, se esiste, se no equivale a return
...
return; // permette di evitare cleanup
...
cleanup;
...


 aggiungerli al vector.
clsss Pluto {
public:	// operations
 ...tutte le operazioni che desideri...
public:	// ctor/dtor
 Pluto();
 virtual ~Pluto();
};
class Pippo {
public:	// operations
 bool Open();
 void Close();
public:	// ctor/dtor
 Pippo();
 virtual ~Pippo() { Close(); }
private:
 std::vector<Pluto*> _plutos;
};
bool Pippo::Open()
{
 Pluto* p1 = new Pluto;
 if (p1==NULL) return false;
 
 _plutos.push_back(p1);
 Pluto* p2 = new Pluto;
 if (p2==NULL) return false;
 _plutos.push_back(p2);
 ...
 altre istruzioni di allocazione
 ...
 ...
 se sei arrivato fino a qui tutte le allocazioni sono state
 completate correttamente, quindi esegui le successive operazioni
 ...
 return true;
}
void Pippo::Close()
{
 while(!_plutos.empty()) {
  delete _plutos.back();
  _plutos.erase(_plutos.end()-1);
 }
}
...client della classe Pippo, tipo 1...
{
  Pippo p;
  if (p.Open()==false) 
   return;  // la classe e il vettore che contiene vengono
  	    // liberati automaticamente
  ... altre istruzioni
}
...client della classe Pippo, tipo 2...
{
  Pippo* p = new Pippo;
  if (p==NULL) {
   delete p;  // liberato Pippo e il suo vettore di Pluto
   return;
  }
  ... altre istruzioni
  delete p;  // liberato Pippo e il suo vettore di Pluto
}
Sicuramente si possono scrivere soluzioni piu' eleganti di questa,
ma da quello che ho scritto sopra puoi vedere che la allocazione e 
la liberazione delle risorse puo' essere effettuata anche senza 
l'utilizzo di salti a label, come il goto (o affini) prevede.
La Close() libera le risorse allocate nella Open(), ma solo quelle che
sono state assegnate al vettore _plutos, quindi non devi preoccuparti
di tener traccia delle allocazioni andate a buon fine dato che e' il
vettore che ne tiene traccia automaticamente.
massimo

 
[cut - esempio]
Ma non potevi usare cose piu' standard? Non tutti usano lo stesse
librerie e conoscono le tue classi. Per esempio perche' pal viene
allocata con newPalette e non con new? Perche' nel; catch c'e'
TGdiObject::TXGdi e non std::bad_alloc?  Non e' che la libreria
fornisce qualche supporto per la pulizia?
  void
  MyClass::fun()
  {
    ...
    if (qualcosa) {
      try {
        auto_ptr<TPalette> pal( newTPalette(...) );
        qualcosa( pal );
      }
      catch (...) {
      }
    }
    ...
    if (...) {
      try {
        auto_ptr<TPalette> pal(...);
      ....
  }
Se c'e' bisogno di chiamare automaticamente qualcosa che non sia
il distruttore quando si esce da una funzione, puoi sempre usare
una classe ausiliaria.
Esempio: una classe Lock con funzioni lock() e unlock(), se
si chiama lock(), prima di uscire bisogna chiamare unlock().
Si puo' definire una classe Guard:
  class Guard {  // semplificata
    Lock & _lck;
  public:
    Guard( Lock & lck ) : _lck(lck) { _lck.lock() }
    ~Guard() { _lck.unlock() }
  }
poi nella funzione:
 
  void func( Lock & lock )
  {
    Guard guard( lock );
    ...
    if (qualcosa1) return;
    ...
    qualcosa( che puo' lanciare eccezioni );
    ...
    if (qualcosa2) return;
    ...
  }
Prima di uscire dalla funzione il lock viene sempre liberato.
Nel caso del goto o del codice ripetuto a ogni return, quando
arriva un'eccezione, si esce dalla funzione senza liberare
le risorse (a meno di intercettare tutte le eccezioni,
liberare le risorse, poi eventualmente rilanciare l'eccezione).
	Mauro

 }
Anche qui, non usando blocchi, ci sarebbero i problemi di scoping.
Comunque questa struttura e' riconducibile facilmente al throw/catch
Fabio

allocata con newPalette e non con new? 
Oop, trattasi di errore di battitura, manca lo spazio, ho scritto:
pal= newTPalette(*MyDib);
ma intendevo:
pal= new TPalette(*MyDib);


 [...]
Questo 'e vero, ma e' una altro problema. Nel caso del CAD (o di
altri grossi programmi in generale), c'e' un ulteriore ostacolo
all'OO: i programmatori si trovano con migliaia di righe di codice
tradizionale gia' scritto e funzionante, ed e' piu' facile andare
avanti con quello che riprogettare tutto in maniera piu' OO.
	Mauro

 e' porlo =0, cosi' non punta a nulla.
Questo no: il puntatore č una variabile come tutte le altre e se č in
classe di allocazione automatica viene, come tutte le altre variabili
automatiche, distrutto automaticamente alla fine del suo scope. Porlo o
meno a zero non fa alcuna differenza anzi puň essere un errore in quanto
cosě facendo si perderebbe il riferimento all'oggetto puntato e questo
non potrebbe piů essere distrutto attraverso il distruttore della classe
a cui appartiene.
EMG

a cui appartiene.
Umh, forse non mi sono spiegato bene, intendevo quello che capita
quando lo aggiungi a un vector. Per esempio se crei una TMyListBox, e
invece di assegnare le stringhe assegni puntatori a qualche oggetto, a
un certo punto durante la distruzione della ListBox arriveranno i
messaggi EvDeleteItem, e poi quello di distruzione della List stessa.
Durante il DeleteItem devi recuperare il puntatore dal membro
ItemData, convertirlo nel tipo di oggetto puntato dal tuo ItemData,
chiamare il distruttore con delete, quindi porre ItemData a 0, non
facendo cosi' viene chiamato in seguito un distruttore di stringa
credo, comunque sia il programma va in crash. 
Questa tecnica va usata quando una classe possiede un puntatore
generico void, o un ancor piu' generico long, e non ha idea di che
tipo di oggetto sia esso, dunque non ha possibilita' di chiamare un
distruttore opportuno. Dunque il riferimente all'oggetto puntato DEVE
essere perso, perche' se e' stato distrutto l'oggetto non e' piu'
valido.


 valido.
Questo mi sembra solo un modo barbaro di organizzare le cose e
nient'altro. Se, come immagino, stai parlando delle MFC, questo
ribadisce ancora una volta quanto queste ultime siano fatte male
(approfitto a sfogarmi quando non c'č Alex... :-) ).
EMG

 
Non ti preoccupare e' normale, sembra che le discussioni filosofiche siano
piu' piacevoli,
e  come dice EMG per tua fortuna non c'e' quel fenomeno di Alex in giro,
altrimenti non finiresti piu' questo thread. ( a me e' capitato qualche
cosa di analogo ). 
Condivido con te la rottura di scatole nella deallocazione di risorse
allocate all'interno di una funzione.
Mi verrebbe da proporti una cosa del genere per risolvere/tamponare il tuo
problema ma ovviamente ha delle controindicazioni :
typedef 	void (object::*function)();
class ExitCode
{
	function		pFunc;
	object*		pObject;
public:
	ExitCode(  object* p, function f )
	{
		pObject = p;
		pFunc = f;
	}
	~ExitCode()
	{
		pObject->*(pFunc)();
	}
}
void xxx::CleanUp()
{
	// Pulizie varie
}
void xxx::yyy()
{
	ExitCode e( this, (function) CleanUp );
	// allochi e crei quel che va
	// la deallocazione scatta al momento della distruzione dello scope della
variabile 'e'
}
Il codice mi e' venuto sul momento quindi potrebbe contenere degli errori,
usalo per l'idea.
La sfortuna vuole che tu debba usare variabili visibili all'interno della
classe per poter fare il cleanup e non solo all'interno della funzione e
che la classe 'xxx' sia derivata da 'object'.
Ciao ZEC.