2010-09-22 45 views
6

¿Ofrece C++ una forma de 'mostrar' algo visual si ocurre una excepción no controlada?C++ excepciones no controladas

Lo que quiero hacer es algo así como assert(unhandled exception.msg()) si realmente ocurre (como en el siguiente ejemplo):

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

int main() { 
foo(); 
} 

me esperaba este tipo de código de no terminar inmediatamente (debido a excepción fue controlada), más bien muestra un mensaje de aserción personalizado (Message! en realidad).

¿Es esto posible?

+3

¿Por qué no pones simplemente un bloque try/catch en 'main'? – GManNickG

+1

@GMan: Un constructor o destructor global también puede lanzar fuera de main. Para el caso de destructor, el desenrollado podría no llegar a main. – Potatoswatter

+0

@Potatoswatter: Sin embargo, estaba más preocupado con su ejemplo particular. – GManNickG

Respuesta

9

No hay manera especificada por el estándar de hecho para mostrar el mensaje de la excepción no detectada. Sin embargo, en muchas plataformas, es posible de todos modos. En Windows, puede usar SetUnhandledExceptionFilter y extraer la información de excepción de C++.Con g ++ (versiones adecuadas de todos modos), el manejador de terminar puede acceder a la excepción no detectada con un código como:

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

y de hecho g ++ 's por defecto por terminado controlador hace algo similar a esto. Puede configurar el manejador de terminación con set_terminate.

En resumen, no, no hay una forma genérica de C++, pero hay formas que dependen de su plataforma.

+0

Me gusta esto. Es posible que el estándar no lo exija específicamente, pero dice que una excepción solo se desactiva cuando sale el bloqueo que lo atrapa. Como eso no puede suceder antes de terminar, esto debería ser portátil o, al menos, compatible con todo eventualmente. El mismo truco debería funcionar con 'inesperado ', y eso permitiría un diagnóstico de throw-on-unwind ilegal. – Potatoswatter

+0

Además, puede detectar excepciones lanzadas desde constructores y destructores globales, que un bloque catch en 'main' no puede. – Potatoswatter

+0

@Potatoswatter He visto al menos una configuración donde este truco realmente no funciona, no pretendo ser lo suficientemente conocedor sobre el estándar (y las otras cosas que estábamos haciendo con el manejo de excepciones) para decir de una manera o la otro, pero sé que funciona en Linux con un gcc moderno. –

1

Si estoy leyendo su pregunta correctamente, está preguntando si puede sobrecargar throw (cambiando su comportamiento predeterminado) por lo que hace algo definido por el usuario. No, no puedes.

edición: ya que eres insistente :), aquí es una mala 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; 
} 

Una vez más, esta es una muy mala idea, y es obviamente dependiente de la plataforma, pero funciona.

+0

No 'throw' en realidad. Quiero cambiar el comportamiento predeterminado cuando se produce una situación de excepción no controlada. –

+0

Así que, básicamente, cuando "tira" una excepción no controlada :-P –

6

Microsoft Visual C++ le permite enganchar excepciones de C++ no controladas like this. Esto es standard STL behaviour.

Establece un controlador a través de una llamada al set_terminate. Se recomienda que su controlador no funcione mucho y luego finalice el programa, pero no veo por qué no podría señalar algo a través de una afirmación, aunque no tiene acceso a la excepción que causó el problema.

+1

El manejador de terminación no debe afirmar. Sin embargo, podría 'printf' (o mejor, simplemente' write (2) 'en' STDERR_FILENO'), y llamar a terminate_handler que reemplazó. Además, vea la respuesta y discusión de Logan sobre por qué la excepción todavía debe estar accesible en ese punto. – Potatoswatter

+0

@Potatoswatter: ¿es aplicable su declaración en todas las circunstancias? En otras palabras, ¿sería posible afirmar, pero no recomendar? Gracias. –

+0

Siempre es posible hacer una afirmación, pero se recomienda utilizar cualquiera de las funciones del controlador para realizar una cola del controlador previamente instalado. – Potatoswatter

4

Si está utilizando Windows, una buena biblioteca para manejar excepciones y bloqueos no controlados es CrashRpt. Si desea hacerlo manualmente, también puede use the following I wrote in this answer.

+0

+1 para una respuesta más expansiva a un número limitado q –

4

creo que se beneficiaría de un cajón de sastre declaración de la siguiente manera:

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

Vale la pena señalar que la excepción en sí no está disponible para la introspección en este método. Esto se debe a que es posible que no esté lanzando una instancia 'std :: exception', C++ le permite arrojar cualquier tipo de valor, como un' int' o lo que sea. Puede evitar esto teniendo también 'catch (std :: exception & e)' antes de 'catch (...)', o cualquier tipo que desee capturar. – SingleNegationElimination

+0

Un buen punto de hecho. –

+0

En Windows debe tener un poco de cuidado con la (...) captura de la excepción estructurada lanzada internamente (por ejemplo, violaciones de acceso). Puede ser peligroso continuar la ejecución en condiciones como estas. – seand

1

Sí, es posible. Aquí van:

#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

El estándar de C++ es el manejador de terminar - como otros han dicho

Si usted está después de una mejor rastreabilidad de los tiros, entonces esto es lo que hacemos

Tenemos un tiro macro que registra el nombre y el número de línea y el mensaje, y luego los arroja. Se necesita un mensaje varargs de estilo printf.

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

me dan un buen mensaje de registro

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

Hay dos problemas con esto. Primero, agrega procesamiento adicional a las excepciones que se atrapan. Esto no es un gran problema, porque si sucede lo suficiente como para que la sobrecarga (o el ruido en el registro) sea un problema, de todos modos no se deben usar excepciones para ese caso. El segundo problema es una preocupación más práctica. Cuando ocurren condiciones de excepción, el procesamiento adicional también puede fallar. La asignación previa de almacenamientos intermedios para mensajes de error puede ayudar, pero en última instancia, debe asegurarse de protegerse contra fallas durante el informe de errores. –

+0

desde su lanzamiento (no el tiempo de ejecución) No estoy en modo catastrófico (sin memoria, sin pila, pila muerta), por lo que el controlador funciona. Si no, estoy muerto de todos modos. Utilizo esta técnica en productos empresariales comerciales muy grandes (100 kloc), sin ningún problema. Puedo ajustar el nivel de registro para suprimir gastos generales – pm100

0

Si está realmente interesado en qué sucedió para hacer que su programa falle, puede beneficiarse examinando la imagen del proceso en un depurador post mortem. La técnica precisa varía un poco del sistema operativo al sistema operativo, pero el tren básico es primero habilitar el dumping central y compilar su programa con símbolos de depuración. Una vez que el programa falla, el sistema operativo copiará su memoria en el disco y luego podrá examinar el estado del programa en el momento en que se bloqueó.

Cuestiones relacionadas