c++ exception assert

c++ - ¿Cómo puedo assert() sin usar abort()?



assert python (6)

Esto es lo que tengo en "assert.h" (Mac OS 10.4):

#define assert(e) ((void) ((e) ? 0 : __assert (#e, __FILE__, __LINE__))) #define __assert(e, file, line) ((void)printf ("%s:%u: failed assertion `%s''/n", file, line, e), abort(), 0)

En función de eso, reemplace la llamada a abort () por un lanzamiento (excepción). Y en lugar de printf puede formatear la cadena en el mensaje de error de la excepción. Al final, obtienes algo como esto:

#define assert(e) ((void) ((e) ? 0 : my_assert (#e, __FILE__, __LINE__))) #define my_assert( e, file, line ) ( throw std::runtime_error(/ std::string(file:)+boost::lexical_cast<std::string>(line)+": failed assertion "+e))

No he intentado compilarlo, pero entiendes el significado.

Nota: deberá asegurarse de que el encabezado de "excepción" siempre esté incluido, así como de los refuerzos (si decide usarlo para formatear el mensaje de error). Pero también puedes hacer que "my_assert" sea una función y solo declarar su prototipo. Algo como:

void my_assert( const char* e, const char* file, int line);

E impleméntelo en algún lugar donde pueda incluir libremente todos los encabezados que necesite.

Envuélvalo en alguna DEPUD #ifdef si lo necesita, o no si siempre desea ejecutar esos controles.

Si uso assert() y la aserción falla, assert() llamará a abort() , terminando el programa en ejecución abruptamente. No puedo pagar eso en mi código de producción. ¿Hay alguna manera de afirmar en tiempo de ejecución pero poder capturar las afirmaciones fallidas para poder manejarlas correctamente?


Las aseveraciones en C / C ++ solo se ejecutan en compilaciones de depuración. Entonces esto no sucederá en tiempo de ejecución. En general, las afirmaciones deben marcar cosas que, si suceden, indican un error y generalmente muestran suposiciones en su código, etc.

Si desea tener un código que verifique los errores en tiempo de ejecución (en versión), probablemente debería usar excepciones en lugar de afirmaciones, ya que son para lo que están diseñadas. Su respuesta básicamente envuelve a un lanzador de excepción en la sintaxis de afirmación. Si bien esto funcionará, no hay una ventaja especial en esto que pueda ver más que lanzar la excepción en primer lugar.


Sí, de hecho lo hay. Tendrá que escribir una función de afirmación personalizada usted mismo, ya que la afirmación de C ++ assert() es exactamente la assert() de C assert() , con la función de abort() incorporada. Afortunadamente, esto es sorprendentemente sencillo.

Assert.hh

template <typename X, typename A> inline void Assert(A assertion) { if( !assertion ) throw X(); }

La función anterior arrojará una excepción si un predicado no se cumple. Entonces tendrás la oportunidad de atrapar la excepción. Si no capta la excepción, se llamará a terminate() , que terminará el programa de manera similar a abort() .

Puede que se pregunte qué hay acerca de cómo optimizar la afirmación cuando estamos construyendo para la producción. En este caso, puede definir constantes que significarán que está construyendo para producción y luego referir a la constante cuando Assert() .

debug.hh

#ifdef NDEBUG const bool CHECK_WRONG = false; #else const bool CHECK_WRONG = true; #endif

main.cc

#include<iostream> struct Wrong { }; int main() { try { Assert<Wrong>(!CHECK_WRONG || 2 + 2 == 5); std::cout << "I can go to sleep now./n"; } catch( Wrong e ) { std::cerr << "Someone is wrong on the internet!/n"; } return 0; }

Si CHECK_WRONG es una constante, la llamada a Assert() se compilará en producción, incluso si la afirmación no es una expresión constante. Hay una ligera desventaja en que al consultar CHECK_WRONG un poco más. Pero a cambio, obtenemos una ventaja, ya que podemos clasificar varios grupos de aserciones y habilitar y deshabilitar cada una de ellas según lo consideremos oportuno. Entonces, por ejemplo, podríamos definir un grupo de aserciones que queremos habilitar incluso en el código de producción, y luego definir un grupo de aserciones que solo queremos ver en las compilaciones de desarrollo.

La función Assert() es equivalente a escribir

if( !assertion ) throw X();

pero indica claramente la intención del programador: hacer una afirmación. Las aserciones también son más fáciles de grep con este enfoque, al igual que plain assert() s.

Para más detalles sobre esta técnica, consulte El lenguaje de programación C ++ de Bjarne Stroustrup, sección 24.3.7.2.



Las funciones de informe de error de glib tienen el enfoque de continuar después de una afirmación. glib es la biblioteca de independencia de la plataforma subyacente que utiliza Gnome (a través de GTK). Aquí hay una macro que verifica una precondición e imprime un seguimiento de pila si falla la condición previa.

#define RETURN_IF_FAIL(expr) do { / if (!(expr)) / { / fprintf(stderr, / "file %s: line %d (%s): precondition `%s'' failed.", / __FILE__, / __LINE__, / __PRETTY_FUNCTION__, / #expr); / print_stack_trace(2); / return; / }; } while(0) #define RETURN_VAL_IF_FAIL(expr, val) do { / if (!(expr)) / { / fprintf(stderr, / "file %s: line %d (%s): precondition `%s'' failed.", / __FILE__, / __LINE__, / __PRETTY_FUNCTION__, / #expr); / print_stack_trace(2); / return val; / }; } while(0)

Aquí está la función que imprime el seguimiento de pila, escrito para un entorno que utiliza la cadena de herramientas gnu (gcc):

void print_stack_trace(int fd) { void *array[256]; size_t size; size = backtrace (array, 256); backtrace_symbols_fd(array, size, fd); }

Así es como usarías las macros:

char *doSomething(char *ptr) { RETURN_VAL_IF_FAIL(ptr != NULL, NULL); // same as assert(ptr != NULL), but returns NULL if it fails. if( ptr != NULL ) // Necessary if you want to define the macro only for debug builds { ... } return ptr; } void doSomethingElse(char *ptr) { RETURN_IF_FAIL(ptr != NULL); }


_set_error_mode(_OUT_TO_MSGBOX);

Créeme, esta función puede ayudarte.