Adicionar mensagens personalizadas em afirmar?

129

Existe uma maneira de adicionar ou editar a mensagem lançada por assert? Eu gostaria de usar algo como

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

Então, o compilador adiciona linha , hora e assim por diante ...

É possível?

Killrazor
fonte
Você pode definir uma macro, como este .
HelloGoodbye

Respostas:

240

Um truque que eu já vi por aí é usar o &&operador. Como um ponteiro "é verdadeiro" se não for nulo, você pode fazer o seguinte sem alterar a condição:

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

Como assertmostra a condição que falhou, ele também exibirá sua mensagem. Se não for suficiente, você pode escrever sua própria myAssertfunção ou macro que exibirá o que quiser.

zneak
fonte
27
Outra opção é reverter os operandos e usar o operador de vírgula. Você precisa parênteses extras para que a vírgula não é tratado como um delimitador entre os argumentos:assert(("A must be equal to B", a == b));
Keith Thompson
3
Seria bom, no entanto, ser capaz de imprimir os valores das variáveis, como em: #assert(a == b && "A (" << A << ") is not equal to B (" << B << ")");
31313 de Frank
7
@Frank, printfretorna um valor diferente de zero se ele imprimiu alguma coisa, para que você possa fazer algo como assert(a == b && printf("a (%i) is not equal to b (%i)", a, b)), embora nesse momento provavelmente deva escrever seu próprio invólucro de declaração.
zneak
1
Código ruim! Eu não entendo isso! Se a == b for falso, a expressão -e também deve ser falsa e, portanto, a cadeia de caracteres não deve ser avaliada.
Ragnarius 7/11
1
@TUIlover, não é assim que os literais de string C funcionam; são constantes em tempo de compilação e seu uso nesse contexto é trivialmente otimizado. Não há custo de tempo de execução.
Zneak 9/08/19
45

Outra opção é reverter os operandos e usar o operador de vírgula. Você precisa de parênteses extras para que a vírgula não seja tratada como um delimitador entre os argumentos:

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

(isso foi copiado dos comentários acima, para melhor visibilidade)

Andrei Bozantan
fonte
3
Esta é uma grande aproximação, com uma questão pequena, ele irá mostrar "aviso: operando esquerdo do operador vírgula não tem efeito" quando compilado em g ++ com `valor -Wunused
v010dya
1
ou com uma macro: #ifndef m_assert #define m_assert (expr, msg) assert ((msg, expr)) #endif
Szymon Marczak
O uso de um wrapper de macro permite evitar o aviso do gcc:#define m_assert(expr, msg) assert(( (void)(msg), (expr) ))
Jander 24/09
25

Aqui está minha versão da macro assert, que aceita a mensagem e imprime tudo de maneira clara:

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

Agora você pode usar isso

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

E em caso de falha, você receberá uma mensagem como esta:

Falha na declaração: MyFunction: requer argumento não nulo

Esperado: ptr! = Nullptr

Origem: C: \ MyProject \ src.cpp, linha 22

Agradável e limpo, fique à vontade para usá-lo em seu código =)

Eugene Magdalits
fonte
Agradável. Muito útil
Killrazor
Estou um pouco confuso. O #Expr é tratado como uma string para substituição direta? Qual é a diferença entre #Expr e Expr?
Minh Tran
@MinhTran Vamos supor que sua condição de afirmação seja x == y. Em seguida, o Expr se expandirá para if( !(x == y))e é aí que a condição será verificada, e #Expr se expandirá para a string literal "x == y", que depois colocamos na mensagem de erro.
Eugene Magdalits 14/10
Infelizmente, esta solução causa um comportamento indefinido devido ao uso de identificadores reservados.
Lembre
19
BOOST_ASSERT_MSG(expre, msg)

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

Você pode usá-lo diretamente ou copiar o código do Boost. Observe também que a declaração Boost é apenas um cabeçalho; portanto, você pode pegar esse único arquivo se não quiser instalar todo o Boost.

Zero
fonte
@Jichao, o que você quer dizer com implementar a interface assert?
Tarc 22/08/16
7

Como a resposta do zneak envolve um pouco o código, uma abordagem melhor é apenas comentar o texto da string que você está falando. ou seja:

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

Como o leitor do erro de declaração procurará o arquivo e a linha de qualquer maneira a partir da mensagem de erro, ele verá a explicação completa aqui.

Porque, no final do dia, isso:

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

lê melhor que isso:

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

em termos de análise humana de código ou seja. legibilidade. Também não é um hack de idioma.

metamorfose
fonte
1
"Como o leitor do erro de declaração procurará o arquivo e a linha de qualquer maneira a partir da mensagem de erro", somente se for diligente.
Jason S
Somente se eles querem consertar o erro, você quer dizer ... que comentário bobo
metamorfose
1
Não. Quanto mais fácil você entender o problema, maior a probabilidade de que elas tomem medidas.
Jason S
encolher de ombros Discordo.
metamorfose
2

assert é uma combinação de macro / função. você pode definir a sua própria macro / função, usando __FILE__, __BASE_FILE__, __LINE__etc, com a sua própria função que recebe uma mensagem personalizada

Merlyn Morgan-Graham
fonte
-6

Para vc, adicione o seguinte código em assert.h,

#define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )
Jichao
fonte
11
Modificar os cabeçalhos do seu compilador é uma má ideia.
Ross cume