c++ debugging assert debugbreak

¿Cuál es la mejor forma de implementar la verificación de afirmaciones en C++?



debugging assert (11)

Assert es (generalmente) Depurar solo

El problema con "afirmar" es que generalmente se trata de binarios de depuración, y que algunos desarrolladores los usan como si el código aún estuviera en producción.

Esto no es malo per se, ya que se supone que el código debe ser probado intensivamente, y por lo tanto, los errores que producen la afirmación seguramente serán descubiertos y eliminados.

Pero a veces (¿la mayoría de las veces?), Las pruebas no son tan intensivas como se desea. No voy a hablar sobre un viejo trabajo en el que tuvimos que codificar hasta el último minuto ( no pregunte ... a veces, los gerentes son solo ... ejem ... ) ... ¿De qué sirve afirmarlo? agregar a un código que será compilado y entregado como un lanzamiento binario para el cliente el próximo minuto?

Afirmar en (algunas) aplicaciones de la vida real

En nuestro equipo, necesitábamos algo para detectar el error y, al mismo tiempo, algo más para manejar el error. Y lo necesitábamos, potencialmente, en Release Build.

Assert detectará y manejará el error solo en la compilación de depuración.

Así que agregamos en su lugar una macro XXX_ASSERT, así como una macro XXX_RAISE_ERROR.

La macro XXX_ASSERT haría lo mismo que la macro ASSERT, pero se crearía tanto en Debug como en Release. Su comportamiento (escribir un registro, abrir un cuadro de mensaje, no hacer nada, etc.) podría controlarse mediante un archivo .INI, y ENTONCES, abortaría / saldría de la aplicación.

Esto fue utilizado como:

bool doSomething(MyObject * p) { // If p is NULL, then the app will abort/exit XXX_ASSERT((p != NULL), "Hey ! p is NULL !") ; // etc. }

La macro XXX_RAISE_ERROR solo "registraría" el error pero no trataría de manejarlo. Esto significa que puede registrar el mensaje en un archivo y / o abrir un MessageBox con el mensaje, y un botón para continuar, y otro para iniciar una sesión de depuración (según la configuración del archivo .INI). Esto fue utilizado como:

bool doSomething(MyObject * p) { if(p == NULL) { // First, XXX_RAISE_ERROR will alert the user as configured in the INI file // perhaps even offering to open a debug session XXX_RAISE_ERROR("Hey ! p is NULL !") ; // here, you can handle the error as you wish // Than means allocating p, or throwing an exception, or // returning false, etc. // Whereas the XXX_ASSERT could simply crash. } // etc. }

Un año después de su introducción en nuestras bibliotecas, solo se está utilizando XXX_RAISE_ERROR. Por supuesto, no se puede usar en partes de la aplicación que sean críticas (tenemos un XXX_RAISE_ERROR_DBG para eso), pero en cualquier otro lugar, es bueno. Y los hechos de que uno puede usar cualquier manejo de error preferido, y que puede ser activado a voluntad, ya sea en la computadora del desarrollador, o el probador, o incluso el usuario, es bastante útil.

Con eso quiero decir, ¿qué tengo que hacer para tener aserciones útiles en mi código?

MFC es bastante fácil, solo uso ASSERT (algo).

¿Cuál es la forma de no MFC?

Editar: ¿Es posible detener la violación de afirmación en assert.c en lugar de mi archivo que llamó a assert ()?

Editar: ¿Cuál es la diferencia entre <assert.h> y <cassert> ?

Respuesta aceptada: un montón de buenas respuestas en esta publicación, desearía poder aceptar más de una respuesta (o alguien las combinaría todas). Así que la respuesta se concede a Ferruccio (para la primera respuesta).


Uso básico de Assert

#include <cassert> /* Some code later */ assert( true );

Notas de buenas prácticas

Las afirmaciones se usan para identificar estados de tiempo de ejecución que deberían ser verdaderos . Como resultado, se compilan en modo de lanzamiento.

Si tiene una situación en la que desea que una afirmación siempre golpee, puede pasarle falso. Por ejemplo:

switch ( someVal ): { case 0: case 1: break; default: assert( false ); /* should never happen */ }

También es posible pasar un mensaje a través de assert:

assert( !"This assert will always hit." );

Las bases de código maduras con frecuencia amplían la funcionalidad de afirmación. Algunas de las extensiones comunes incluyen:

  • Alternar afirma por módulo para localizar las pruebas.
  • Creando una macro assert adicional que se compila en la mayoría de las compilaciones de depuración. Esto es deseable para el código que se llama con mucha frecuencia (millones de veces por segundo) y es poco probable que sea incorrecto.
  • Permitir a los usuarios deshabilitar la afirmación de hit actual, todos los afirma en la unidad de compilación o todos los asertos en la base de código. Esto evita que se activen aseveraciones benignas, creando compilaciones inutilizables.

Depende de si está buscando algo que funcione fuera de Visual C ++ o no. También depende del tipo de afirmación que estés buscando.

Hay algunos tipos de aserciones:

  1. Preprocesador
    Estas afirmaciones se hacen usando la directiva de preprocesador #error
    Las aserciones del preprocesador solo se evalúan durante la fase de preprocesamiento y, por lo tanto, no son útiles para elementos tales como las plantillas.

  2. Ejecutar tiempo
    Estas afirmaciones se hacen usando la función assert() definida en <cassert>
    Las aserciones de tiempo de ejecución solo se evalúan en tiempo de ejecución. Y como señaló BoltBait, no se compilan si se ha definido la macro NDEBUG .

  3. Estático
    Estas afirmaciones se hacen, como dijo, al utilizar la macro ASSERT() , pero solo si está utilizando MFC. No sé de otra manera de hacer aserciones estáticas que forman parte del estándar C / C ++, sin embargo, la biblioteca Boost ofrece otra solución: static_assert .
    La función static_assert de la biblioteca Boost es algo que se agregará en el estándar C ++ 0x .

Como advertencia adicional, la función assert() que sugirió Ferruccio no tiene el mismo comportamiento que la macro MFC ASSERT() . El primero es una aserción de tiempo de ejecución, mientras que el último es una aserción estática.

¡Espero que esto ayude!


Para entrar dentro del archivo que llamó a assert, puede usar una macro personalizada que arroje una excepción o llame a __debugbreak :

#define MYASSERT(EXPR, MSG) if (!(EXPR)) throw MSG;

O:

#define MYASSERT(EXPR) if (!(EXPR)) __debugbreak();


Para responder la pregunta en su segunda "edición":

<assert.h> es el encabezado C

<cassert> es el encabezado de la biblioteca estándar de C ++ ... generalmente incluye <assert.h>


use intellisense para abrirlo en el estudio visual (clic derecho)

// cassert standard header #include <yvals.h> #include <assert.h>

yvals.h es cosas de Windows. Entonces, en lo que se refiere a assert (), las dos formas de incluirlo son idénticas. es una buena práctica usar el <cxxx> porque a menudo no es tan simple (envoltura del espacio de nombres y tal vez otra magia)

Esto se rompe en el sitio de llamadas para mí ...

Aquí hay un artículo explicando por qué no quieres escribir esta macro tú mismo.


Microsoft-specific CRT afirma

#include <crtdbg.h> #include <sstream> ... // displays nondescript message box when x <= 42 _ASSERT(x > 42); // displays message box with "x > 42" message when x <= 42 _ASSERTE(x > 42); // displays message box with computed message "x is ...!" when x <= 42 _ASSERT_EXPR( x > 42, (std::stringstream() << L"x is " << x << L"!").str().c_str());


#include <cassert> assert(something);

y para la verificación en tiempo de compilación, las afirmaciones estáticas de Boost son bastante útiles:

#include <boost/static_assert.hpp> BOOST_STATIC_ASSERT(sizeof(int) == 4); // compile fails if ints aren''t 32-bit


Hay una biblioteca de código abierto más avanzada llamada ModAssert, que tiene aserciones que funcionan tanto en Visual C ++ como en gcc. Probablemente también en otros compiladores, no estoy seguro. Lleva un tiempo aprenderlo, pero si desea buenas afirmaciones que no dependan de MFC, mire estas. Está en http://sourceforge.net/projects/modassert/



Para responder la tercera pregunta del asker: la primera razón por la que usamos "cassert" en lugar de "assert.h" es porque, en el caso de C ++, hay una concesión para el hecho de que el compilador C ++ no puede almacenar las descripciones de funciones en el código archivos, pero en un dll o en el compilador en sí. El segundo es que puede haber cambios menores en las funciones para facilitar las diferencias entre C y C ++, ya sea presente o en el futuro. Como assert.h es una biblioteca C, la preferencia es usar "cassert" mientras está en C ++.