Newsgroup: it.comp.lang.c++
Titolo: A proposito di exception

Non so se e` lo standard a comandarlo, ma trovo tremendamente nocivo 
inserire la dichiarazione throw sul prototipo delle funzioni, perche' 
perlomeno col GCC (testato da 3.3 a 4.1) succede che se una funzione a 
un livello inferiore ti tira un exception diversa dal tipo che dichiari 
nel prototipo l'exception ti salta fuori anche da un handler generico.
int c() throw(std::string &);
Se qualcosa all'interno di c() o a un livello inferiore genera un 
exception di tipo diverso da "std::string" l'exception salta fin fuori 
dal main...
Mi ricollego al thread "Pattern Rii" dove Davide Quack usava proprio 
questa sintassi in uno dei suoi esempi.
Esempio:
#include <iostream>
#include <string>
int a()
{
     throw std::string("Ciao!"); // con questa linea funziona
     throw int(2); /* con questa linea vado in core dump (con quella 
sopra commentata ovviamente */
}
int c() throw(std::string &) /* se tolgo il throw di qui funziona tutto 
correttamente */
{  a();  }
int main()
{
     try {
         c();
     }
     catch(std::string &s) {
         std::cerr << "Catturata exception: " << s << "\n";
     }
     catch(...) {
         std::cerr << "Catturata exception sconosciuta.\n";
     }
}

 la dichiarazione throw sul prototipo delle funzioni,
Con il VC2005 c'è questo warning abbastanza significativo.
warning C4290: C++ exception specification ignored except to indicate a 
function is not __declspec(nothrow)
-- 
http://aratriel.wordpress.com/2007/11/05/in-lavorazione-il-primo-teaser-trailer-di-star-trek-2008/

 nel prototipo l'exception ti salta fuori anche da un handler generico.
Se non metti la clausola throw nel prototipo della funzione, 
implicitamente stai dicendo che la funzione può lanciare qualsiasi tipo 
di eccezione. Se invece metti la clausola throw, allora la funzione può 
lanciare SOLO le eccezioni che hai specificato.
Dunque una funzione che dichiara di lanciare SOLO un'eccezione di tipo 
T, non può chiamare una funzione che dichiara di lanciare un'eccezione 
di tipo S. Seguendo questa logica una funzione che lancia solo T non 
potrebbe chiamare una funzione che non dichiara cosa lancia (perchè 
potrebbe lanciare S!). Invece si può, e la ragione e pragmatica: siamo 
pieni di codice che non specifica le eccezioni, e quel codice 
risulterebbe inutilizzabile...
Dunque è plausibile (ma di questo non sono sicuro!) che se a una 
funzione che dichiara le proprie eccezioni capita di lanciare una 
eccezione diversa, il compilatore può decidere di fare quel che gli 
pare... O forse è addirittura costretto a termiare il programma, perchè 
potrebbe essere pericoloso lanciare un'eccezione diversa da quella che 
il codice si aspettava come unica possibilità.
E.

 nel prototipo l'exception ti salta fuori anche da un handler generico.
E' lo standard, AFAIK. Ed è giusto che sia così.
come ti è stato detto, se un metodo dichiara le eccezioni lanciate, non
può alla fine lanciarne altre; in questo caso viene lanciato un handler
di errore formale (è un controllo a runtime). Purtroppo questo controllo
non viene effettuato alla compilazione, per cui diventa quasi
inutilizzabile il meccanismo di enforcement del controllo sulle
eccezioni, vanificando, secondo me, un buon meccanismo di controllo
della correttezza.
Java, per esempio, obbliga a raccogliere tutte le eccezioni prima.

 non viene effettuato alla compilazione, 
...in quanto è impossibile farlo!
E.

 ...in quanto è impossibile farlo!
Perché impossibile? Solo perché (a quanto ho capito) è permesso alle
funzioni/metodi lanciare eccezioni senza dichiararle.

 Java, per esempio, obbliga a raccogliere tutte le eccezioni prima.
Internet e' piena di commenti, anche autorevoli, su come il meccanismo
di Java abbia anche lui i suoi bei problemini, a cominciare da:
http://accu.org/index.php/journals/406
Ciao,
Nicola

Nicola Musatti ha scritto:


E chi non ne ha? :)
Ad ogni modo, interessante articolo, grazie per la segnalazione.


Esatto.
E.

 di tipo S.
Non e' vero: basta che la chiamata sia inserita in un blocco try/catch, 
che ne evita la propagazione.
  bye
	av.

 nel prototipo l'exception ti salta fuori anche da un handler generico.
Come dicevo a manu?, lo standard richiede che su eccezione inaspettata
viene chiamata unexpected. Questa di default chiama terminate che chiama
abort.
Puoi ridefinire unexpected. Ma in buona sostanza io mi terrei lontano
dalle eccezioni specificate. L'unica cosa che riconosco 'utile' è la
throw(), quando sono *veramente* sicuro che non partano eccezioni per
nessun motivo e rendere esplicita la garanzia nothrow di exception
safety.
Che so: prese due matrici per riferimento (con le dimensione parti del
tipo e non dipendenti templaticamente dal tipo di cella *o* con qualche
garanzia a margine di soli tipi primitivi), farne la somma. Sono
ragionevolmente sicuro che questo non lanci eccezioni, posso anche
mettere il nothrow(). In pratica nel mondo reale non lo farei comunque.
-- 
cc = callcc {|cc| cc.call cc}; cc.call cc

 il codice si aspettava come unica possibilità.
La storia è questa: quando hai specificato che eccezioni lanci e ne esce
una diversa, viene chiamato di default il metodo unexpected che a sua
volta (di default) chiama terminate.
terminate chiama abort, che fa si che il cleanup non venga fatto, i
distruttori non siano chiamati ed esplode tutto così come è.
Insomma, se uno pensa di usare queste tecniche (oltre a pensarci una
dozzina di volte) farebbe bene a pensare a rendere un po' furba
unexpected. Altrimenti di peggio può solo brazare il disco all'errore.


 Java, per esempio, obbliga a raccogliere tutte le eccezioni prima.
Ed è una delle tante cause della sovraingegnerizzazione dei programmi
Java, oltre che un'insopportabile perdita di tempo (e comunque sarebbe
qualcosa di assolutamente poco gestibile in ambiente templatico).


 qualcosa di assolutamente poco gestibile in ambiente templatico).
Questo non lo so.

 Questo non lo so.
Pensaci su e lo vedi da te: che eccezioni può lanciare questo metodo?
template<typename T>
void f() {
        T t;
}
La risposta è: non lo sai e non lo puoi sapere. 


 ritorno delle funzioni di libreria.
Il problema e' che ti impone di preoccuparti di aspetti che non ti
riguardano, forzando la violazione del principio della "separazione
delle preoccupazioni" (= separation of concerns).
Un esempio: sto adattando una libreria client FTP (in C#, ma fa lo
stesso), dove ho un metodo SendFile che in pseudo codice e' piu' o
meno cosi':
void SendFile(string localName, string remoteName) {
  stream s;
  s.open(localName);
  SendStream(s, remoteName);
}
Ora, io so che SendStream lancia esclusivamente eccezioni di tipo
ServerError, pero' perche' dovrei preoccuparmi di cosa puo' lanciare
s.open()? Di sicuro dice di piu' l'eccezione originale di quanto non
possa dire un'eccezione tradotta da me e in questo caso la specifica
delle eccezioni si tradurrebbe nel copia e incolla da parte mia della
corrispondente documentazione, tempo che potrei usare in modo
migliore.
Ciao,
Nicola

 migliore.
Vero. Infatti mi rendo conto che essere obbligati a dichiarare le
eccezioni è in questo caso limitante se non addirittura impossibile. Non
riesco a decidere quale dovrebbe essere la situazione corretta...