Añadir mensajes personalizados en afirmar?

129

¿Hay alguna manera de agregar o editar el mensaje lanzado por afirmar? Me gustaría usar algo como

assert(a == b, "A must be equal to B");

Luego, el compilador agrega línea , tiempo , etc.

¿Es posible?

Killrazor
fuente
Puede definir una macro, como esta .
HelloGoodbye

Respuestas:

240

Un truco que he visto es usar el &&operador. Dado que un puntero "es verdadero" si no es nulo, puede hacer lo siguiente sin alterar la condición:

assert(a == b && "A is not equal to B");

Como assertmuestra la condición que falló, también mostrará su mensaje. Si no es suficiente, puede escribir su propia myAssertfunción o macro que mostrará lo que desee.

zneak
fuente
27
Otra opción es invertir los operandos y usar el operador de coma. Necesita paréntesis adicionales para que la coma no se trate como un delimitador entre los argumentos:assert(("A must be equal to B", a == b));
Keith Thompson
3
Sin embargo, sería bueno poder imprimir los valores de las variables, como en:assert(a == b && "A (" << A << ") is not equal to B (" << B << ")");
Frank
77
@Frank, printfdevuelve un valor distinto de cero si imprime algo, por lo que podría hacer algo como assert(a == b && printf("a (%i) is not equal to b (%i)", a, b)), aunque en ese momento probablemente debería escribir su propio contenedor de aserción.
zneak
1
Mal código! No entiendo esto! Si a == b es falso, la expresión y también debe ser falsa y, por lo tanto, la cadena no debe evaluarse.
ragnarius
1
@TUIlover, no es así como funcionan los literales de cadena C; son constantes de tiempo de compilación y su uso en este contexto se optimiza trivialmente. No hay costo de tiempo de ejecución.
zneak
45

Otra opción es invertir los operandos y usar el operador de coma. Necesita paréntesis adicionales para que la coma no se trate como un delimitador entre los argumentos:

assert(("A must be equal to B", a == b));

(esto se copió de los comentarios anteriores, para una mejor visibilidad)

Andrei Bozantan
fuente
3
Este es un gran enfoque, con un pequeño problema, mostrará "advertencia: el operando izquierdo del operador de coma no tiene ningún efecto" cuando se compila en g ++ con `-Wunused-value
v010dya
1
o con una macro: #ifndef m_assert #define m_assert (expr, msg) afirmar ((msg, expr)) #endif
Szymon Marczak
El uso de un macro wrapper le permite evitar la advertencia de gcc:#define m_assert(expr, msg) assert(( (void)(msg), (expr) ))
Jander
25

Aquí está mi versión de afirmar macro, que acepta el mensaje e imprime todo de manera clara:

#include <iostream>

#ifndef NDEBUG
#   define M_Assert(Expr, Msg) \
    __M_Assert(#Expr, Expr, __FILE__, __LINE__, Msg)
#else
#   define M_Assert(Expr, Msg) ;
#endif

void __M_Assert(const char* expr_str, bool expr, const char* file, int line, const char* msg)
{
    if (!expr)
    {
        std::cerr << "Assert failed:\t" << msg << "\n"
            << "Expected:\t" << expr_str << "\n"
            << "Source:\t\t" << file << ", line " << line << "\n";
        abort();
    }
}

Ahora puedes usar esto

M_Assert(ptr != nullptr, "MyFunction: requires non-null argument");

Y en caso de falla, recibirá un mensaje como este:

Error de afirmación: MyFunction: requiere un argumento no nulo

Esperado: ptr! = Nullptr

Fuente: C: \ MyProject \ src.cpp, línea 22

Agradable y limpio, siéntase libre de usarlo en su código =)

Eugene Magdalits
fuente
Buena esa. Muy útil
Killrazor
Estoy un poco confundida. ¿#Expr se trata como una cadena para la sustitución directa? ¿Cuál es la diferencia entre #Expr y Expr?
Minh Tran
@MinhTran Supongamos que su condición de afirmación es x == y. Luego Expr se expandirá en if( !(x == y))y aquí es donde se verifica la condición, y #Expr se expandirá en cadena literal "x == y", que luego ponemos en el mensaje de error.
Eugene Magdalits
Desafortunadamente, esta solución causa un comportamiento indefinido debido al uso de identificadores reservados.
Recuerda a Monica el
19
BOOST_ASSERT_MSG(expre, msg)

http://www.boost.org/doc/libs/1_51_0/libs/utility/assert.html

Puede usar eso directamente o copiar el código de Boost. También tenga en cuenta que la afirmación de Boost es solo un encabezado, por lo que puede tomar ese único archivo si no desea instalar todo Boost.

Cero
fuente
@Jichao, ¿qué quieres decir con implementar la interfaz de afirmación?
Tarc
7

Como la respuesta de zneak complica un poco el código, un mejor enfoque es simplemente comentar el texto de cadena del que está hablando. es decir.:

assert(a == b); // A must be equal to B

Dado que el lector del error de aserción buscará el archivo y la línea de todos modos desde el mensaje de error, verá la explicación completa aquí.

Porque, al final del día, esto:

assert(number_of_frames != 0); // Has frames to update

lee mejor que esto:

assert(number_of_frames != 0 && "Has frames to update");

en términos de análisis humano del código, es decir. legibilidad. Tampoco es un hack de idiomas.

metamorfosis
fuente
1
"Dado que el lector del error de afirmación buscará el archivo y la línea de todos modos desde el mensaje de error", solo si es diligente.
Jason S
Solo si quieren arreglar el error, quieres decir ... qué comentario más tonto
metamorfosis el
1
No. Cuanto más fácil sea que las personas vean el problema, es más probable que tomen medidas.
Jason S
encogerse de hombros En desacuerdo.
metamorfosis
2

afirmar es una combinación macro / función. puede definir su propia macro / función, usando __FILE__, __BASE_FILE__, __LINE__etc, con su propia función que toma un mensaje personalizado

Merlyn Morgan-Graham
fuente
-6

Para vc, agregue el siguiente código en afirmar.h,

#define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )
Jichao
fuente
11
Modificar los encabezados de tu compilador es una mala idea.
Ross Ridge