PDA

View Full Version : Exception vs "return boolean"


guldo76
16-04-2009, 10:58
Ciao,
mi chiedo se ci siano particolari considerazioni a favore di una strategia rispetto all'altra. Mi riferisco alle alternative:
1) nessun metodo restituisce void; piuttosto si restituisce un booleano e lo si controlla;
2) il metodo restituisce tranquillamente void, ma genera un'eccezione se fallisce.

Cosa ne pensate? Sono sempre indeciso se adottare una strategia o l'altra...

EDIT: mi riferisco in generale alla programmazione a oggetti

71104
16-04-2009, 11:22
devi rispettare il significato concettuale di valore di ritorno e di eccezione. evita di restituire un booleano solo per indicare una condizione di successo o di errore: il valore di ritorno é ció che la funzione deve calcolare, quindi se le fai restituire un booleano stai dicendo che quella funzione deve calcolare un booleano. io per indicare una condizione di errore uso sempre le eccezioni, sia in C++ sia in C# sia in Java (visto che non hai specificato il linguaggio).

tra l'altro in C++ lanciare un'eccezione spesso é piu efficiente: supponi di avere 5 chiamate annidate, 5 funzioni che si chiamano a catena e di cui l'ultima potrebbe fallire; inoltre supponi che la condizione d'errore debba essere semplicemente propagata e controllata solo "in cima", cio nella prima delle 5 funzioni.
se le 5 funzioni ritornano un booleano che indica la condizione d'errore allora devi usare 4 if, uno dopo ciascuna chiamata, mentre se usi il meccanismo delle eccezioni il flusso di esecuzione salta direttamente a monte in caso di eccezione.

naturalmente per far si' che la cosa sia realmente efficiente devi evitare la pessima abitudine di lanciare come eccezioni in C++ oggetti istanziati con new, altrimenti l'efficienza va a farsi benedire (per allocare nell'heap nel caso peggiore hai bisogno di uno switch in kernel mode, e comunque passi per tutta la trafila del CRT); piuttosto lancia un booleano:

void callee()
{
if (error_condition)
{
throw false;
}
}

void caller()
{
try
{
callee();
}
catch (bool error)
{
// ...
}
}

Tommo
16-04-2009, 11:28
Dipende da vari fattori decidere quale delle due usare... a mio parere delle linee guida sensate sono queste:

-interfaccia comune
Se già usi ovunque uno dei due approcci è meglio usare sempre lo stesso per non spiazzare l'user
-prestazioni
try catch dicono siano più lenti, quindi in sezioni time-critical potrebbe essere meglio bool
-probabilità di fallimento
Se l'eccezione è comune, e fornisce dei dettagli che servono a risolverla (es file mancante) indubbiamente è meglio un'eccezione.
-gravità dell'eccezione
Se dopo l'eccezione il programma è del tutto compromesso forse ha poco senso proprio lanciarla, tanto il programma andrà chiuso lo stesso.
-modo d'uso
A volte un bool può essere più elegante di una eccezione, se il caso false è perfettamente contemplato:

if( device->createWindow() )
...

E' molto più elegante del corrispondente try...catch, almeno secondo me.

Cmq tutto ciò è del tutto IMHO :D

PGI-Bis
16-04-2009, 13:50
Le eccezioni non sono un chè di particolare della programmazione orientata agli oggetti. Appartengono alla categoria delle condizioni nella teoria dei sistemi.

Anche un boolean può esprimere una condizione, da cui deriva l'incertezza.

Se il linguaggio non ha una sintassi ad hoc per le eccezioni chiaramente uno è costretto ad usare dei valori di controllo.

Se il linguaggio ha una sintassi ad hoc per le eccezioni allora uno DEVE usarle per la stessa ragione per cui al panettiere si chiede "uno sfilatino, grazie" e non "un cilindro, grazie". E' una questione di grammatica: se un fenomeno è identificabile con parole diverse si sceglie quella massimamente specifica.

Dunque ogni volta che incontri una condizione che determina impedimento al conseguimento di uno scopo per una causa esterna al processo che deve raggiungerlo spari un'eccezione.

In questo modo chi legge il codice sa che si è verificata una condizione che ha determinato impedimento... eccetera eccetera.

guldo76
16-04-2009, 13:55
devi rispettare il significato concettuale di valore di ritorno e di eccezione. evita di restituire un booleano solo per indicare una condizione di successo o di errore

Grazie per la precisazione. Mi hai convinto. :)

naturalmente per far si' che la cosa sia realmente efficiente devi evitare la pessima abitudine di lanciare come eccezioni in C++ oggetti istanziati con new, altrimenti l'efficienza va a farsi benedire [...] piuttosto lancia un booleano

Questo purtroppo in C# non si può fare, devo lanciare un'Exception (o un'istanza di una classe che eredita da Exception).

Cmq ora faccio così: catturo l'eccezione, scrivo un log e rilancio l'eccezione stessa.

Dipende da vari fattori decidere quale delle due usare... a mio parere delle linee guida sensate sono queste:

Grazie della risposta.

Chiaramente se in un progetto si è sempre fatto in un certo modo, è opportuno continuare per la stessa strada, sicuramente.


-modo d'uso
A volte un bool può essere più elegante di una eccezione, se il caso false è perfettamente contemplato

Oh, sicuro. Se la funzione ha lo scopo di dirmi se è verificata o meno una certa condizione, di sicuro deve restituire un booleano; ma se va in errore deve generare un'eccezione, e non restituire false come se avesse potuto controllare la condizione in questione senza problemi.

Grazie mille :)

71104
16-04-2009, 13:57
-interfaccia comune
Se già usi ovunque uno dei due approcci è meglio usare sempre lo stesso per non spiazzare l'user l'utente mica deve leggere il codice


-prestazioni
try catch dicono siano più lenti, quindi in sezioni time-critical potrebbe essere meglio bool "dicono" non é neanche lontanamente una "linea guida sensata"


-probabilità di fallimento
Se l'eccezione è comune, e fornisce dei dettagli che servono a risolverla (es file mancante) indubbiamente è meglio un'eccezione. argomentazione inutile, se servono informazioni aggiuntive basta passare un puntatore a una struct (in C/C++) o restituire un intero oggetto (in C# o Java).


-gravità dell'eccezione
Se dopo l'eccezione il programma è del tutto compromesso forse ha poco senso proprio lanciarla, tanto il programma andrà chiuso lo stesso. a maggior ragione in questo caso ha senso un'eccezione (non catturata) anziché una trafila di if: oltrettutto le eccezioni SEH in Windows permettono di collezionare un crash dump che l'utente puó inviare al programmatore il quale a sua volta puó caricarlo in un debugger e vedere esattamente cosa é successo.


-modo d'uso
A volte un bool può essere più elegante di una eccezione, se il caso false è perfettamente contemplato:

if( device->createWindow() )
...
e secondo te é perfettamente contemplato che il sistema non riesca a creare una cavolo di finestra? :D
qui era decisamente meglio un'eccezione.

guldo76
16-04-2009, 14:06
Dunque ogni volta che incontri una condizione che determina impedimento al conseguimento di uno scopo per una causa esterna al processo che deve raggiungerlo spari un'eccezione.
OK, facciamo un esempio. Mi devo connettere a un DB che sta su una certa macchina remota. Evito di fare tutti i controlli che magari potrei: connettività alla rete, disponibilità della macchina, stato dell'istanza del DB, etc...
Provo semplicemente a connettermi: se riesco, OK, altrimenti genero un'eccezione (-> log) e fine del problema.
No?

71104
16-04-2009, 14:09
OK, facciamo un esempio. Mi devo connettere a un DB che sta su una certa macchina remota. Evito di fare tutti i controlli che magari potrei: connettività alla rete, disponibilità della macchina, stato dell'istanza del DB, etc...
Provo semplicemente a connettermi: se riesco, OK, altrimenti genero un'eccezione (-> log) e fine del problema.
No? orientativamente si, ma se lavori in Java bada bene che sia un'eccezione checked e comunque (per C++ e C#) preoccupati di catturarla perché stiamo parlando di una condizione d'errore esterna che non puoi controllare.

shinya
16-04-2009, 14:14
Ciao,
mi chiedo se ci siano particolari considerazioni a favore di una strategia rispetto all'altra. Mi riferisco alle alternative:
1) nessun metodo restituisce void; piuttosto si restituisce un booleano e lo si controlla;
2) il metodo restituisce tranquillamente void, ma genera un'eccezione se fallisce.

Cosa ne pensate? Sono sempre indeciso se adottare una strategia o l'altra...

Una strada non esclude necessariamente l'altra.
Io in Java ogni tanto uso una classe che mi permette di scrivere qualcosa tipo:

// pseudo-java
final Either<ExpectedResult, AnException> result = myFunction();

if (result.isRight()) {
// tutto ok qui ...
}
else {
// oh noooeesss...
}
...

PGI-Bis
16-04-2009, 14:15
orientativamente si, ma se lavori in Java bada bene che sia un'eccezione checked e comunque (per C++ e C#) preoccupati di catturarla perché stiamo parlando di una condizione d'errore esterna che non puoi controllare.

Bingo :mano:

Non parlerei tuttavia di condizione d'errore. L'errore è infatti un impedimento al raggiungimento dello scopo per causa interna. E' proprio il fatto che il verificarsi della condizione dipenda da un fattore esterno a determinarne la natura di eccezione.

guldo76
16-04-2009, 14:23
orientativamente si, ma se lavori in Java bada bene che sia un'eccezione checked e comunque (per C++ e C#) preoccupati di catturarla perché stiamo parlando di una condizione d'errore esterna che non puoi controllare.
Sì sì, certo. Non mi sono espresso benissimo.
Diciamo che faccio qualcosa del genere (esempio semplificato):

static void Connetti(){
try{
...
...
...
}catch (Exception ex){
ScriviLog(ex.Message);
throw ex;
}
}

static void Main(){
try{
Connetti();
}catch {
ScriviLog("bla bla");
}
}


OK?

shinya
16-04-2009, 14:29
OK?
Mica tanto... quante volte scrivi nel log? Una volta per ogni rethrow?

guldo76
16-04-2009, 14:33
Mica tanto... quante volte scrivi nel log? Una volta per ogni rethrow?
In linea di massima sì, per capire poi facilmente dal log in quale punto il programma è andato in errore. Cattiva idea? Come sarebbe meglio fare altrimenti?

shinya
16-04-2009, 14:36
In linea di massima sì, per capire poi facilmente dal log in quale punto il programma è andato in errore. Cattiva idea? Come sarebbe meglio fare altrimenti?
Facilmente? Con la stessa eccezione spalmata su tutto il log? Buona fortuna!

http://today.java.net/pub/a/today/2006/04/06/exception-handling-antipatterns.html
Guarda qual'è il primo degli anti-pattern :)

71104
16-04-2009, 14:38
Cattiva idea? *annuisce*


Come sarebbe meglio fare altrimenti? esaminare lo stack trace: il log é qualcosa che viene letta dall'amministratore di sistema, il quale non ha i sorgenti e quindi puó fare poco con gli stack frame. non ti preoccupare di questi dettagli, lo stack trace completo ce l'hai nel crash dump :)

guldo76
16-04-2009, 15:04
Facilmente? Con la stessa eccezione spalmata su tutto il log? Buona fortuna!

http://today.java.net/pub/a/today/2006/04/06/exception-handling-antipatterns.html
Guarda qual'è il primo degli anti-pattern :)

:ops:

*annuisce*


esaminare lo stack trace: il log é qualcosa che viene letta dall'amministratore di sistema, il quale non ha i sorgenti e quindi puó fare poco con gli stack frame. non ti preoccupare di questi dettagli, lo stack trace completo ce l'hai nel crash dump :)
Crash dump?

Quindi mi state dicendo piuttosto è meglio non catturare mai nessuna eccezione se non nel Main, in cui prendo lo stack trace e lo scrivo nel log?

71104
16-04-2009, 17:41
:ops:


Crash dump?

Quindi mi state dicendo piuttosto è meglio non catturare mai nessuna eccezione se non nel Main, in cui prendo lo stack trace e lo scrivo nel log? lo stack trace non lo devi "prendere" tu: fa parte del crash dump, che viene collezionato automaticamente in un Windows opportunamente configurato quando un programma crasha per essersi lasciato scappare un'eccezione SEH; tutto questo discorso riguarda solamente il C e il C++ e solamente su Windows.

se invece vogliamo parlare di Java la risposta diretta a questo thread é: non ritornare mai valori che indichino condizioni di errore o di successo, usare sempre le eccezioni per indicare condizioni di errore e che siano eccezioni checked per errori causati da fattori esterni (mancaza di connettivitá, files di inizializzazione non trovati, ecc.) e unchecked per bug del tuo programma (riferimenti nulli, index out of bounds, ecc.); per quanto riguarda la cattura delle eccezioni: quelle unchecked lasciale sempre propagare, quelle checked catturale nel punto del programma in cui é appropriato reagire alla condizione d'errore. i crash dump in quella cagata di Java ( :asd: ) purtroppo non esistono, in caso di eccezione unchecked lo sviluppatore dovrá accontentarsi dello stack trace che viene stampato automaticamente da Java su System.out quando un'eccezione esce fuori dal main o dal metodo run di un thread.

per quanto riguarda infine C#, vale tutto come Java salvo che:
- non esiste distinzione tra eccezioni checked e unchecked, quindi questa distinzione é affidata a te che devi discernere tra quando catturare un'eccezione nel tuo codice e quando lasciarla andare.
- é possibile collezionare crash dump e caricarli nel debugger, ma purtroppo non ho idea di come si faccia.

guldo76
16-04-2009, 18:06
Grazie davvero per l'aiuto :)

Ma a questo punto mi chiedo:

Supponiamo che io abbia un metodo Main (che non restituisce nulla) che al suo interno chiama un metodo Login (che non restituisce nulla). Supponiamo che la procedura di Login fallisca per problemi di connettività, ma all'interno del metodo io mi occupi di gestire questa eccezione (checked) e scrivere sul log, senza rifare il throw dell'eccezione. A questo punto, il Main che ha richiamato il Login, come viene messo al corrente della situazione?

cdimauro
16-04-2009, 20:30
i crash dump in quella cagata di Java ( :asd: ) purtroppo non esistono, in caso di eccezione unchecked lo sviluppatore dovrá accontentarsi dello stack trace che viene stampato automaticamente da Java su System.out quando un'eccezione esce fuori dal main o dal metodo run di un thread.
Hai detto niente. :D
per quanto riguarda infine C#, vale tutto come Java salvo che:
- non esiste distinzione tra eccezioni checked e unchecked, quindi questa distinzione é affidata a te che devi discernere tra quando catturare un'eccezione nel tuo codice e quando lasciarla andare.
- é possibile collezionare crash dump e caricarli nel debugger, ma purtroppo non ho idea di come si faccia.
Piccola nota: sembra che le eccezioni in .NET siano abbastanza lente, paragonate ad altri linguaggi/framework/runtime.

71104
16-04-2009, 22:03
Hai detto niente. :D mai visto un crash dump? mai provato a caricarlo in Visual Studio? é come fare il debug del programma al momento del crash: gli stack trace di ogni singolo thread sono il minimo.


Piccola nota: sembra che le eccezioni in .NET siano abbastanza lente, paragonate ad altri linguaggi/framework/runtime. fonti?

PGI-Bis
17-04-2009, 03:07
i crash dump in quella cagata di Java ( :asd: ) purtroppo non esistono

Ritenta, sarai più fortunato.

PGI-Bis
17-04-2009, 03:10
i crash dump in quella cagata di Java ( :asd: ) purtroppo non esistono

Ritenta, sarai più fortunato.

cdimauro
17-04-2009, 07:09
mai visto un crash dump? mai provato a caricarlo in Visual Studio? é come fare il debug del programma al momento del crash: gli stack trace di ogni singolo thread sono il minimo.
Capito. Allora non c'è proprio paragone. :D
fonti?
Se ne parlava nella mailing list di Python, in cui si lamentavano gli sviluppatori che effettuano il porting di Python su .NET (IronPython).

In Python è comune l'utilizzo dell'eccezioni, e CPython (l'implementazione ufficiale) è abbastanza veloce nello gestirle, ma in IronPython hanno un drastico calo prestazionale.

shinya
17-04-2009, 08:44
Giusto per completezza, anche in Java le eccezioni sono "lente" di default, ma si possono velocizzare scendendo a compromessi.
http://www.javaspecialists.eu/archive/Issue129.html

guldo76
17-04-2009, 09:45
Mi fa piacere che ne sia uscita una discussione interessante.

Cmq io alla fine non trovando di meglio farò così:

bool FaiQualcosa(){
try{
return isTuttoOK();
}catch(EccezioneSpecifica ex){
Log("messaggio di log");
return false;
}
}
vale a dire: restituisci FALSE sia che il valore da restituire sia effettivamente FALSE, sia che si verifichi un errore gestito.

71104
17-04-2009, 18:35
Ritenta, sarai più fortunato.

Ritenta, sarai più fortunato.

ma come, ho tentato una volta sola e ho giá preso due sóle? :D
scherzi a parte, se un programma Java mi crasha a causa di un'eccezione come dovrei fare ad ottenere un crash dump cosi da poterne fare il debug dopo la terminazione? naturalmente si suppone che al momento del crash il programma giri in maniera indipendente, non al di sotto di un debugger.

71104
17-04-2009, 18:41
Se ne parlava nella mailing list di Python, in cui si lamentavano gli sviluppatori che effettuano il porting di Python su .NET (IronPython).

In Python è comune l'utilizzo dell'eccezioni, e CPython (l'implementazione ufficiale) è abbastanza veloce nello gestirle, ma in IronPython hanno un drastico calo prestazionale. capisco; se il calo é dovuto all'istanziamento dell'oggetto dell'eccezione (ipotizzo) allora si puó ammortizzare istanziando l'eccezione una volta sola e lanciando sempre lo stesso oggetto, come mostrato nel link postato da sinhya; a quel punto l'eccezione puó metterci tutti i millisecondi che gli pare per l'istanziamento, e quindi si puó anche conservare lo stack trace.

edit - minchia che cazzata allucinante che ho scritto :asd:

Kralizek
17-04-2009, 18:47
da quello che so il problema non è istanziare le eccezioni quanto il fatto che il runtime prima di fare un throw non dato dall'utente prova 2 volte ad eseguire il blocco di codice. Infatti debuggando della roba nei crash dump ci trovi spesso first-time exceptions che non portano a nessun crash.

A dire il vero non ho invetigato, ma magari c'entra qualcosa con il calo di prestazioni.

71104
17-04-2009, 18:55
da quello che so il problema non è istanziare le eccezioni quanto il fatto che il runtime prima di fare un throw non dato dall'utente prova 2 volte ad eseguire il blocco di codice. Infatti debuggando della roba nei crash dump ci trovi spesso first-time exceptions che non portano a nessun crash.

A dire il vero non ho invetigato, ma magari c'entra qualcosa con il calo di prestazioni. si chiamano first-chance (non first-time) e non c'entra assolutamente niente.

Kralizek
17-04-2009, 19:26
si chiamano first-chance (non first-time) e non c'entra assolutamente niente.

ottimo :P

a dimostrazione di quanto poco ne sapessi :P

cdimauro
17-04-2009, 19:44
capisco; se il calo é dovuto all'istanziamento dell'oggetto dell'eccezione (ipotizzo) allora si puó ammortizzare istanziando l'eccezione una volta sola e lanciando sempre lo stesso oggetto, come mostrato nel link postato da sinhya; a quel punto l'eccezione puó metterci tutti i millisecondi che gli pare per l'istanziamento, e quindi si puó anche conservare lo stack trace.

edit - minchia che cazzata allucinante che ho scritto :asd:
L'esempio di shinya è molto interessante, ma complica un po' il codice.

Il problema più grosso riguarda non la creazione dello stack trace, quanto la gestione dell'eccezione e del relativo unrolling dello stack.

Comunque ho recuperato il thread (http://mail.python.org/pipermail/python-dev/2009-March/087237.html) in cui se ne parla. C'è anche un link a un interessante articolo su MSDN che tratta specificamente l'argomento.

PGI-Bis
17-04-2009, 20:32
ma come, ho tentato una volta sola e ho giá preso due sóle? :D

Amo le interfacce web perchè ti danno quel senso di sicurezza quando premi il pulsante "invio" e ti dicono che l'operazioni non è andata a buon fine, e tu ritenti e ritenti e ritenti...

scherzi a parte, se un programma Java mi crasha a causa di un'eccezione come dovrei fare ad ottenere un crash dump cosi da poterne fare il debug dopo la terminazione? naturalmente si suppone che al momento del crash il programma giri in maniera indipendente, non al di sotto di un debugger.

Bisogna distinguere tra il crash della jvm e il crash dell'applicazione java che gira nella jvm.

I crash della jvm generano automaticamente un dump, l'hs_err_pid. E questi possono essere prodotti da eccezioni non gestite. Perchè è un programma nativo, ha parti scritte in C++ e in C++ un programma può terminare per il rilascio di un'eccezione non controllata. Non è un dump particolarmente bello a vedersi ma è molto dettagliato. Per vederne uno basta creare un metodo nativo che rilascia un'eccezione o accede ad un puntatore nullo, invocare il metodo da un programma Java e ci si ritroverà con un hs_err_pid.txt.

Per i crash di un'applicazione Java la faccenda è invece più bizzarra. Un'applicazione Java non crasha mai per il rilascio di un'eccezione non controllata. Quando sulla console salta fuori la sfilza di linee di uno stack trace l'applicazione non è crashata: è ancora funzionante. Lo stack trace che vedi sulla console non è il risultato di una fuoriuscita dell'eccezione dal programma: è prodotta dall'UncaughtExceptionHandler del Thread in cui si è verificata l'eccezione. E ee l'applicazione è ancora in esecuzione puoi tranquillamente fare un dump con jmap o jvisualvm.

Se l'applicazione termina dopo il rilascio di un'eccezione non controllata è perchè è defunto l'ultimo thread non demone della JVM. La jvm non fa un dump automatico ogni volta che termina, bisogna per forza connettergli un JMXBean prima che si spenga. Non è esattamente "farla girare in un debugger" ma è comunque richiesto che si sappia dell'esistenza di un possibile problema.

cdimauro
17-04-2009, 20:45
Amo le interfacce web perchè ti danno quel senso di sicurezza quando premi il pulsante "invio" e ti dicono che l'operazioni non è andata a buon fine, e tu ritenti e ritenti e ritenti...

Più che altro verso quell'ora (poco dopo le 4 del mattino) c'è il db del forum in manutenzione, per cui non è consigliabile scrivere messaggi. :p