C ++ che cattura tutte le eccezioni


244

Esiste un equivalente c ++ di Java

try {
    ...
}
catch (Throwable t) {
    ...
}

Sto cercando di eseguire il debug del codice Java / jni che chiama le funzioni native di Windows e la macchina virtuale continua a bloccarsi. Il codice nativo appare bene nel test unitario e sembra bloccarsi solo quando viene chiamato tramite jni. Un meccanismo generico di rilevazione delle eccezioni si rivelerebbe estremamente utile.



2
Si noti che la maggior parte degli arresti anomali non sono causati da eccezioni in C ++. Puoi cogliere tutte le eccezioni, ma ciò non impedisce molti arresti anomali.
Mooing Duck

Risposte:


335
try{
    // ...
} catch (...) {
    // ...
}

catturerà tutte le eccezioni C ++, ma dovrebbe essere considerato un design errato. Puoi usare il nuovo meccanismo current_exception di c ++ 11, ma se non hai la possibilità di usare c ++ 11 (sistemi di codice legacy che richiedono una riscrittura), allora non hai un puntatore d'eccezione con nome da usare per ottenere un messaggio o un nome . Potresti voler aggiungere clausole di cattura separate per le varie eccezioni che puoi rilevare e prendere tutto ciò che è in basso per registrare un'eccezione imprevista. Per esempio:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}

68
È buona norma rilevare le eccezioni per riferimento const. Come in: catch (std :: exception const & ex) {/ * ... * /}
coryan

12
@coryan: Perché è buona norma catturare per riferimento const?
Tim MB

19
Evitare copie non necessarie è un vantaggio.
Greg D,

21
-1: il suggerimento che ciò "catturerà tutte le eccezioni in C ++" è fuorviante. Prova a generare un errore di divisione per zero all'interno del blocco try. Vedrai che genererà un'eccezione che non viene rilevata, ma il codice è chiaramente in C ++. Sarebbe più utile affermare che ciò "catturerà tutte le eccezioni C ++" e quindi aggiungere qualche nota di eccezioni strutturate alle note sull'utilità limitata.
omatai,

42
@omatai: risolto, rileva tutte le eccezioni C ++. La divisione per zero è un comportamento indefinito e non genera un'eccezione C ++.
Mooing Duck,

151

Qualcuno dovrebbe aggiungere che non si possono intercettare "arresti anomali" nel codice C ++. Quelli non generano eccezioni, ma fanno tutto ciò che vogliono. Quando vedi un programma che si arresta in modo anomalo a causa di una dereferenza con puntatore null, sta facendo un comportamento indefinito. Non c'è std::null_pointer_exception. Cercare di catturare le eccezioni non ci aiuterà.

Solo per il caso qualcuno sta leggendo questo thread e pensa di poter ottenere la causa degli arresti anomali del programma. Invece dovrebbe essere usato un debugger come gdb.


4
Bene, come sottolinea Shy, è possibile con il compilatore VC. Non è una buona idea, ma è possibile.
Shog9

7
si con SEH. ma non con le normali tecniche c ++ standard :) beh se ti attacchi a Windows puoi quasi fare tutto :)
Johannes Schaub - litb

1
Mmm ... grazie per questo bocconcino. Ho cercato la risposta sul perché le mie eccezioni con puntatore nullo non vengono colte!
Dalin Seivewright,

10
Puoi intercettare segfault con SEH su Windows e signal (2) / sigaction (2) su sistemi POSIX, che coprono la stragrande maggioranza dei sistemi in uso oggi, ma come la gestione delle eccezioni, non è qualcosa che dovrebbe essere usato per il normale controllo del flusso. È più un "fai qualcosa di utile prima di morire".
Adam Rosenfield,

1
@AdamRosenfield fino a quando non ti sarai implementato try { .. } catch(...) { ... }per catturare usando signal / sigaction, non lo definirei "catching" :) Se in un gestore di segnale, è relativamente difficile per il programmatore sapere dove si è verificato l'incidente nel codice (sto parlando sul rilevamento programmatico di questo), rispetto a try / catch.
Johannes Schaub - litb

72

Ecco come è possibile decodificare il tipo di eccezione dall'interno catch(...)se necessario (può essere utile quando si rileva l'ignoto da una libreria di terze parti) con GCC:

#include <iostream>

#include <exception>
#include <typeinfo>
#include <stdexcept>

int main()
{
    try {
        throw ...; // throw something
    }
    catch(...)
    {
        std::exception_ptr p = std::current_exception();
        std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
    }
    return 1;
}

e se puoi permetterti di usare Boost puoi rendere la tua sezione di cattura ancora più semplice (all'esterno) e potenzialmente multipiattaforma

catch (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}

58
try {
   // ...
} catch (...) {
   // ...
}

Si noti che ...all'interno delcatch è una vera ellissi, cioè. tre punti.

Tuttavia, poiché le eccezioni C ++ non sono necessariamente sottoclassi di una Exceptionclasse base , non è possibile vedere effettivamente la variabile di eccezione generata quando si utilizza questo costrutto.


24
In C ++ 11 c'è: provare {std :: string (). At (1); // questo genera uno std :: out_of_range} catch (...) {eptr = std :: current_exception (); // cattura}
Mohammad Alaggan,

2
@bfontaine: Beh sì, ma ho detto che per distinguere l'identificatore catchdal segnaposto di codice esistente in un commento ( // ...) che ovviamente non è la sintassi C ++.
Greg Hewgill,

1
@GregHewgill: sì, era solo il nitpicking tipografico.
bfontaine,

1
@bfontaine: abbastanza giusto. :)
Greg Hewgill il

44

non è possibile (in C ++) catturare tutte le eccezioni in modo portatile. Questo perché alcune eccezioni non sono eccezioni in un contesto C ++. Ciò include cose come la divisione per zero errori e altri. È possibile hackerare e quindi ottenere la possibilità di generare eccezioni quando si verificano questi errori, ma non è facile da fare e certamente non è facile da ottenere in modo portatile.

Se vuoi catturare tutte le eccezioni STL, puoi farlo

try { ... } catch( const std::exception &e) { ... }

Che ti permetterà di usare e.what(), che restituirà a const char*, che può dirti di più sull'eccezione stessa. Questo è il costrutto che ricorda di più il costrutto Java, di cui hai chiesto.

Questo non ti aiuterà se qualcuno è abbastanza stupido da lanciare un'eccezione che non eredita std::exception.


2
perché non è questo in cima?
Ivan Sanz-Carasa,

31

In breve, usare catch(...). Tuttavia, si noti che catch(...)è pensato per essere utilizzato in combinazione con throw;sostanzialmente:

try{
    foo = new Foo;
    bar = new Bar;
}
catch(...)       // will catch all possible errors thrown. 
{ 
    delete foo;
    delete bar;
    throw;       // throw the same error again to be handled somewhere else
}

Questo è il modo corretto di usare catch(...).


6
è meglio utilizzare RAII per la gestione della memoria che gestisce automaticamente queste situazioni di eccezione.
paykoob,

1
@paykoob Come gestisce i casi in cui sei riuscito a creare un nuovo foo ma non è riuscito su una barra. O quando il costruttore della barra tenta di aprire un file ma fallisce e quindi lancia. allora potresti finire con un pazzo
penzolante

2
@MelleSterk In questo caso lo stack non verrebbe ancora ripulito, il che farebbe funzionare Fooil distruttore? Ho pensato che fosse questo il punto centrale di RAII. Tuttavia, se hai bisogno di un puntatore a Foopiuttosto che creare lo Foostack, allora dovresti avvolgere il puntatore in qualcos'altro che è dichiarato nello stack.
reirab del

sì auto foo = std :: make_unique <Foo> (); auto bar = std :: make_unique <Bar> (); // è sicuro per le eccezioni e non presenta perdite, nessuna cattura (...) richiesta
paulm

Questa risposta merita un voto, se non altro per la discussione iniziata :)
Cristik,

21

è possibile farlo scrivendo:

try
{
  //.......
}
catch(...) // <<- catch all
{
  //.......
}

Ma qui c'è un rischio molto evidente: non riesci a trovare il tipo esatto di errore che è stato generato nel tryblocco, quindi usa questo tipo di catchquando sei sicuro che, indipendentemente dal tipo di eccezione, il programma deve persistere nel modo definito nel catchblocco.


31
Spero che tu abbia una sorta di badge per rispondere a una domanda quasi 5 anni dopo che è stata fornita una risposta superiore!


18

Puoi usare

catch(...)

ma è molto pericoloso. Nel suo libro Debugging di Windows , John Robbins racconta una storia di guerra su un bug davvero brutto che è stato mascherato da un comando catch (...). Farai molto meglio a cogliere eccezioni specifiche. Prendi qualunque cosa pensi che il tuo blocco try possa ragionevolmente lanciare, ma lascia che il codice generi un'eccezione più in alto se succede qualcosa di veramente imprevisto.


1
Ho appena preso alcuni utilizzi di questi e ho aggiunto un po 'di registrazione in quella fase. Non fare nulla con un'eccezione è sicuramente chiedere problemi.
jxramos,

15

Consentitemi di menzionarlo qui: Java

try 
{
...
}
catch (Exception e)
{
...
}

potrebbe NON cogliere tutte le eccezioni! In realtà ho avuto questo genere di cose prima, ed è provocatorio; L'eccezione deriva da Throwable. Quindi, letteralmente, per catturare tutto, NON vuoi catturare Eccezioni; vuoi prendere Throwable.

So che suona nitido, ma quando hai passato diversi giorni a cercare di capire da dove provenisse l '"eccezione non rilevata" nel codice che era circondato da un blocco try ... catch (Exception e) ", si attacca con tu.


2
Ovviamente, non dovresti mai catturare oggetti Error - se dovessi prenderli sarebbero Eccezioni. Gli oggetti errore sono cose completamente fatali, come esaurire lo spazio dell'heap, ecc.
SCdF

1
Né eccezioni di runtime che sono il più delle volte GoodProgrammer Eccezioni previste !!!
OscarRyz,

3
Abbiamo avuto un bug davvero grave causato dalla cattura di un OutOfMemoryError a causa di un blocco catch (Throwable) invece di lasciarlo uccidere cose ...
Trejkaz,

1
Ovviamente catch(Exception)potrebbe non intercettare tutte le eccezioni in Java, lo stai mescolando con C # ... Java = catch(Thowable), C # = catch(Exception). Non confonderli.
Chef Faraone

2
@OscarRyz Sembra il CoderMalfunctionError(che in realtà è una vera Errorsottoclasse Java ... anche se non significa come suona.)
Reirab

9

Bene, se vuoi prendere tutte le eccezioni per creare un minidump per esempio ...

Qualcuno ha fatto il lavoro su Windows.

Vedi http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus Nell'articolo, spiega come ha scoperto come catturare tutti i tipi di eccezioni e fornisce il codice che funziona.

Ecco l'elenco che puoi prendere:

 SEH exception
 terminate
 unexpected
 pure virtual method call
 invalid parameter
 new operator fault 
 SIGABR
 SIGFPE
 SIGILL
 SIGINT
 SIGSEGV
 SIGTERM
 Raised exception
C++ typed exception

E l'uso: CCrashHandler ch; ch.SetProcessExceptionHandlers (); // fallo per un thread ch.SetThreadExceptionHandlers (); // per ogni thred


Per impostazione predefinita, questo crea un minidump nella directory corrente (crashdump.dmp)


4

Un meccanismo generico di rilevazione delle eccezioni si rivelerebbe estremamente utile.

Dubbioso. Sai già che il tuo codice è rotto, perché si sta bloccando. Le eccezioni alimentari possono mascherare questo, ma probabilmente si tradurrà in bug ancora più cattivi e sottili.

Quello che vuoi davvero è un debugger ...


13
Non sono d'accordo, ci sono molti casi in applicazioni in tempo reale in cui preferirei prendere un'eccezione sconosciuta, scrivere qualsiasi cosa in un registro / perseguire un corso d'azione generico di errore, piuttosto che lasciare che l'applicazione si blocchi.
f0ster,

3
Sospetto piuttosto che tu stia pensando a casi in cui è possibile perseguire un corso d'azione di errore generico, ignorando convenientemente quelli in cui lo stack viene distrutto o la memoria è esaurita e neanche la gestione generica degli errori avrà successo. Niente di sbagliato nel rilevare errori da cui è possibile recuperare, ma IMHO un catch-all dovrebbe davvero esistere solo come isolato (stack separato, memoria pre-allocata), logica accuratamente scritta chiamata appena prima della conclusione del programma; se non sai qual è il problema, non puoi essere sicuro che possa essere recuperato.
Shog9,

1
Vale a dire installare un gestore di segnale che srotola alcuni log creati durante il runtime per capire dove si è bloccato il programma e, si spera, perché.
Più chiaro

3
  1. È possibile eseguire l'applicazione Java utilizzando JNI da una finestra della console (avviarla da una riga di comando java) per vedere se esiste un rapporto su ciò che potrebbe essere stato rilevato prima che la JVM si arrestasse in modo anomalo. Quando si esegue direttamente come un'applicazione per finestre Java, è possibile che manchino messaggi che potrebbero apparire se si eseguisse invece da una finestra della console.

  2. In secondo luogo, puoi stub dell'implementazione della DLL JNI per mostrare che i metodi nella tua DLL vengono immessi da JNI, stai tornando correttamente, ecc.?

  3. Nel caso in cui il problema riguardi un uso errato di uno dei metodi dell'interfaccia JNI dal codice C ++, hai verificato che alcuni semplici esempi JNI compilano e funzionano con la tua configurazione? Sto pensando in particolare all'utilizzo dei metodi dell'interfaccia JNI per convertire i parametri in formati C ++ nativi e trasformare i risultati delle funzioni in tipi Java. È utile stubare quelli per assicurarsi che le conversioni di dati funzionino e che non si vada in tilt nelle chiamate simili a COM nell'interfaccia JNI.

  4. Ci sono altre cose da controllare, ma è difficile suggerire qualsiasi senza sapere di più su quali siano i tuoi metodi Java nativi e cosa sta cercando di fare l'implementazione JNI di essi. Non è chiaro che la cattura di un'eccezione dal livello di codice C ++ sia correlata al tuo problema. (È possibile utilizzare l'interfaccia JNI per riproporre l'eccezione come Java, ma non è chiaro da cosa fornite che ciò possa aiutare.)


2

Per il vero problema di non riuscire a eseguire correttamente il debug di un programma che utilizza JNI (o il bug non appare quando lo si esegue con un debugger):

In questo caso spesso aiuta ad aggiungere wrapper Java attorno alle chiamate JNI (ovvero tutti i metodi nativi sono privati ​​e i metodi pubblici nella classe li chiamano) che eseguono alcuni controlli di integrità di base (controlla che tutti gli "oggetti" siano liberati e "oggetti" non vengono utilizzati dopo la liberazione) o la sincronizzazione (basta sincronizzare tutti i metodi da una DLL a una singola istanza di oggetto). Lascia che i metodi del wrapper Java registrino l'errore e generino un'eccezione.

Ciò contribuirà spesso a trovare il vero errore (che sorprendentemente si trova principalmente nel codice Java che non obbedisce alla semantica delle funzioni chiamate causando alcune brutte doppie liberazioni o simili) più facilmente rispetto al tentativo di eseguire il debug di un programma Java massicciamente parallelo in un debugger nativo ...

Se conosci la causa, mantieni il codice nei metodi wrapper che lo evita. È meglio che i tuoi metodi wrapper generino eccezioni rispetto al tuo codice JNI in crash la VM ...


1

Bene, questo dipende davvero dall'ambiente del compilatore. gcc non li cattura. Visual Studio e l'ultimo Borland che ho usato hanno fatto.

Quindi la conclusione sugli arresti anomali è che dipende dalla qualità dell'ambiente di sviluppo.

La specifica C ++ afferma che catch (...) deve catturare eventuali eccezioni, ma non in tutti i casi.

Almeno da quello che ho provato.


1

Sii consapevole

try{
// ...
} catch (...) {
// ...
}

rileva solo le eccezioni a livello di lingua, altre eccezioni / errori di basso livello come Access Violatione Segmentation Faultnon verranno rilevate.


Cose come Segmentation Fault non sono in realtà eccezioni, sono segnali; quindi, non puoi prenderli come eccezioni tipiche. Tuttavia, ci sono alcune soluzioni alternative come questa .
MAChitgarha,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.