web-dev-qa-db-fra.com

Ajout de message à assert

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".

35
tauran

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.

65
Konrad Rudolph

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.

11
Cameron

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++

8
Alsk
#define ASSERT_WITH_MESSAGE(condition, message) do { \
if (!(condition)) { printf((message)); } \
assert ((condition)); } while(false)
6
AlcubierreDrive

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ù 

  • (I) gnore: ignore l'assertion courante
  • Ignore (F) orever: rappelez-vous le fichier et la ligne où l'assertion a été déclenchée et L'ignore pour la suite de l'exécution du programme
  • Ignorer (A) ll: ignorer toutes les assertions restantes (tous les fichiers et toutes les lignes)
  • (D) ebug: entrer dans le débogueur s'il est connecté, sinon abort() (sous Windows, Le système invitera l'utilisateur à joindre un débogueur)
  • A (b) ort: appelez abort() immédiatement

Vous pouvez en savoir plus à ce sujet ici:

J'espère que cela pourra aider.

2
Gregory Pakosz

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

0
Feng Wang