C ++ capturando todas las excepciones

244

¿Hay un equivalente en C ++ de Java

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

Estoy tratando de depurar el código Java / jni que llama a las funciones nativas de Windows y la máquina virtual sigue fallando. El código nativo aparece bien en las pruebas unitarias y solo parece bloquearse cuando se llama a través de jni. Un mecanismo genérico de captura de excepciones resultaría extremadamente útil.

Obediah Stane
fuente
2
Tenga en cuenta que la mayoría de los bloqueos no son causados ​​por excepciones en C ++. Puede detectar todas las excepciones, pero eso no evitará muchos bloqueos.
Mooing Duck

Respuestas:

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

detectará todas las excepciones de C ++, pero debe considerarse un mal diseño. Puede usar el nuevo mecanismo current_exception de c ++ 11, pero si no tiene la capacidad de usar c ++ 11 (sistemas de código heredados que requieren una reescritura), entonces no tiene un puntero de excepción con nombre para usar para obtener un mensaje o nombre . Es posible que desee agregar cláusulas de captura separadas para las diversas excepciones que puede capturar, y solo capturar todo en la parte inferior para registrar una excepción inesperada. P.ej:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}
Greg D
fuente
68
Es una buena práctica detectar excepciones por referencia constante. Como en: catch (std :: except const & ex) {/ * ... * /}
coryan
12
@coryan: ¿Por qué es una buena práctica atrapar por referencia constante?
Tim MB
19
Evitar copias innecesarias es un beneficio.
Greg D
21
-1: la sugerencia de que esto "capturará todas las excepciones en C ++" es engañosa. Intenta generar un error de división por cero dentro del bloque try. Verá que generará una excepción que no se detecta, pero el código está claramente en C ++. Sería más útil afirmar que esto "detectará todas las excepciones de C ++" y luego agregar alguna mención de excepciones estructuradas a las notas sobre la utilidad limitada.
omatai
42
@omatai: Corregido, capturará todas las excepciones de C ++. La división por cero es un comportamiento indefinido y no genera una excepción de C ++.
Mooing Duck
151

Alguien debería agregar que no se pueden detectar "bloqueos" en el código C ++. Esos no arrojan excepciones, pero hacen lo que quieran. Cuando ve que un programa se bloquea debido a una desreferencia de puntero nulo, está haciendo un comportamiento indefinido. No haystd::null_pointer_exception . Intentar atrapar excepciones no ayudará allí.

Solo por el caso, alguien está leyendo este hilo y piensa que puede obtener la causa del bloqueo del programa. En su lugar, se debe usar un depurador como gdb.

Johannes Schaub - litb
fuente
44
Bueno, como señala Shy, es posible con el compilador de VC. No es una buena idea, pero es posible.
Shog9
77
Sí, con SEH. pero no con técnicas c ++ estándar sanas :) bueno, si te apegas a Windows, casi puedes hacer todo :)
Johannes Schaub - litb
1
Mmm ... gracias por este dato. ¡He estado buscando la respuesta de por qué no se capturan mis excepciones de puntero nulo!
Dalin Seivewright
10
Puede detectar segfaults con SEH en Windows y signal (2) / sigaction (2) en los sistemas POSIX, que cubren la gran mayoría de los sistemas en uso hoy en día, pero como el manejo de excepciones, no es algo que deba usarse para el control de flujo normal. Es más un "hacer algo útil antes de morir".
Adam Rosenfield
1
@AdamRosenfield hasta que haya implementado la try { .. } catch(...) { ... }captura usando la señal / sigaction, no lo llamaría "captura" :) Si en un controlador de señal, es relativamente difícil para el programador saber en qué parte del código ocurrió el bloqueo (estoy hablando sobre detectarlo programáticamente), en comparación con try / catch.
Johannes Schaub - litb
72

Así es como puede realizar ingeniería inversa del tipo de excepción desde el interior catch(...)si lo necesita (puede ser útil al capturar datos desconocidos de una biblioteca de terceros) 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;
}

y si puede permitirse el lujo de usar Boost , puede hacer que su sección de captura sea aún más simple (en el exterior) y potencialmente multiplataforma

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

Tenga en cuenta que el ...interior del catches una elipsis real, es decir. tres puntos.

Sin embargo, debido a que las excepciones de C ++ no son necesariamente subclases de una Exceptionclase base , no hay ninguna forma de ver realmente la variable de excepción que se genera al usar esta construcción.

Greg Hewgill
fuente
24
En C ++ 11 hay: try {std :: string (). At (1); // esto genera un std :: out_of_range} catch (...) {eptr = std :: current_exception (); // captura}
Mohammad Alaggan el
2
@bfontaine: Bueno, sí, pero dije eso para distinguir el catchespecificador del marcador de posición de código existente en un comentario ( // ...) que obviamente no es la sintaxis de C ++.
Greg Hewgill
1
@GregHewgill: sí, solo era una trampa tipográfica.
bfontaine
1
@bfontaine: bastante justo. :)
Greg Hewgill
44

no es posible (en C ++) capturar todas las excepciones de manera portátil. Esto se debe a que algunas excepciones no son excepciones en un contexto de C ++. Esto incluye cosas como la división por cero errores y otros. Es posible hackear y, por lo tanto, obtener la capacidad de lanzar excepciones cuando ocurren estos errores, pero no es fácil de hacer y ciertamente no es fácil hacerlo de manera portátil.

Si desea capturar todas las excepciones STL, puede hacerlo

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

Lo que le permitirá usar e.what(), lo que devolverá un const char*, que puede darle más información sobre la excepción en sí. Esta es la construcción que se asemeja a la construcción de Java, que más preguntaste.

Esto no te ayudará si alguien es lo suficientemente estúpido como para lanzar una excepción que no hereda std::exception.

Más claro
fuente
2
¿Por qué no está esto en la parte superior?
Ivan Sanz-Carasa
31

En resumen, uso catch(...). Sin embargo, tenga en cuenta que catch(...)está destinado a ser utilizado conjuntamente con throw;básicamente:

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
}

Esta es la forma correcta de usar catch(...).

Mellester
fuente
66
es mejor usar RAII para la administración de memoria que maneja automáticamente estas situaciones de excepción.
paykoob
1
@paykoob ¿Cómo maneja eso los casos en los que lograste crear un nuevo foo pero fallaron en una barra? O cuando el constructor de la barra intenta abrir un archivo pero falla y por lo tanto tira. entonces podrías terminar con un foo
peligroso
2
@MelleSterk ¿No se limpiaría aún la pila en ese caso, que ejecutaría Fooel destructor? Pensé que ese era el objetivo de RAII. Sin embargo, si necesita un puntero a un en Foolugar de simplemente crearlo Fooen la pila, entonces necesitará envolver el puntero en otra cosa que se declare en la pila.
reirab
sí auto foo = std :: make_unique <Foo> (); barra automática = std :: make_unique <Bar> (); // es una excepción segura y no tendrá fugas, no se requiere captura (...)
Paul
Esta respuesta merece un voto aunque solo sea por la discusión que comenzó :)
Cristik
21

es posible hacer esto escribiendo:

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

Pero aquí hay un riesgo muy poco notable: no puede encontrar el tipo exacto de error que se ha arrojado en el trybloque, así que use este tipo de catchcuando esté seguro de que no importa cuál sea el tipo de excepción, el programa debe persistir en la forma definida en el catchbloque.

Infinito
fuente
31
¡Espero que haya recibido algún tipo de insignia por responder una pregunta casi 5 años después de que se proporcionó una respuesta superior!
18

Puedes usar

catch(...)

Pero eso es muy peligroso. En su libro Debugging Windows , John Robbins cuenta una historia de guerra sobre un error realmente desagradable que fue enmascarado por un comando catch (...). Es mucho mejor capturar excepciones específicas. Captura lo que creas que tu bloque de prueba podría lanzar razonablemente, pero deja que el código arroje una excepción más arriba si sucede algo realmente inesperado.

John D. Cook
fuente
1
Acabo de captar algunos usos de estos y me metí en algunos registros en esa etapa. No hacer nada con excepción definitivamente es pedir problemas.
jxramos
15

Permítanme mencionar esto aquí: el Java

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

¡NO puede atrapar todas las excepciones! De hecho, he tenido este tipo de cosas antes, y es increíblemente provocativo; La excepción deriva de Throwable. Entonces, literalmente, para atrapar todo, NO desea atrapar excepciones; quieres atrapar a Throwable.

Sé que suena quisquilloso, pero cuando pasaste varios días tratando de descubrir de dónde vino la "excepción no descubierta" en el código que estaba rodeado por un bloque try ... catch (Exception e) ", se queda con tú.

Paul Sonier
fuente
2
Por supuesto, nunca debe atrapar objetos de Error; si se supone que los atrape, serían Excepciones. Los objetos de error son cosas completamente fatales, como quedarse sin espacio de almacenamiento dinámico, etc.
SCdF
1
¡Ni excepciones de tiempo de ejecución, que son la mayoría de las veces GoodProgrammer, excepciones esperadas!
OscarRyz
3
Tuvimos un error muy serio causado por la captura de un OutOfMemoryError debido a un bloqueo de captura (Throwable) en lugar de dejar que matara cosas ...
Trejkaz
1
Por supuesto, es catch(Exception)posible que no capture todas las excepciones en Java, lo está mezclando con C # ... Java = catch(Thowable), C # = catch(Exception). No los confundas.
Chef Faraón el
2
@OscarRyz Eso suena como el CoderMalfunctionError(que en realidad es una verdadera Errorsubclase de Java ... aunque no significa cómo suena)
Reirab
9

Bueno, si desea capturar todas las excepciones para crear un minivolcado, por ejemplo ...

Alguien hizo el trabajo en Windows.

Ver http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus En el artículo, explica cómo descubrió cómo detectar todo tipo de excepciones y proporciona un código que funciona.

Aquí está la lista que puedes atrapar:

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

Y el uso: CCrashHandler ch; ch.SetProcessExceptionHandlers (); // hacer esto para un hilo ch.SetThreadExceptionHandlers (); // por cada thred


Por defecto, esto crea un minivolcado en el directorio actual (crashdump.dmp)

Réplica
fuente
4

Un mecanismo genérico de captura de excepciones resultaría extremadamente útil.

Dudoso. Ya sabes que tu código está roto porque está fallando. Comer excepciones puede enmascarar esto, pero eso probablemente solo dará como resultado errores aún más desagradables y sutiles.

Lo que realmente quieres es un depurador ...

Shog9
fuente
13
No estoy de acuerdo, hay muchos casos en aplicaciones en tiempo real en los que prefiero detectar una excepción desconocida, escribir cualquier cosa en un registro / seguir algún curso de acción de error genérico, en lugar de dejar que la aplicación se bloquee.
f0ster
3
Sospecho que está pensando en casos en los que puede seguir un curso de acción de error genérico, ignorando convenientemente aquellos en los que la pila está destruida o la memoria está agotada y el manejo de errores genéricos tampoco tendrá éxito. No hay nada malo con los errores de captura de los que puede recuperarse, pero en mi humilde opinión, un conjunto debería realmente existir solo como aislado (pila separada, memoria preasignada), lógica cuidadosamente escrita llamada justo antes de la finalización del programa; Si no sabe cuál es el problema, no puede estar seguro de que pueda recuperarse.
Shog9
1
Es decir, instale un controlador de señal que desenrolle algunos registros que cree durante el tiempo de ejecución para averiguar dónde se bloqueó el programa y, con suerte, por qué.
Más claro
3
  1. ¿Puede ejecutar su aplicación Java que utiliza JNI desde una ventana de la consola? Cuando se ejecuta directamente como una aplicación de ventana Java, es posible que le falten mensajes que aparecerían si se ejecutara desde una ventana de consola.

  2. En segundo lugar, ¿puede bloquear la implementación de su DLL JNI para mostrar que los métodos en su DLL se ingresan desde JNI, está regresando correctamente, etc.?

  3. En caso de que el problema sea con el uso incorrecto de uno de los métodos de la interfaz JNI del código C ++, ¿ha verificado que algunos ejemplos simples de JNI compilan y funcionan con su configuración? Estoy pensando en particular en utilizar los métodos de la interfaz JNI para convertir parámetros a formatos nativos de C ++ y convertir los resultados de las funciones en tipos Java. Es útil agruparlos para asegurarse de que las conversiones de datos funcionen y que no se vuelva loco en las llamadas tipo COM a la interfaz JNI.

  4. Hay otras cosas que verificar, pero es difícil sugerir alguna sin saber más acerca de cuáles son sus métodos nativos de Java y qué está tratando de hacer la implementación de JNI de ellos. No está claro que detectar una excepción del nivel de código C ++ esté relacionado con su problema. (Puede usar la interfaz JNI para volver a lanzar la excepción como Java, pero de lo que proporciona no está claro si esto va a ayudar).

orcmid
fuente
2

Para el verdadero problema de no poder depurar correctamente un programa que usa JNI (o el error no aparece cuando se ejecuta bajo un depurador):

En este caso, a menudo es útil agregar envoltorios Java alrededor de sus llamadas JNI (es decir, todos los métodos nativos son privados y sus métodos públicos en la clase los llaman) que hacen una comprobación básica de sanidad (verifique que todos los "objetos" estén liberados y "objetos" no se usan después de la liberación) o la sincronización (solo sincronice todos los métodos de una DLL a una sola instancia de objeto). Deje que los métodos java wrapper registren el error y generen una excepción.

A menudo, esto ayudará a encontrar el error real (que sorprendentemente se encuentra principalmente en el código Java que no obedece a la semántica de las funciones llamadas que causan algunas liberaciones dobles desagradables o similares) más fácilmente que tratar de depurar un programa Java masivamente paralelo en un depurador nativo ...

Si conoce la causa, mantenga el código en sus métodos de envoltura que lo evite. Es mejor que sus métodos de envoltura arrojen excepciones que su código JNI bloquee la VM ...

mihi
fuente
1

Bueno, esto realmente depende del entorno del compilador. gcc no los atrapa. Visual Studio y el último Borland que usé lo hicieron.

Entonces, la conclusión sobre los bloqueos es que depende de la calidad de su entorno de desarrollo.

La especificación de C ++ dice que catch (...) debe detectar cualquier excepción, pero no en todos los casos.

Al menos por lo que intenté.

ene
fuente
1

Se consciente

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

captura solo excepciones de nivel de idioma, otras excepciones / errores de bajo nivel como Access Violationy Segmentation Faultno se detectarán.

muaz
fuente
Cosas como la Falla de segmentación no son realmente excepciones, son señales; por lo tanto, no puede atraparlos como excepciones típicas. Sin embargo, hay algunas soluciones como esta .
MAChitgarha