Percebo que, embora a pergunta não tenha um rótulo de idioma, provavelmente está falando implicitamente sobre "idiomas do café". Mas apenas por uma questão de integridade, gostaria de mencionar o consenso aparente um tanto divergente no mundo C ++.
Normalmente, há três coisas em que os programadores de C ++ estarão interessados:
- Ele terá zero de sobrecarga nas compilações otimizadas? (Ou seja, pode ser "compilado"?)
- Posso usá-lo para interceptar um depurador no ponto em que o erro foi detectado?
- Posso usá-lo para relatar problemas de funções declaradas
noexcept
?
No passado, eu abordei o primeiro problema escrevendo código como este
int
factorial(const int n)
{
if (CHECK_ARGS)
{
if (n < 0)
throw std::invalid_argument {"n < 0"};
}
int fac = 1;
for (int i = 2; i <= n; ++i)
fac *= i;
return fac;
}
em que d CHECK_ARGS
é #define
uma constante em tempo de compilação para que o compilador possa eliminar completamente todo o código de verificação de argumentos em compilações otimizadas. (Não estou dizendo que compilar as verificações é uma coisa boa em geral, mas acredito que um usuário deve ter a opção de compilá-las.)
Ainda gosto dessa solução: o código de verificação de argumentos é claramente visível, agrupado no if
. No entanto, o segundo e o terceiro problema não são resolvidos por isso. Portanto, agora estou mais inclinado a usar uma assert
macro para verificação de argumentos.
Os padrões de codificação Boost concordam com isso:
E os erros do programador?
Como desenvolvedor, se violei uma pré-condição de uma biblioteca que estou usando, não quero que a pilha seja desenrolada. O que eu quero é um dump principal ou equivalente - uma maneira de inspecionar o estado do programa no ponto exato em que o problema foi detectado. Isso geralmente significa assert()
ou algo parecido.
Houve uma palestra muito interessante proferida por John Lakos no CppCon'14 intitulada Programação Defensiva Feita Corretamente ( parte 1 , parte 2 ). Na primeira parte de sua palestra, ele discute a teoria dos contratos e o comportamento indefinido. Na segunda parte, ele apresenta o que considero uma proposta muito boa para verificação sistemática de argumentos. Em essência, ele propõe macros de asserção que permitem ao usuário selecionar quanto de orçamento (em termos de utilização da CPU) ela deseja doar à biblioteca para verificação de argumentos e fazer com que a biblioteca faça uso racional desse orçamento. Além disso, o usuário também pode instalar uma função global de tratamento de erros que será chamada caso um contrato quebrado seja detectado.
Quanto ao aspecto de uma função ser privada, não creio que isso signifique que nunca deveremos verificar se seus argumentos. Podemos confiar mais em nosso próprio código para não violar o contrato de uma função interna, mas também sabemos que também não somos perfeitos. A verificação de argumentos nas funções internas é tão útil para detectar nossos próprios erros quanto nas funções públicas para detectar erros no código do cliente.