Ajouter des messages personnalisés dans assert?

129

Existe-t-il un moyen d'ajouter ou de modifier le message lancé par assert? J'aimerais utiliser quelque chose comme

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

Ensuite, le compilateur ajoute la ligne , l' heure et ainsi de suite ...

C'est possible?

Killrazor
la source
Vous pouvez définir une macro, comme ceci .
HelloGoodbye

Réponses:

240

Un hack que j'ai vu est d'utiliser l' &&opérateur. Étant donné qu'un pointeur «est vrai» s'il n'est pas nul, vous pouvez effectuer les opérations suivantes sans modifier la condition:

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

Puisque assertmontre la condition qui a échoué, il affichera également votre message. Si cela ne suffit pas, vous pouvez écrire votre propre myAssertfonction ou macro qui affichera ce que vous voulez.

zneak
la source
27
Une autre option consiste à inverser les opérandes et à utiliser l'opérateur virgule. Vous avez besoin de parenthèses supplémentaires pour que la virgule ne soit pas traitée comme un délimiteur entre les arguments:assert(("A must be equal to B", a == b));
Keith Thompson
3
Ce serait bien, cependant, de pouvoir imprimer les valeurs des variables, comme dans:assert(a == b && "A (" << A << ") is not equal to B (" << B << ")");
Frank
7
@Frank, printfrenvoie une valeur différente de zéro s'il imprimait quelque chose, vous pouvez donc faire quelque chose comme assert(a == b && printf("a (%i) is not equal to b (%i)", a, b)), bien qu'à ce stade, vous devriez probablement écrire votre propre wrapper d'assert.
zneak
1
Mauvais code! Je ne comprends pas ça! Si a == b est faux, l'expression and doit également être fausse et, par conséquent, la chaîne ne doit pas être évaluée.
ragnarius
1
@TUIlover, ce n'est pas ainsi que fonctionnent les littéraux de chaîne C; ce sont des constantes de compilation et leur utilisation dans ce contexte est optimisée de manière triviale. Il n'y a aucun coût d'exécution.
zneak
45

Une autre option consiste à inverser les opérandes et à utiliser l'opérateur virgule. Vous avez besoin de parenthèses supplémentaires pour que la virgule ne soit pas traitée comme un délimiteur entre les arguments:

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

(ceci a été copié à partir des commentaires ci-dessus, pour une meilleure visibilité)

Andrei Bozantan
la source
3
C'est une excellente approche, avec un petit problème, il affichera "avertissement: l'opérande gauche de l'opérateur virgule n'a aucun effet" lorsqu'il est compilé en g ++ avec `-Wunused-value
v010dya
1
ou avec une macro: #ifndef m_assert #define m_assert (expr, msg) assert ((msg, expr)) #endif
Szymon Marczak
L'utilisation d'un wrapper de macro vous permet d'éviter l'avertissement gcc:#define m_assert(expr, msg) assert(( (void)(msg), (expr) ))
Jander
25

Voici ma version de la macro assert, qui accepte le message et imprime tout de manière claire:

#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();
    }
}

Maintenant, vous pouvez utiliser ceci

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

Et en cas d'échec, vous recevrez un message comme celui-ci:

Échec de l'assertion: MyFunction: nécessite un argument non nul

Attendu: ptr! = Nullptr

Source: C: \ MyProject \ src.cpp, ligne 22

Agréable et propre, n'hésitez pas à l'utiliser dans votre code =)

Eugène Magdalits
la source
Joli. Très utile
Killrazor
Je suis un peu confus. #Expr est-il traité comme une chaîne de substitution directe? Quelle est la différence entre #Expr et Expr?
Minh Tran
@MinhTran Supposons que votre condition d'assertion est x == y. Ensuite, Expr se développera if( !(x == y))et c'est là que la condition est vérifiée, et #Expr se développera en littéral de chaîne "x == y", que nous mettons ensuite dans un message d'erreur.
Eugene Magdalits
Malheureusement, cette solution entraîne un comportement non défini en raison de l'utilisation d'identificateurs réservés.
Rappelez
19
BOOST_ASSERT_MSG(expre, msg)

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

Vous pouvez soit l'utiliser directement, soit copier le code de Boost. Notez également que l'assertion de Boost est uniquement un en-tête, vous pouvez donc simplement récupérer ce fichier unique si vous ne souhaitez pas installer tout Boost.

Zéro
la source
@Jichao, qu'entendez-vous par implémentation de l'interface d'assert?
Tarc
7

Comme la réponse de zneak complique quelque peu le code, une meilleure approche consiste simplement à commenter la chaîne de texte dont vous parlez. c'est à dire.:

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

Puisque le lecteur de l'erreur d'assertion recherchera de toute façon le fichier et la ligne à partir du message d'erreur, il verra l'explication complète ici.

Parce qu'en fin de compte, ceci:

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

lit mieux que ça:

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

en termes d'analyse humaine du code ie. lisibilité. Pas non plus un hack de langue.

métamorphose
la source
1
"Puisque le lecteur de l'erreur d'assertion recherchera le fichier et la ligne de toute façon à partir du message d'erreur," - seulement s'ils sont diligents.
Jason S du
Seulement s'ils veulent corriger le bogue, vous voulez dire ... quel commentaire idiot
métamorphose
1
Non. Plus vous aidez les gens à voir le problème, plus ils auront de chances d'agir.
Jason S du
hausser les épaules Pas d' accord.
métamorphose du
2

assert est une combinaison macro / fonction. vous pouvez définir votre propre macro / fonction, en utilisant __FILE__, __BASE_FILE__, __LINE__etc, avec votre propre fonction qui prend un message personnalisé

Merlyn Morgan-Graham
la source
-6

Pour vc, ajoutez le code suivant dans assert.h,

#define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )
Jichao
la source
11
Modifier les en-têtes de votre compilateur est une mauvaise idée.
Ross Ridge