Bonjour!
Je cherche un moyen d’ajouter des messages personnalisés à des déclarations d’assertion .. J'ai trouvé cette question Ajouter des messages personnalisés dans assert? mais le message est statique ici. Je veux faire quelque chose comme ça:
assert((0 < x) && (x < 10), std::string("x was ") + myToString(x));
Quand l'assertion échoue, je veux la sortie normale plus par exemple "x était 100".
Vous n'avez pas de chance ici. Le meilleur moyen est de définir votre propre macro assert
.
En gros, ça peut ressembler à ça:
#ifndef NDEBUG
# define ASSERT(condition, message) \
do { \
if (! (condition)) { \
std::cerr << "Assertion `" #condition "` failed in " << __FILE__ \
<< " line " << __LINE__ << ": " << message << std::endl; \
std::terminate(); \
} \
} while (false)
#else
# define ASSERT(condition, message) do { } while (false)
#endif
Cela définira la macro ASSERT
uniquement si la macro no-debug NDEBUG
n’est pas définie.
Alors vous l'utiliseriez comme ceci:
ASSERT((0 < x) && (x < 10), "x was " << x);
Ce qui est un peu plus simple que votre utilisation puisque vous n’avez pas besoin de spécifier explicitement "x was "
et x
, ceci est fait implicitement par la macro.
Il y a quelques vieilles astuces pour inclure des messages sans écrire vos propres routines:
La première est la suivante:
bool testbool = false;
assert(("this is the time", testbool));
Il y a aussi:
bool testbool = false;
assert(testbool && "This is a message");
Le premier fonctionne, car le résultat de l'expression parens intérieurs est la valeur de 'testbool' . Le second fonctionne, car la valeur de la chaîne sera différente de zéro.
Une meilleure solution consiste à apprendre au débogueur à s’arrêter sur une affirmation en cas d’échec. Vous pouvez alors examiner non seulement la valeur x mais également toute autre information, y compris la pile d’appels. Peut-être que c’est ce que vous recherchez vraiment. Un exemple d'implémentation est mentionné ici Manières de montrer à vos co-programmeurs que certaines méthodes ne sont pas encore implémentées dans une classe lors de la programmation en C++
#define ASSERT_WITH_MESSAGE(condition, message) do { \
if (!(condition)) { printf((message)); } \
assert ((condition)); } while(false)
Par souci d’exhaustivité, j’ai publié une implémentation de macro d’affirmation de 2 fichiers en C++:
#include <pempek_assert.h>
int main()
{
float min = 0.0f;
float max = 1.0f;
float v = 2.0f;
PEMPEK_ASSERT(v > min && v < max,
"invalid value: %f, must be between %f and %f", v, min, max);
return 0;
}
Vous invitera à:
Assertion 'v > min && v < max' failed (DEBUG)
in file e.cpp, line 8
function: int main()
with message: invalid value: 2.000000, must be between 0.000000 and 1.000000
Press (I)gnore / Ignore (F)orever / Ignore (A)ll / (D)ebug / A(b)ort:
Où
abort()
(sous Windows, Le système invitera l'utilisateur à joindre un débogueur)abort()
immédiatementVous pouvez en savoir plus à ce sujet ici:
J'espère que cela pourra aider.
Oui, c'est possible.
Pour activer une expression telle que better_assert((0 < x) && (x < 10), std::string("x was ") + myToString(x));
, nous sommes supposés avoir une macro correspondante sous la forme:
#define better_assert(EXPRESSION, ... ) ((EXPRESSION) ? \
(void)0 : print_assertion(std::cerr, \
"Assertion failure: ", #EXPRESSION, " in File: ", __FILE__, \
" in Line: ", __LINE__ __VA_OPT__(,) __VA_ARGS__))
dans lequel print_assertion
est une fonction proxy permettant de faire l'assertion. Lorsque EXPRESSION
est évalué false
, toutes les informations de débogage, le __VA_ARGS__
, seront transférées dans std::cerr
. Cette fonction prend un nombre arbitraire d'arguments, nous devrions donc implémenter une fonction variée basée sur un modèle:
template< typename... Args >
void print_assertion(std::ostream& out, Args&&... args)
{
out.precision( 20 );
if constexpr( debug_mode )
{
(out << ... << args) << std::endl;
abort();
}
}
Dans l'implémentation précédente, l'expression (out << ... << args) << std::endl;
utilisait l'expression de repli en C++ 17 ( https://en.cppreference.com/w/cpp/language/fold ); l'expression constante debug_mode
est liée aux options de compilation passées, ce qui peut être défini comme
#ifdef NDEBUG
constexpr std::uint_least64_t debug_mode = 0;
#else
constexpr std::uint_least64_t debug_mode = 1;
#endif
Il convient également de noter que l'expression if constexpr( debug_mode )
utilise constexpr si ( https://en.cppreference.com/w/cpp/language/if ) importé depuis C++ 17.
Pour tout emballer, nous avons:
#ifdef NDEBUG
constexpr std::uint_least64_t debug_mode = 0;
#else
constexpr std::uint_least64_t debug_mode = 1;
#endif
template< typename... Args >
void print_assertion(std::ostream& out, Args&&... args)
{
out.precision( 20 );
if constexpr( debug_mode )
{
(out << ... << args) << std::endl;
abort();
}
}
#ifdef better_assert
#undef better_assert
#endif
#define better_assert(EXPRESSION, ... ) ((EXPRESSION) ? (void)0 : print_assertion(std::cerr, "Assertion failure: ", #EXPRESSION, " in File: ", __FILE__, " in Line: ", __LINE__ __VA_OPT__(,) __VA_ARGS__))
Un scénario de test typique démontrant son utilisation peut être:
double const a = 3.14159265358979;
double const b = 2.0 * std::asin( 1.0 );
better_assert( a==b, " a is supposed to be equal to b, but now a = ", a, " and b = ", b );
Cela produira un message d'erreur comme:
Assertion failure: a==b in File: test.cc in Line: 9 a is supposed to be equal to b, but now a = 3.1415926535897900074 and b = 3.141592653589793116
[1] 8414 abort (core dumped) ./test
Et le code source complet est disponible dans ce référentiel: https://github.com/fengwang/better_assert