C ++ fängt alle Ausnahmen ab

242

Gibt es ein C ++ - Äquivalent zu Java?

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

Ich versuche, Java / jni-Code zu debuggen, der native Windows-Funktionen aufruft, und die virtuelle Maschine stürzt immer wieder ab. Der native Code scheint beim Unit-Test in Ordnung zu sein und stürzt nur ab, wenn er über jni aufgerufen wird. Ein generischer Ausnahmefangmechanismus würde sich als äußerst nützlich erweisen.

Obediah Stane
quelle
2
Beachten Sie, dass die meisten Abstürze nicht durch Ausnahmen in C ++ verursacht werden. Sie können alle Ausnahmen abfangen, aber das verhindert nicht viele Abstürze.
Mooing Duck

Antworten:

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

fängt alle C ++ - Ausnahmen ab, sollte aber als schlechtes Design angesehen werden. Sie können den neuen current_exception-Mechanismus von c ++ 11 verwenden. Wenn Sie jedoch nicht in der Lage sind, c ++ 11 (ältere Codesysteme, die neu geschrieben werden müssen) zu verwenden, müssen Sie keinen benannten Ausnahmezeiger zum Abrufen einer Nachricht oder eines Namens verwenden . Möglicherweise möchten Sie separate catch-Klauseln für die verschiedenen Ausnahmen hinzufügen, die Sie abfangen können, und nur alles unten abfangen, um eine unerwartete Ausnahme aufzuzeichnen. Z.B:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}
Greg D.
quelle
68
Es ist eine gute Praxis, Ausnahmen durch konstante Referenz abzufangen. Wie in: catch (std :: exception const & ex) {/ * ... * /}
Coryan
12
@coryan: Warum ist es eine gute Praxis, durch konstante Referenz zu fangen?
Tim MB
19
Das Vermeiden unnötiger Kopien ist ein Vorteil.
Greg D
21
-1: Der Vorschlag, dass dies "alle Ausnahmen in C ++ abfängt", ist irreführend. Versuchen Sie, innerhalb des try-Blocks einen Fehler durch Division durch Null zu generieren. Sie werden sehen, dass eine Ausnahme generiert wird, die nicht abgefangen wird, der Code sich jedoch eindeutig in C ++ befindet. Es wäre hilfreicher zu behaupten, dass dies "alle C ++ - Ausnahmen abfängt" und dann strukturierte Ausnahmen in den Hinweisen zur eingeschränkten Nützlichkeit erwähnt.
Omatai
42
@omatai: Behoben, es werden alle C ++ - Ausnahmen abgefangen. Die Division durch Null ist ein undefiniertes Verhalten und generiert keine C ++ - Ausnahme.
Mooing Duck
150

Jemand sollte hinzufügen, dass man im C ++ - Code keine "Abstürze" abfangen kann. Diese werfen keine Ausnahmen, sondern machen alles, was sie wollen. Wenn Sie sehen, dass ein Programm aufgrund einer Nullzeiger-Dereferenzierung abstürzt, führt es ein undefiniertes Verhalten aus. Es gibt keine std::null_pointer_exception. Der Versuch, Ausnahmen zu fangen, hilft dort nicht weiter.

Nur für den Fall, dass jemand diesen Thread liest und glaubt, die Ursache für die Programmabstürze zu bekommen. Stattdessen sollte ein Debugger wie gdb verwendet werden.

Johannes Schaub - litb
quelle
4
Nun, wie Shy betont, ist dies mit dem VC-Compiler möglich. Es ist keine gute Idee, aber es ist möglich.
Shog9
7
ja mit SEH. aber nicht mit vernünftigen Standard-C ++ - Techniken :)
Nun,
1
Mmm ... danke für diesen Leckerbissen. Ich habe nach der Antwort gesucht, warum meine Nullzeiger-Ausnahmen nicht abgefangen werden!
Dalin Seivewright
10
Sie können Segfaults mit SEH unter Windows und signal (2) / sigaction (2) auf POSIX-Systemen abfangen, was die überwiegende Mehrheit der heute verwendeten Systeme abdeckt, aber wie die Ausnahmebehandlung sollte es nicht für die normale Flusskontrolle verwendet werden. Es ist eher ein "etwas Nützliches tun, bevor man stirbt".
Adam Rosenfield
1
@AdamRosenfield, bis Sie try { .. } catch(...) { ... }für das Fangen mit Signal / Sigaction implementiert haben, würde ich es nicht "Fangen" nennen :) Wenn in einem Signalhandler, ist es für den Programmierer relativ schwer zu wissen, wo im Code der Absturz passiert ist (ich spreche über das programmgesteuerte Erkennen) im Vergleich zu try / catch.
Johannes Schaub - litb
72

Auf diese Weise können Sie den Ausnahmetyp von innen catch(...)mit GCC zurückentwickeln, falls dies erforderlich ist (kann nützlich sein, wenn Unbekanntes aus einer Drittanbieter-Bibliothek abgefangen wird):

#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;
}

und wenn Sie es sich leisten können, Boost zu verwenden , können Sie Ihren Fangbereich (außen) noch einfacher und möglicherweise plattformübergreifend gestalten

catch (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}
Bobah
quelle
58
try {
   // ...
} catch (...) {
   // ...
}

Beachten Sie, dass das ...Innere catcheine echte Ellipse ist, dh. drei Punkte.

Da C ++ - Ausnahmen jedoch nicht unbedingt Unterklassen einer Basisklasse Exceptionsind, kann die bei Verwendung dieses Konstrukts ausgelöste Ausnahmevariable nicht angezeigt werden.

Greg Hewgill
quelle
24
In C ++ 11 gibt es: try {std :: string (). At (1); // dies erzeugt einen std :: out_of_range} catch (...) {eptr = std :: current_exception (); // Capture}
Mohammad Alaggan
2
@bfontaine: Nun ja, aber ich sagte das, um den Bezeichner catchvom vorhandenen Code-Platzhalter in einem Kommentar ( // ...) zu unterscheiden, der offensichtlich keine C ++ - Syntax ist.
Greg Hewgill
1
@ GregHewgill: Ja, es war nur typografisches Nitpicking.
Bfontaine
1
@bfontaine: Fair genug. :)
Greg Hewgill
43

Es ist (in C ++) nicht möglich, alle Ausnahmen portabel abzufangen. Dies liegt daran, dass einige Ausnahmen keine Ausnahmen in einem C ++ - Kontext sind. Dies schließt Dinge wie Division durch Null Fehler und andere ein. Es ist möglich, sich zu hacken und so Ausnahmen auszulösen, wenn diese Fehler auftreten, aber es ist nicht einfach und sicherlich nicht einfach, auf tragbare Weise richtig zu machen.

Wenn Sie alle STL-Ausnahmen abfangen möchten, können Sie dies tun

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

Damit können Sie verwenden e.what(), was a zurückgibt const char*, das Ihnen mehr über die Ausnahme selbst erzählen kann. Dies ist das Konstrukt, das dem Java-Konstrukt, nach dem Sie gefragt haben, am ähnlichsten ist.

Dies hilft Ihnen nicht, wenn jemand dumm genug ist, eine Ausnahme auszulösen, von der nicht geerbt wird std::exception.

Klarer
quelle
1
Warum ist das nicht oben?
Ivan Sanz-Carasa
30

Kurz gesagt, verwenden catch(...). Beachten Sie jedoch, dass catch(...)dies in Verbindung mit throw;grundsätzlich verwendet werden soll:

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
}

Dies ist die richtige Verwendung catch(...).

Mellester
quelle
6
Es ist besser, RAII für die Speicherverwaltung zu verwenden, die diese Ausnahmesituationen automatisch behandelt.
Paykoob
1
@paykoob Wie geht das mit Fällen um, in denen Sie einen neuen Foo erstellt haben, der jedoch in einer Leiste fehlgeschlagen ist? Oder wenn der Konstruktor von bar versucht, eine Datei zu öffnen, aber fehlschlägt und daher wirft. dann könnten Sie mit einem dangeling foo
enden
2
@MelleSterk Würde der Stapel in diesem Fall nicht immer noch bereinigt werden, was den FooDestruktor ausführen würde ? Ich dachte, das wäre der springende Punkt bei RAII. Wenn Sie jedoch einen Zeiger auf a benötigen, Fooanstatt nur den Fooauf dem Stapel zu erstellen , müssen Sie den Zeiger in etwas anderes einschließen, das auf dem Stapel deklariert ist.
Reirab
ja auto foo = std :: make_unique <Foo> (); auto bar = std :: make_unique <Bar> (); // ist
ausnahmesicher und leckt
Diese Antwort verdient eine Abstimmung, wenn auch nur für die Diskussion, die sie begonnen hat :)
Cristik
21

Dies ist schriftlich möglich:

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

Hier besteht jedoch ein nicht wahrnehmbares Risiko: Sie können den genauen Fehlertyp, der in den tryBlock geworfen wurde, nicht finden. Verwenden Sie diese Art von Fehler, catchwenn Sie sicher sind, dass das Programm unabhängig von der Art der Ausnahme bestehen bleiben muss auf die im catchBlock definierte Weise .

Unendlich
quelle
31
Ich hoffe, Sie haben fast 5 Jahre nach der Beantwortung einer überlegenen Antwort eine Art Abzeichen für die Beantwortung einer Frage erhalten!
18

Sie können verwenden

catch(...)

aber das ist sehr gefährlich. In seinem Buch Debugging Windows erzählt John Robbins eine Kriegsgeschichte über einen wirklich bösen Fehler, der durch einen catch-Befehl (...) maskiert wurde. Sie sind viel besser dran, bestimmte Ausnahmen zu erwischen. Fangen Sie alles auf, was Sie denken, dass Ihr try-Block vernünftigerweise auslösen könnte, aber lassen Sie den Code eine Ausnahme höher auslösen, wenn etwas wirklich Unerwartetes passiert.

John D. Cook
quelle
1
Ich habe gerade einige Verwendungen davon gefangen und in diesem Stadium einige Protokollierungen vorgenommen. Mit einer Ausnahme nichts zu tun, ist definitiv ein Problem.
Jxramos
15

Lassen Sie mich dies hier nur erwähnen: das Java

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

darf NICHT alle Ausnahmen abfangen! Ich habe so etwas schon einmal erlebt und es ist wahnsinnig; Ausnahme ergibt sich von Throwable. Um also buchstäblich alles zu fangen, möchten Sie KEINE Ausnahmen fangen. du willst Throwable fangen.

Ich weiß, es klingt pingelig, aber wenn Sie mehrere Tage damit verbracht haben, herauszufinden, woher die "nicht erfasste Ausnahme" in Code stammt, der von einem try ... catch (Ausnahme e) "- Block umgeben war, bleibt es dabei Sie.

Paul Sonier
quelle
2
Natürlich sollten Sie niemals Fehlerobjekte abfangen - wenn Sie sie abfangen sollten, wären dies Ausnahmen. Fehlerobjekte sind völlig schwerwiegende Dinge, wie z. B. der Speicherplatzmangel usw.
SCdF
1
Weder Laufzeitausnahmen, die meistens GoodProgrammerExpected Ausnahmen sind !!!
OscarRyz
3
Wir hatten einen wirklich schwerwiegenden Fehler, der durch das Abfangen eines OutOfMemoryError aufgrund eines Catch-Blocks (Throwable) verursacht wurde, anstatt ihn Dinge töten zu lassen ...
Trejkaz
1
Natürlich werden catch(Exception)möglicherweise nicht alle Ausnahmen in Java abgefangen, Sie verwechseln es mit C # ... Java = catch(Thowable), C # = catch(Exception). Verwechsle sie nicht.
Chef Pharao
2
@OscarRyz Das klingt wie die CoderMalfunctionError(was eigentlich eine echte Java- ErrorUnterklasse ist ... obwohl es nicht bedeutet, wie es sich anhört.)
reirab
9

Nun, wenn Sie alle Ausnahmen abfangen möchten, um beispielsweise einen Minidump zu erstellen ...

Jemand hat die Arbeit unter Windows gemacht.

Siehe http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus In diesem Artikel erklärt er, wie er herausgefunden hat, wie alle Arten von Ausnahmen abgefangen werden können, und stellt Code bereit, der funktioniert.

Hier ist die Liste, die Sie fangen können:

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

Und die Verwendung: CCrashHandler ch; ch.SetProcessExceptionHandlers (); // mache das für einen Thread ch.SetThreadExceptionHandlers (); // für jeden Thred


Standardmäßig wird ein Minidump im aktuellen Verzeichnis erstellt (crashdump.dmp).

Nachbeben
quelle
4

Ein generischer Ausnahmefangmechanismus würde sich als äußerst nützlich erweisen.

Zweifelhaft. Sie wissen bereits, dass Ihr Code defekt ist, weil er abstürzt. Essensausnahmen können dies maskieren, aber das wird wahrscheinlich nur zu noch schlimmeren, subtileren Fehlern führen.

Was Sie wirklich wollen, ist ein Debugger ...

Shog9
quelle
13
Ich bin anderer Meinung, es gibt viele Fälle in Echtzeitanwendungen, in denen ich lieber eine unbekannte Ausnahme abfangen, etwas in ein Protokoll schreiben / eine allgemeine Fehleraktion verfolgen möchte , anstatt die Anwendung abstürzen zu lassen.
f0ster
3
Ich vermute , dass Sie eher denken von Fällen , in denen Sie können einige allgemeine Fehlervorgehensweise verfolgen, bequem diejenigen ignorieren , wo der Stapel im Papierkorb oder der Speicher erschöpft ist und generische Fehlerbehandlung wird auch nicht erfolgreich sein würde. Es ist nichts Falsches daran, Fehler abzufangen, die Sie beheben können , aber meiner Meinung nach sollte ein Allheilmittel nur als isolierte (separater Stapel, vorab zugewiesener Speicher), sorgfältig geschriebene Logik existieren, die unmittelbar vor dem Beenden des Programms aufgerufen wird. Wenn Sie das Problem nicht kennen, können Sie nicht sicher sein, dass es behoben werden kann.
Shog9
1
Das heißt, Sie installieren einen Signal-Handler, der ein Protokoll abwickelt, das Sie zur Laufzeit erstellt haben, um herauszufinden, wo das Programm abgestürzt ist und hoffentlich warum.
Klarer
3
  1. Können Sie Ihre JNI-verwendende Java-Anwendung über ein Konsolenfenster ausführen (über eine Java-Befehlszeile starten), um festzustellen, ob ein Bericht darüber vorliegt, was möglicherweise vor dem Absturz der JVM erkannt wurde? Wenn Sie direkt als Java-Fensteranwendung ausgeführt werden, fehlen möglicherweise Meldungen, die angezeigt werden, wenn Sie stattdessen über ein Konsolenfenster ausgeführt werden.

  2. Zweitens können Sie Ihre JNI-DLL-Implementierung stubben, um zu zeigen, dass Methoden in Ihrer DLL von JNI eingegeben werden, Sie ordnungsgemäß zurückkehren usw.?

  3. Haben Sie für den Fall, dass das Problem in einer falschen Verwendung einer der JNI-Schnittstellenmethoden aus dem C ++ - Code liegt, überprüft, ob einige einfache JNI-Beispiele kompiliert wurden und mit Ihrem Setup funktionieren? Ich denke insbesondere an die Verwendung der JNI-Schnittstellenmethoden zum Konvertieren von Parametern in native C ++ - Formate und zum Umwandeln von Funktionsergebnissen in Java-Typen. Es ist nützlich, diese zu stubben, um sicherzustellen, dass die Datenkonvertierungen funktionieren und Sie bei den COM-ähnlichen Aufrufen der JNI-Schnittstelle nicht durcheinander geraten.

  4. Es gibt noch andere Dinge zu überprüfen, aber es ist schwierig, irgendwelche vorzuschlagen, ohne mehr darüber zu wissen, was Ihre nativen Java-Methoden sind und was die JNI-Implementierung von ihnen versucht. Es ist nicht klar, dass das Abfangen einer Ausnahme von der C ++ - Codeebene mit Ihrem Problem zusammenhängt. (Sie können die JNI-Schnittstelle verwenden, um die Ausnahme als Java-Schnittstelle erneut auszulösen. Aus Ihren Angaben geht jedoch nicht hervor, dass dies hilfreich sein wird.)

orcmid
quelle
2

Für das eigentliche Problem, dass ein Programm, das JNI verwendet, nicht ordnungsgemäß debuggt werden kann (oder der Fehler nicht auftritt, wenn es unter einem Debugger ausgeführt wird):

In diesem Fall ist es oft hilfreich, Java-Wrapper um Ihre JNI-Aufrufe hinzuzufügen (dh alle nativen Methoden sind privat und Ihre öffentlichen Methoden in der Klasse rufen sie auf), die eine grundlegende Überprüfung der Integrität durchführen (überprüfen Sie, ob alle "Objekte" freigegeben sind und "Objekte"). werden nach dem Freigeben nicht verwendet) oder synchronisiert (synchronisieren Sie einfach alle Methoden von einer DLL zu einer einzelnen Objektinstanz). Lassen Sie die Java-Wrapper-Methoden den Fehler protokollieren und eine Ausnahme auslösen.

Dies hilft oft dabei, den eigentlichen Fehler (der überraschenderweise hauptsächlich im Java-Code enthalten ist, der nicht der Semantik der aufgerufenen Funktionen entspricht und einige unangenehme Double-Frees oder ähnliches verursacht) leichter zu finden als den Versuch, ein massiv paralleles Java-Programm in einem zu debuggen nativer Debugger ...

Wenn Sie die Ursache kennen, behalten Sie den Code in Ihren Wrapper-Methoden bei, um dies zu vermeiden. Lassen Sie Ihre Wrapper-Methoden lieber Ausnahmen auslösen, als dass Ihr JNI-Code die VM zum Absturz bringt ...

mihi
quelle
1

Nun, das hängt wirklich von der Compiler-Umgebung ab. gcc fängt diese nicht. Visual Studio und das letzte Borland, das ich verwendet habe, haben es getan.

Die Schlussfolgerung zu Abstürzen lautet also, dass dies von der Qualität Ihrer Entwicklungsumgebung abhängt.

Die C ++ - Spezifikation besagt, dass catch (...) Ausnahmen abfangen muss, dies jedoch nicht in allen Fällen.

Zumindest von dem, was ich versucht habe.

Jan.
quelle
0

Sei vorsichtig

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

fängt nur Ausnahmen auf Sprachebene ab, andere Ausnahmen / Fehler auf niedriger Ebene mögen Access Violationund werden Segmentation Faultnicht abgefangen.

Muaz
quelle
Dinge wie Segmentierungsfehler sind eigentlich keine Ausnahmen, sie sind Signale; Daher können Sie sie nicht wie typische Ausnahmen abfangen. Es gibt jedoch einige Problemumgehungen wie diese .
MAChitgarha