Benutzerdefinierte Nachrichten in assert hinzufügen?

128

Gibt es eine Möglichkeit, die von assert ausgelöste Nachricht hinzuzufügen oder zu bearbeiten? Ich würde gerne so etwas verwenden

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

Dann fügt der Compiler Zeile , Zeit usw. hinzu ...

Ist es möglich?

Killrazor
quelle
Sie können ein Makro, wie definieren diese .
HelloGoodbye

Antworten:

239

Ein Hack, den ich gesehen habe, ist die Verwendung des &&Operators. Da ein Zeiger "wahr" ist, wenn er nicht null ist, können Sie Folgendes tun, ohne die Bedingung zu ändern:

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

Da assertder Zustand angezeigt wird, der fehlgeschlagen ist, wird auch Ihre Nachricht angezeigt. Wenn es nicht ausreicht, können Sie Ihre eigene myAssertFunktion oder Ihr eigenes Makro schreiben , das alles anzeigt, was Sie wollen.

zneak
quelle
27
Eine andere Möglichkeit besteht darin, die Operanden umzukehren und den Kommaoperator zu verwenden. Sie benötigen zusätzliche Klammern, damit das Komma nicht als Trennzeichen zwischen den Argumenten behandelt wird:assert(("A must be equal to B", a == b));
Keith Thompson
3
Es wäre jedoch schön, die Werte von Variablen drucken zu können, wie in:assert(a == b && "A (" << A << ") is not equal to B (" << B << ")");
Frank
7
@Frank printfgibt einen Wert ungleich Null zurück, wenn etwas gedruckt wurde, sodass Sie so etwas tun können assert(a == b && printf("a (%i) is not equal to b (%i)", a, b)), obwohl Sie an diesem Punkt wahrscheinlich Ihren eigenen Assert-Wrapper schreiben sollten.
zneak
1
Schlechter Code! Ich verstehe das nicht! Wenn a == b falsch ist, sollte der and-Ausdruck ebenfalls falsch sein, und daher sollte die Zeichenfolge nicht ausgewertet werden.
Ragnarius
1
@TUIlover, so funktionieren C-String-Literale nicht; Sie sind Konstanten zur Kompilierungszeit und ihre Verwendung in diesem Zusammenhang ist trivial optimiert. Es fallen keine Laufzeitkosten an.
Zneak
45

Eine andere Möglichkeit besteht darin, die Operanden umzukehren und den Kommaoperator zu verwenden. Sie benötigen zusätzliche Klammern, damit das Komma nicht als Trennzeichen zwischen den Argumenten behandelt wird:

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

(Dies wurde aus Gründen der besseren Sichtbarkeit aus den obigen Kommentaren kopiert.)

Andrei Bozantan
quelle
3
Dies ist ein großartiger Ansatz, mit einem winzigen Problem wird "Warnung: linker Operand des Kommaoperators hat keine Auswirkung" angezeigt, wenn in g ++ mit `-Wunused-value
v010dya
1
oder mit einem Makro: #ifndef m_assert #define m_assert (Ausdruck, msg) assert ((msg, Ausdruck)) #endif
Szymon Marczak
Mit einem Makro-Wrapper können Sie die gcc-Warnung vermeiden:#define m_assert(expr, msg) assert(( (void)(msg), (expr) ))
Jander
25

Hier ist meine Version des Assert-Makros, das die Nachricht akzeptiert und alles klar ausdruckt:

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

Jetzt können Sie dies verwenden

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

Und im Fehlerfall erhalten Sie eine Meldung wie folgt:

Assert fehlgeschlagen: MyFunction: erfordert ein Argument ungleich Null

Erwartet: ptr! = Nullptr

Quelle: C: \ MyProject \ src.cpp, Zeile 22

Schön und sauber, zögern Sie nicht, es in Ihrem Code zu verwenden =)

Eugene Magdalits
quelle
Schön. Sehr nützlich
Killrazor
Ich bin ein wenig verwirrt. Wird #Expr als Zeichenfolge für die direkte Substitution behandelt? Was ist der Unterschied zwischen #Expr und Expr?
Minh Tran
@MinhTran Nehmen wir an, dass Ihre Assert-Bedingung lautet x == y. Dann wird Expr erweitert if( !(x == y))und hier wird die Bedingung überprüft, und #Expr wird in ein Zeichenfolgenliteral erweitert "x == y", das wir dann in eine Fehlermeldung einfügen .
Eugene Magdalits
Leider verursacht diese Lösung aufgrund der Verwendung reservierter Bezeichner undefiniertes Verhalten.
Erinnern Sie sich an Monica
19
BOOST_ASSERT_MSG(expre, msg)

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

Sie können das entweder direkt verwenden oder den Code von Boost kopieren. Beachten Sie auch, dass Boost Assert nur ein Header ist, sodass Sie nur diese einzelne Datei abrufen können, wenn Sie nicht den gesamten Boost installieren möchten.

Null
quelle
@Jichao, was meinst du mit der Implementierung der Assert-Schnittstelle?
Tarc
7

Da die Antwort von zneak den Code etwas verwickelt, ist es besser, nur den Zeichenfolgentext zu kommentieren, über den Sie sprechen. dh:

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

Da der Leser des Assert-Fehlers die Datei und die Zeile ohnehin anhand der Fehlermeldung nachschlägt, wird hier die vollständige Erklärung angezeigt.

Denn am Ende des Tages ist dies:

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

liest sich besser als das:

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

in Bezug auf die menschliche Analyse von Code dh. Lesbarkeit. Auch kein Sprachhack.

Metamorphose
quelle
1
"Da der Leser des Assert-Fehlers die Datei und die Zeile trotzdem aus der Fehlermeldung nachschlagen wird" - nur wenn sie fleißig sind.
Jason S
Nur wenn sie den Fehler beheben wollen, meinen Sie ... was für ein dummer Kommentar
Metamorphose
1
Nein. Je einfacher Sie es den Menschen machen, das Problem zu erkennen, desto wahrscheinlicher werden sie Maßnahmen ergreifen.
Jason S
Achselzucken Nicht einverstanden.
Metamorphose
2

assert ist eine Makro- / Funktionskombination. Sie können Ihre eigene Makro / Funktion, mit definieren __FILE__, __BASE_FILE__, __LINE__etc, mit Ihrer eigenen Funktion, nimmt eine benutzerdefinierte Nachricht

Merlyn Morgan-Graham
quelle
-6

Fügen Sie für vc den folgenden Code in assert.h hinzu:

#define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )
Jichao
quelle
11
Das Ändern der Header Ihres Compilers ist eine schlechte Idee.
Ross Ridge