Tuesday, September 16, 2008

Você já deve ter lido por aí que usar ASSERT e VERIFY nos seus programas garante um aumento da qualidade do software substancialmente.

ASSERT é uma macro que só faz algo de útil quando o código é compilado com um determinado #define (_DEBUG, NDEBUG, etc) e este possui um valor conhecido (ou simplesmente está (in)definido).

Um ponto 'perigoso' com relação ao ASSERT é que nas versões de release, o argumento recebido é completamente ignorado. Um exemplo de ASSERT (adaptado de afx.h):

#ifdef _DEBUG
#define ASSERT(x) ((void) (x) || !::AfxDebugBreak())
#else
#define ASSERT(x) ((void)0)
#endif

Na versão de debug, se avaliar a expressão 'x' resultar num valor considerado falso o programa será interrompido. Porém na versão de release, a expressão 'x' não é sequer avaliada!

Outro fato bastante importante é prestar atenção e usar somente uma expressão por cláusula de ASSERT, para facilitar no diagnóstico do erro. Se você tiver 3 condições num ASSERT e ele falhar, qual das três teve valor falso? Evite tiros no pé gratuitos!

Já a macro VERIFY possui a característica de não ignorar seus argumentos em tempo de execução:

#ifdef _DEBUG
#define VERIFY(x) ASSERT(x)
#else
#define VERIFY(x) ((void)(x))
#endif

Ela mantém seu código instrumentado e ao mesmo tempo permite construções mais consisas, como por exemplo, ao invés de checar duas vezes se o ponteiro é valido antes de usar:

ASSERT(p);
if (p) {
  printf("(%d, %d)\n", p->x, p->y);
}

usando VERIFY podemos fazer o teste na mesma linha do if:

if (VERIFY(p)) {
  printf("(%d, %d)\n", p->x, p->y);
}

A meu ver tornando o código mais legível e igualmente instrumentado!

Podemos inclusive inverter a lógica (pra quem prefere checar assertivas de entrada):

if (!VERIFY(p)) {
  goto error;
}

ASSERT e VERIFY são ferramentas indispensáveis para escrever código com menos bugs que a média :)

posted on Tuesday, September 16, 2008 5:15:52 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]