2010-09-22 24 views
6

Il C++ offre un modo per "mostrare" qualcosa di visivo se si verifica un'eccezione non gestita?Eccezioni C++ non gestite

Quello che voglio fare è quello di fare qualcosa di simile assert(unhandled exception.msg()) se effettivamente accade (come nel seguente esempio):

void foo() { 
    throw std::exception("Message!"); 
} 

int main() { 
foo(); 
} 

Mi aspetto che questo tipo di codice non per la terminazione (perché eccezione è stata gestita), piuttosto mostra un messaggio di asserzione personalizzato (Message!).

È possibile?

+3

Perché non metti solo un blocco try/catch in 'main'? – GManNickG

+1

@GMan: un costruttore o un distruttore globale può anche lanciare il main esterno. Per il caso del distruttore, lo svolgimento potrebbe non arrivare al main. – Potatoswatter

+0

@Potatoswatter: In effetti, ero più interessato al suo particolare esempio. – GManNickG

risposta

9

Non c'è modo specificato dallo standard per visualizzare in realtà il messaggio del eccezione non rilevata. Tuttavia, su molte piattaforme, è comunque possibile. Su Windows, è possibile utilizzare SetUnhandledExceptionFilter ed estrarre le informazioni sull'eccezione C++.Con g ++ (versioni appropriate di ogni caso), il gestore può accedere interrompere l'eccezione non rilevata con un codice simile:

void terminate_handler() 
    { 
     try { throw; } 
     catch(const std::exception& e) { log(e.what()); } 
     catch(...) {} 
    } 

anzi g 's ++ predefinito interrompere gestore fa qualcosa di simile a questo. È possibile impostare il gestore di terminazione con set_terminate.

In breve, non c'è un modo generico in C++, ma ci sono modi in base alla piattaforma.

+0

Mi piace. Lo standard potrebbe non specificarlo in modo specifico, ma afferma che un'eccezione viene disattivata solo quando il blocco che lo intercetta viene chiuso. Dal momento che ciò non può accadere prima di terminare, questo alla fine dovrebbe essere portatile o almeno compatibile con il futuro. Lo stesso trucco dovrebbe funzionare con 'unexpected', e ciò consentirebbe una diagnosi di throw-on-unwind illegale. – Potatoswatter

+0

Inoltre, può catturare eccezioni generate da costruttori e distruttori globali, che un blocco catch in 'main' non può. – Potatoswatter

+0

@Potatoswatter Ho visto almeno un setup in cui questo trucco non funziona, non pretendo di essere abbastanza informato sullo standard (e sulle altre cose che stavamo facendo con la gestione delle eccezioni) per dire in un modo o nel altro, ma so che funziona su Linux con un gcc modernista. –

1

Se sto leggendo correttamente la tua domanda, stai chiedendo se è possibile sovraccaricare throw (cambiando il suo comportamento predefinito) in modo che faccia qualcosa definito dall'utente. No, non puoi.

Edit: visto che sei insistente :), ecco una cattiva idea ™:

#include <iostream> 
#include <stdlib.h> 
#include <windows.h> 

void monkey() { 
    throw std::exception("poop!"); 
} 

LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter) { 
    std::cout << "poop was thrown!" << std::endl; 
    return EXCEPTION_EXECUTE_HANDLER; 
    } 

int main() { 
    SetUnhandledExceptionFilter(&MyUnhandledExceptionFilter); 
    monkey(); 
    return 1; 
} 

Ancora una volta, questa è una pessima idea, ed è, ovviamente, dipende dalla piattaforma, ma funziona.

+0

Non 'gettare' in realtà. Voglio cambiare comportamento predefinito quando si verifica una situazione di eccezione non gestita. –

+0

Fondamentalmente, quando si lancia un'eccezione non gestita :-P –

6

Microsoft Visual C++ consente di agganciare eccezioni C++ non gestite like this. Questo è standard STL behaviour.

È possibile impostare un gestore tramite una chiamata a set_terminate. È consigliabile che il gestore non funzioni molto e quindi interrompa il programma, ma non vedo perché non è possibile segnalare qualcosa tramite un'asserzione, sebbene non si abbia accesso all'eccezione che ha causato il problema.

+1

Il gestore terminato non deve far valere. Potrebbe 'printf' (o meglio, solo' write (2) 'a' STDERR_FILENO'), comunque, e chiamare il terminate_handler che ha sostituito. Inoltre, vedi la risposta e la discussione di Logan sul motivo per cui l'eccezione dovrebbe essere ancora accessibile a quel punto. – Potatoswatter

+0

@Potatoswatter - la tua dichiarazione è applicabile in tutte le circostanze, in altre parole sarebbe possibile affermarla, ma non è consigliabile? Grazie. –

+0

È sempre possibile affermare, ma si consiglia a qualsiasi funzione del gestore di chiamare in coda il gestore precedentemente installato. – Potatoswatter

4

Se si utilizza Windows, una buona libreria per la gestione di eccezioni non gestite e arresti anomali è CrashRpt. Se vuoi farlo manualmente, puoi anche use the following I wrote in this answer.

+0

+1 per una risposta più estesa a una stretta q –

4

penso che si dovrebbe beneficiare di un catch-all economico come segue:

int main() { 
try { 
    foo(); 
catch (...) { 
    // Do something with the unhandled exception. 
} 
} 
+4

Vale la pena notare che l'eccezione stessa non è disponibile per l'introspezione in questo metodo. Questo perché potresti non lanciare un'istanza 'std :: exception', il C++ ti permette di lanciare qualsiasi tipo di valore, come un' int' o altro. Puoi aggirare il problema avendo anche un 'catch (std :: exception & e)' prima del 'catch (...)', o di qualunque tipo tu voglia catturare. – SingleNegationElimination

+0

Un buon punto in effetti. –

+0

Su Windows devi stare un po 'attento con (...) catturare l'eccezione strutturata generata internamente (ad esempio le violazioni di accesso). Potrebbe essere pericoloso continuare l'esecuzione in condizioni come queste. – seand

1

Sì, la sua possibile. Qui si va:

#include <iostream> 
#include <exception> 

void foo() 
{ 
    throw std::exception("Message!"); 
} 

int main() 
{ 
    try 
    { 
    foo(); 
    } 
    catch (std::exception& e) 
    { 
    std::cout << "Got exception: " << e.what() << std::endl; 
    } 

    return 0; 
} 
0

Lo standard C++ è il gestore di sospendere - come altri hanno detto

Se siete dopo una migliore rintracciabilità per tiri allora questo è quello che facciamo

Abbiamo un tiro macro che registra il nome del file e il numero della linea e il messaggio e poi getta. Richiede un messaggio varargs in stile printf.

Throw(proj::FooException, "Fingle %s unable to process bar %d", fingle.c_str(), barNo); 

ottengo un bel messaggio di log

Throw FooException from nargle.cpp:42 Fingle barf is unable to process bar 99 
+1

Ci sono due problemi con questo. Innanzitutto, aggiunge un'ulteriore elaborazione alle eccezioni catturate. Questo non è un grosso problema perché se succede abbastanza che l'overhead (o il rumore nel log) è un problema, non dovresti comunque usare eccezioni per quel caso. Il secondo problema è una preoccupazione più pratica. Quando si verificano condizioni di eccezione, l'elaborazione aggiuntiva potrebbe non riuscire. La preallocazione dei buffer per i messaggi di errore può essere d'aiuto, ma alla fine è necessario assicurarsi di proteggerli dagli errori durante la segnalazione degli errori. –

+0

da quando lancio (non al runtime) non sono in modalità catastrofica (senza memoria, senza stack, senza heap), quindi il gestore funziona. Se no, allora sono morto comunque. Uso questa tecnica in prodotti aziendali di grandi dimensioni (100 kloc), senza problemi. Posso regolare il livello di registrazione per sopprimere l'overhead – pm100

0

Se sei davvero interessato a ciò che è accaduto per far fallire il tuo programma, potresti trarre vantaggio dall'esame dell'immagine del processo in un debugger post-mortem. La tecnica precisa varia un po 'dal sistema operativo al sistema operativo, ma il treno di base è innanzitutto abilitare il dumping del nucleo e compilare il programma con i simboli di debug. Una volta che il programma si blocca, il sistema operativo copia la sua memoria su disco e puoi esaminare lo stato del programma nel momento in cui si è arrestato.

Problemi correlati