2012-08-12 19 views
9

En mi programa quiero usar las afirmaciones que muestran un mensaje de error. Además de las soluciones bien conocidas para C y C++ existe la solución "real" como BOOST ofrece BOOST_ASSERT_MSG(expr, msg) (vea también assert() with message)¿Confirmar con mensaje dinámico?

Pero un mensaje estático no es suficiente para mí, también quiero mostrar algunas veces las variables fallidas, p.ej en un caso como

BOOST_ASSERT_MSG(length >= 0, "No positive length found! It is " << length) 

Como se puede ver que me gustaría dar formato al mensaje de "cadena" como un stringstream o ostream como que habíamos me permitirá mostrar fácilmente tipos personalizados (suponiendo que he definido el relevante función de formateo).

El problema aquí es que BOOST_ASSERT_MSG requiere de manera predeterminada char const *, por lo que no es compatible.

¿Hay alguna manera de redefinir/sobrecargar assertion_failed_msg() de tal manera que usar una secuencia como mensaje funcione? ¿Cómo?
(Mi enfoque ingenuo fallidos como el compilador primero quería hacer un operator<<("foo",bar) en el mensaje en sí mismo ...)

Respuesta

6

Se podría definir su propio macro

#define ASSERT_WITH_MSG(cond, msg) do \ 
{ if (!(cond)) { std::ostringstream str; str << msg; std::cerr << str.str(); std::abort(); } \ 
} while(0) 
+0

Por qué 'while (0)'? – WiSaGaN

+1

Consulte http://stackoverflow.com/questions/1067226/c-multi-line-macro-do-while0-vs-scope-block – Greg

+1

Si usa 'while (0)', omita ';'. –

5

Es relativamente trivial para lograrlo.

BOOST_ASSERT_MSG(length >= 0, (std::stringstream() << "No positive length found! It is " << length).str().c_str()) 
+4

Esperaba hacerlo de esa manera, pero el compilador se queja: 'error: 'struct std :: basic_ostream ' no tiene ningún miembro llamado 'str'' – Chris

+3

Oh, sí. Sigo olvidando qué tan rota está la lib del stringstream. – Puppy

+0

Tienes que 'static_cast ()' el valor de retorno de 'operator <<()' para poder usar 'std :: stringstream :: str()'. De lo contrario, intentas llamar a 'std :: ostream :: str()', que no existe. – Ruslan

1

uso el BOOST_ASSERT_MSG con mi propio envoltorio alrededor de ella, de modo que el mensaje especificando afirmar con múltiples operator<< parece menos complejo.

#if defined ASSERT_ENABLED 

    #define ASSERT(cond, msg) {\ 
     if(!(cond))\ 
     {\ 
      std::stringstream str;\ 
      str << msg;\ 
      BOOST_ASSERT_MSG(cond, str.str().c_str());\ 
     }\ 
    } 
#else 
    #define ASSERT(...) 
#endif 

ejemplo de uso, proporcionan mensaje personalizado como está dando salida a cout:

ASSERT(execSize == (_oldSize - remaining), "execSize : " << execSize << ", _oldSize : " << _oldSize << ", remaining : " << remaining); 

Lo que hace es, si se ha definido ASSERT_ENABLED, activar los mensajes de aserción. if(!(cond)) parte es la optimización, lo que evita las costosas operaciones de cadena especificados por el parámetro macro msg, si es condtrue

+0

¿Realmente necesitas '# si se define ASSERT_ENABLED'? Creo que si assert está deshabilitado, las declaraciones se eliminan de todos modos, si se usa un compilador de optimización. ¿Estoy en lo correcto? –

+0

@SohailSi: Sí, '# si se define ASSERT_ENABLED' es para optimización. En general, las compilaciones de lanzamiento están destinadas a ser desactivadas con aserciones. Esto elimina el código del binario. Binario más pequeño, menos código, mejor uso de la memoria caché de instrucciones. Mientras que assert ahorra mucho tiempo en la depuración. A veces, una simple verificación de afirmación podría ahorrar 3 días de tiempo de depuración. –

+0

Quiero decir si ASSERT_ENABLED definido es falso, entonces el compilador debería eliminar automáticamente las instrucciones BOOST_ASSERT_MSG, por lo tanto, debería eliminar el resto de su código de afirmación. Por lo tanto, ASSERT_ENABLED no es necesario que se verifique explícitamente. Pero no estoy seguro de por qué es necesario decirlo explícitamente.¿Dónde estoy equivocado? –