Aggiungi messaggi personalizzati in assert?


129

C'è un modo per aggiungere o modificare il messaggio generato da assert? Mi piacerebbe usare qualcosa di simile

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

Quindi, il compilatore aggiunge riga , tempo e così via ...

È possibile?


È possibile definire una macro, in questo modo .
Ciao Arrivederci

Risposte:


240

Un trucco che ho visto in giro è usare l' &&operatore. Poiché un puntatore "è vero" se non è null, è possibile effettuare le seguenti operazioni senza alterare la condizione:

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

Poiché assertmostra la condizione non riuscita, verrà visualizzato anche il tuo messaggio. Se non è abbastanza, puoi scrivere la tua myAssertfunzione o macro che visualizzerà quello che vuoi.


27
Un'altra opzione è quella di invertire gli operandi e utilizzare l'operatore virgola. Hai bisogno di parentesi extra quindi la virgola non viene trattata come delimitatore tra gli argomenti:assert(("A must be equal to B", a == b));
Keith Thompson

3
Sarebbe bello, però, poter stampare i valori delle variabili, come in:assert(a == b && "A (" << A << ") is not equal to B (" << B << ")");
Frank

7
@Frank, printfrestituisce un valore diverso da zero se viene stampato qualcosa, quindi potresti fare qualcosa del genere assert(a == b && printf("a (%i) is not equal to b (%i)", a, b)), anche se a quel punto dovresti probabilmente scrivere il tuo wrapper di asserzione.
zneak,

1
Codice errato! Non lo capisco! Se a == b è falso, anche l'espressione e dovrebbe essere falsa e, pertanto, la stringa non dovrebbe essere valutata.
Ragnarius,

1
@TUIlover, non è così che funzionano i letterali di stringhe C; sono costanti di compilazione e il loro uso in questo contesto è banalmente ottimizzato. Non ci sono costi di runtime.
zneak,

45

Un'altra opzione è quella di invertire gli operandi e utilizzare l'operatore virgola. Hai bisogno di parentesi extra in modo che la virgola non sia trattata come delimitatore tra gli argomenti:

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

(questo è stato copiato dai commenti sopra, per una migliore visibilità)


3
Questo è un ottimo approccio, con un piccolo problema, mostrerà "avvertimento: l'operando di sinistra dell'operatore virgola non ha alcun effetto" quando compilato in g ++ con `-Wunused-value
v010dya

1
o con una macro: #ifndef m_assert #define m_assert (expr, msg) assert ((msg, expr)) #endif
Szymon Marczak

L'uso di un macro wrapper ti consente di evitare l'avvertimento gcc:#define m_assert(expr, msg) assert(( (void)(msg), (expr) ))
Jander

25

Ecco la mia versione di assert macro, che accetta il messaggio e stampa tutto in modo chiaro:

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

Ora puoi usare questo

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

E in caso di fallimento riceverai un messaggio come questo:

Asserzione non riuscita: MyFunction: richiede un argomento non nullo

Previsto: ptr! = Nullptr

Fonte: C: \ MyProject \ src.cpp, riga 22

Bello e pulito, sentiti libero di usarlo nel tuo codice =)


Ben fatto. Molto utile
Killrazor,

Sono un po 'confuso. #Expr è trattato come una stringa per la sostituzione diretta? Qual è la differenza tra #Expr ed Expr?
Minh Tran,

@MinhTran Supponiamo che la tua condizione di asserzione sia x == y. Quindi Expr si espanderà if( !(x == y))e qui è dove viene verificata la condizione, e #Expr si espanderà in stringa letterale "x == y", che quindi inseriremo nel messaggio di errore.
Eugene Magdalits,

Sfortunatamente, questa soluzione provoca un comportamento indefinito a causa dell'utilizzo di identificatori riservati.
Ricorda Monica il


7

Dato che la risposta di zneak contorna un po 'il codice, un approccio migliore è semplicemente commentare il testo della stringa di cui stai parlando. vale a dire .:

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

Poiché il lettore dell'errore assert cercherà comunque il file e la riga dal messaggio di errore, vedrà la spiegazione completa qui.

Perché, alla fine della giornata, questo:

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

legge meglio di così:

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

in termini di analisi umana del codice, ad es. leggibilità. Inoltre non è un hack di lingua.


1
"Dato che il lettore dell'errore assert cercherà il file e la riga comunque dal messaggio di errore", solo se sono diligenti.
Jason S,

Solo se vogliono correggere il bug vuoi dire ... che commento sciocco
metamorfosi

1
No. Più è facile per le persone vedere il problema, più è probabile che agiranno.
Jason S,

scrollata di spalle Non sono d'accordo.
metamorfosi,

2

assert è una combinazione macro / funzione. è possibile definire il proprio macro / funzione, utilizzando __FILE__, __BASE_FILE__, __LINE__ecc, con la propria funzione che prende un messaggio personalizzato


-6

Per vc, aggiungi il seguente codice in assert.h,

#define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )

11
Modificare le intestazioni del compilatore non è una buona idea.
Ross Ridge,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.