Newsgroup: it.comp.lang.c++
Titolo: Gestione errori C++

Ciao,
una delle maggiori difficoltï¿œ che incontro quando scrivo programmi C++ ï¿œ 
la gestione delle eccezioni.
In particolare cerco di inserire tutti i possibili controlli su azioni 
potenzialmente inclini a errori di run-time ma ogni tanto qualcuno sfugge.
Il risultato ï¿œ che magari una volta al mese il programma va in crash e 
non riesco a rendere ripetibile la situazione, quindi mi risulta 
difficile capire dove ho sbagliato.
In sintesi ho due domande:
1) esiste un libro, un tutorial o un sito particolarmente specifico su 
questo tema?
2) ï¿œ possibile gestire le eccezioni "non gestite" localmente a livello 
globale? In altre parole, se si verifica un errore a run-time non 
gestito (es. non in un try..catch) vorrei venisse chiamata una funzione 
generica.
Marco

 globale? 
http://www.cplusplus.com/reference/clibrary/cstdlib/atexit/

 
http://www.cplusplus.com/reference/clibrary/cstdlib/atexit/
Grazie della risposta. Due dubbi:
1) il documento dice "The function pointed by the function pointer 
argument is called when the program terminates *normally*."
Un crash è inteso comunque come uscita "normale"?
2) sempre da quello che ho capito la funzione atexit richiama una 
funzione a mia scelta *prima di uscire* dal programma. Questo significa 
che poi il programma terminerà comunque?
Marco

 terminerà comunque?
forse non c'entra molto e non ho capito cosa vuoi fare. Ma
se il problema è capire dove è successo qualcosa, io nei
punti in cui mi aspetto la verifica di certe condizioni
lancio un'eccezione dedicata al crash, con le macro
predefinite __FILE__ e __LINE__ come argomenti del
costruttore, poi la intercetto nel main facendo stampare i
valori.
In questo modo so dove si è verificato il problema.
Essendo io non troppo smanicato so bene che non è la
migliore delle soluzioni ma per una sorta di primo debug
mi è sempre stata utile.
ciao!
Apx.

 valori.
Vediamo se riesco a spiegarmi meglio.
Scrivo un programma e cerco di prendere le dovute precauzioni:
- controllo i valori prima di passarli alle funzioni
- uso try..catch per operazioni di I/O o comunque dove non ho il 
controllo completo della situazione
- ecc...
e infatti il programma funziona e gestisce correttamente le eccezioni. 
Succede però di rado (es. una volta al mese) che il software vada in 
crash ("il programma ha eseguito un'operazione non valida e verrà 
terminato").
Questo significa che ho lasciato scoperta una possibilità che per quanto 
rara, avviene.
Io vorrei quindi due cose, complementari, non alternative:
1) individuare il punto esatto e la motivazione per cui si è generato il 
crash (al fine di risolvere alla radice il bug)
2) nel caso di nuovi errori non ancora "scoperti" il programma non 
dovrebbe terminare, ma continuare l'esecuzione saltando il punto 
incriminato. Ad esempio con Python stampa un messaggio sulla console 
(anche se non è visualizzata) e prosegue.
Quindi il mio ideale sarebbe: in caso di eccezione non gestita che 
darebbe luogo a un crash il programma dovrebbe chiamare una funzione 
(forse è quello che fa atexit) passandomi però il riferimento all'errore 
così che io possa scrivere un file di log da analizzare successivamente. 
In seguito il programma dovrebbe continuare, senza uscire al desktop.
Spero di essermi capito :)
Marco


Sì, ora è chiaro persino per me. Ma almeno con i
compilatori a cui sono abituato io, la tua situazione "il
programma ha eseguito un'operazione non valida e verrà
terminato" vuol dire per esempio una divisione per zero di
cui io non ho controllato il valore del denominatore e per
la quale non ho previsto il lancio dell'eccezione.
Dovrebbe/potrebbe allora esserci una specie di "eccezione
di libreria", passami il termine inadeguato, che viene
lanciata automaticamente e forse quella contiene le info
di nome file e linea. Ma se c'è, ora su due piedi non
ricordo qual è.
In ogni caso mi pare che tu vorresti il lancio automatico
di un'eccezione anche se non hai codificato il caso in cui
si potrebbe presentare; questa gestirebbe tutti i problemi
per cui servirebbe invece il debugger, che ovviamente
lavora su un altro livello. Come dicevo non so se questo è
previsto dal linguaggio o da qualche sua (pericolosa)
estensione.
O potrei ancora non aver capito, ma sono lo sciacquino del
C++ sicché mi auto-perdono da me :D
ciao!
Apx.

PS - sul tuo desiderio che il programma prosegua, non
voglio insegnare niente a nessuno ma credo che sia una
situazione che va esplicitamente prevista a livello
architetturale del codice. Per esempio, una divisione per
zero a cui mi riferivo, cosa potrebbe implicare? Mi pare
ragionevole che questa possibilità vada intercettata e
gestita per permettere al programma di proseguire.
Se si lancia un'eccezione o errore fatale, a meno che non
si sia in fase di sviluppo (caso in cui io personalmente
ricompilo disabilitando il punto dove si pianta) vuol dire
che non è previsto che si possa proseguire, pena risultati
cannati.
ciao!
Apx.

 estensione.
Capisco. Il punto è che purtroppo c'è sempre poco tempo per eseguire 
bene il test dei programmi che te li strappano da sotto la tastiera.
E una "pezza" del genere farebbe comodo in attesa della rev. B :)
Marco

 cannati.
Concordo. Ma posso scegliere di avere risultati cannati senza crash? :)
Marco

Il 01/02/2011 15:32, Archaeopteryx ha scritto:

Ok, non mi sono capito ... ho tralasciato ... :-)
(tralascia, riprova o annulla ?)
dunque la cosa più rozza e rapida (dipende dalla mole del 
codice cmq) che puoi fare è:
qualsiasi pezzo di codice non gestito lo cacci tal quale 
dentro un blocco try catch, col catch GENERICO senza 
eccezione tipizzata ossia catch (...) e stampi quei 
marcatori che ti diceva arkaeopteryx (che non ti dicono la 
ragione, ma DOVE è capitato l'errore).
Ogni tanto degli "assert" (_assert(denom!=0);) ti aiutano 
magari a prevenire, oltre ad intercettare dopo. E' un modo 
per saltare dentro le catch senza scrivere miriadi di IF 
testvalue annidati, un po' più sintetico diciamo
E poi per ogni eccezione già gestita in modo strutturato, 
AGGIUNGI comuqnue, in coda, il catch (...) che trappa tutto 
il rimanente.
PEr quanto riguarda il TUO codice ora sei grossomodo apposto.
Ora se usi librerie che utilizzano anche esse la gestione 
degli errori via eccezioni (try catch throw), dovresti 
essere circa blindato (ma con una simile gestione grezza 
(...), oltre al trapping da marcatura a uomo fai poco altro, 
perché senza gestione selettiva non sai mai cosa stai 
intercettando ... non so, oggi forse esistono metodi 
ispettivi ex-post, boh, tipo qualche variabile globale di 
status del programma settata dal runtime stesso, come era 
l'antica _errno)
Se librerie che usi non usano le eccezioni, non sono sicuro 
che tutto sia davvero intercettabile, o quantomeno 
potrebbero scatenare errori ma anche lontani (nello spazio e 
nel tempo, lol) dalla vera chiamata che ha ritornato un 
errore codificato magari con un -1 ritornato, o un false non 
testato).
Quindi se non puoi controllare tutto il codice esterno, è 
quasi impossibile rimanere impermeabili e al contempo dare 
una diagnosi veramente tempestiva.
Poi vagamente mi ricordo che esistevano clausole restrittive 
da aggiungere alle funzioni (o ai prototipi) tipo nothrow(), 
ma non me ne ricordo l'uso o se c'entri una mazza.
La gestione dell'errore è davvero l'aspetto dove si 
distingue il tenace e minuzioso : se vuoi stare davvero 
attento, ti accorgi che il codice gestente errori diventa 
praticamente il doppio di quello che fa qualcosa di 
concreto, e la leggibilità è come quella di un libro tipo il 
deserto dei tartari (non succede mai un cazzo, e finisci a 
scorrere le paginate di corsa, e ti perdi i punti salienti 
dispersi nel mare)
L'è un bel casino
ciao
Soviet

 L'è un bel casino
Me ne sono accorto :)
Ciao!
Marco

 In seguito il programma dovrebbe continuare, senza uscire al desktop.
Purtroppo in C++ non si puo' fare quello che richiedi.
Il "crash" di un programma C++ e' tipicamente provocato dall'uso di un 
puntatore non valido o dall'indirizzamento di un array fuori dai limiti.
Tali errori generano un'eccezione a livello di sistema operativo, che 
non genera un'eccezione C++, ma porta alla terminazione del programma.
Nota che non tutte gli indirizzamenti di un array fuori dai limiti 
generano un'eccezione a livello di sistema operativo. Si tratta di un 
comportamento indefinito dal linguaggio, che potrebbe avere qualunque 
effetto, per esempio sovrascrivere un'altra variabile del tuo programma.
Quello che consiglio di fare e' prendere in considerazione le seguenti 
strategie:
* Usare un altro linguaggio di programmazione.
* Creare due (o piu') processi, uno di gestione e uno di elaborazione. 
Se il processo di elaborazione termina per errore, il processo di 
gestione se ne accorge e lo puo' rilanciare.
* Sostituire gli utilizzi di puntatori con riferimenti o con oggetti 
smart-pointer, e gli utilizzi di array con oggetti vector.
* In un sistema operativo di tipo POSIX (come GCC), catturare il segnale 
inviato dal sistema operativo al processo con l'apposita chiamata di 
sistema "signal".
* Nel sistema operativo Microsoft Windows, usare lo Structured Exception 
Handling. Questo puo' essere fatto in Visual C++ con appositi costrutti 
non standard, oppure, con l'opzione di compilazione /EHa, che trasforma 
le eccezioni del sistema operativo in eccezioni C++.
* Attivare un'opzione del sistema operativo (disponibile sia per i 
sistemi operativi POSIX che per Microsoft Windows) per cui ogni volta 
che un processo va in crash viene creato un file contenente lo stack del 
processo (detto "core dump"). Poi, usando un debugger e tale file, 
dovrebbe essere possibile risalire alla sequenza di chiamate di funzione 
attive e al valore delle variabili locali al momento del crash. Io pero' 
non l'ho mai fatto.
Per quanto riguarda il concetto di "ignorare l'errore e proseguire", 
questa politica spesso genera un programma inutilizzabile, in quanto le 
istruzioni immediatamente seguenti quella errata spesso non hanno senso 
in caso di errore, e corrompono ulteriormente i dati.
-- 
Carlo Milanesi
http://carlomilanesi.wordpress.com/

 terminato").
Allora sarà il sistema operativo ad aiutarti. Se puoi devi lanciare il programma
compilato in debug con il debugger attivo. Magari è scomodo, ma è la via più
semplice.
Oppure compili in release, lanci il programma con i simboli "dentro" o "vicino"
all'eseguibile (dipende dalla piattaforma) e poi aspetti che il sistema
operativo di faccia il dump dell'applicativo in crash. So gestire la cosa su
windows, ma non sotto Linux. 
I try e catch _sono_ un problema in questo caso. Se non li metti è meglio.
Banalmente, se il programma va in crash dentro un catch finiresti per inseguire
l'errore sbagliato. Sicuramente devi correggere l'errore dentro il catch, ma
devi poi correggere anche quello che ha lanciato la prima eccezione. Con il
try/catch la cosa ti è mascherata.

 in caso di errore, e corrompono ulteriormente i dati.
Si questo ï¿œ vero, infatti parlavo di "pezza" non certo di buona 
programmazione.
Marco

 try/catch la cosa ti è mascherata.
Grazie anche a te per l'intervento!
Marco

 In altre parole, cosa devo cercare con google?
Su che piattaforma operi?
Poi non mi hai risposto alla domanda principe. Ti puoi permettere di 
compilare e provare il programma in modalità debug? Se si allora fallo e 
lanciata il tuo programma sotto gdb su linux e sotto windebug sotto windows.
Windebug e un potente e ostico debugger rilasciato gratuitamente da 
Microsoft.

 Microsoft.
Ok, ma il punto è che mi è capitato di eseguire in debug l'applicazione 
per quasi una settimana continuativa. Poi, i release, dopo un mese... 
"crash"! e ovviamente non è una bella cosa ;)
Marco

 "crash"! e ovviamente non è una bella cosa
Sotto windows mi capita ancora oggi una cosa simile. Il problema, nel 
mio caso, credo fosse di ADO che su qualsiasi S.O. che non sia Windows 
7, mi da dei problemi.
Una grossa differenza tra debug e release sono le ottimizzazioni 
assenti. Non sa quello, ma ti costa poco provato. Un'altra è la gestione 
della memoria "più" conservativa.
Detto ciò, compila con i simboli (sotto windows con il compilatore 
microsoft vuol dire generare il file pdb). Poi usa windebug come 
debugger post morte. Parti da qui per le tue ricerche: 
http://support.citrix.com/article/ctx107528
Su linux no ho la stessa esperienza che su windows, parti a documentarti 
da qui: 
http://linux.die.net/man/5/core

 da qui: 
http://linux.die.net/man/5/core
Grazie mille delle informazioni.
Marco