c++ - sinonimo - mormente




É asseverar o mal? (14)

Admito ter usado as afirmações sem considerar o relatório de erros adequado. No entanto, isso não significa que eles sejam muito úteis quando usados ​​corretamente.

Eles são especialmente úteis se você quiser seguir o princípio "Crash Early". Por exemplo, suponha que você esteja implementando um mecanismo de contagem de referência. Em determinados locais do seu código, você sabe que o relato deve ser zero ou um. E também suponha que, se o relato estiver errado, o programa não irá falhar imediatamente, mas durante o próximo loop de mensagens, será difícil descobrir por que as coisas deram errado. Uma afirmação teria sido útil na detecção do erro mais próximo de sua origem.

Os criadores da linguagem Go write :

Go não fornece afirmações. Eles são inegavelmente convenientes, mas nossa experiência tem sido que os programadores os utilizam como uma muleta para evitar pensar em lidar com erros e reportar corretamente. O tratamento adequado de erros significa que os servidores continuam operando após erros não fatais, em vez de falhas. O relatório de erros apropriado significa que os erros são diretos e diretos, evitando que o programador interprete um grande traço de falha. Erros precisos são particularmente importantes quando o programador que vê os erros não está familiarizado com o código.

Qual é a sua opinião sobre isso?


As afirmações não são más, mas podem ser facilmente mal utilizadas. Eu concordo com a afirmação de que "afirmações são frequentemente usadas como uma muleta para evitar pensar sobre manipulação de erros e relatórios apropriados". Eu já vi isso com bastante frequência.

Pessoalmente, eu gosto de usar asserções porque elas documentam suposições que eu poderia ter feito ao escrever meu código. Se essas suposições forem quebradas enquanto se mantém o código, o problema pode ser detectado durante o teste. No entanto, eu faço questão de retirar todas as afirmações do meu código ao fazer uma compilação de produção (ou seja, usando #ifdefs). Ao eliminar as afirmações na produção, elimino o risco de alguém usá-las como uma muleta.

Há também outro problema com afirmações. As asserções são verificadas apenas em tempo de execução. Mas muitas vezes é o caso que o cheque que você gostaria de executar poderia ter sido executado em tempo de compilação. É preferível detectar um problema em tempo de compilação. Para programadores C ++, o boost fornece BOOST_STATIC_ASSERT, o que permite que você faça isso. Para programadores C, este artigo ( texto do link ) descreve uma técnica que pode ser usada para executar asserções em tempo de compilação.

Em resumo, a regra geral que eu sigo é: Não use declarações em uma construção de produção e, se possível, use apenas asserções para coisas que não podem ser verificadas em tempo de compilação (isto é, devem ser verificadas em tempo de execução).


Eles devem ser usados ​​para detectar erros no programa. Não é má entrada do usuário.

Se usado corretamente, eles não são maus.


Eu comecei recentemente adicionando algumas afirmações ao meu código, e é assim que eu tenho feito isso:

Eu divido mentalmente meu código em código de limite e código interno. Código de limite é o código que manipula a entrada do usuário, lê arquivos e obtém dados da rede. Neste código, eu solicito entrada em um loop que só sai quando a entrada é válida (no caso de entrada interativa do usuário) ou em exceções no caso de dados corrompidos de arquivos / redes irrecuperáveis.

Código interno é tudo mais. Por exemplo, uma função que define uma variável na minha classe pode ser definida como

void Class::f (int value) {
    assert (value < end);
    member = value;
}

e uma função que recebe entrada de uma rede pode ser lida como tal:

void Class::g (InMessage & msg) {
    int const value = msg.read_int();
    if (value >= end)
        throw InvalidServerData();
    f (value);
}

Isso me dá duas camadas de cheques. Qualquer coisa em que os dados são determinados em tempo de execução sempre recebe uma exceção ou tratamento de erros imediato. No entanto, essa verificação extra em Class::f com a instrução assert significa que, se algum código interno chamar Class::f , ainda tenho uma verificação de sanidade. Meu código interno pode não passar um argumento válido (porque eu posso ter calculado o value de algumas séries complexas de funções), então eu gosto de ter a asserção na função de configuração para documentar que, independentemente de quem está chamando a função, o value não deve ser maior igual ou igual a end .

Isso parece se encaixar no que estou lendo em alguns lugares, que as afirmações devem ser impossíveis de violar em um programa que funcione bem, enquanto as exceções devem ser para casos excepcionais e errôneos que ainda são possíveis. Porque, em teoria, eu estou validando todas as entradas, não deveria ser possível para minha afirmação ser acionada. Se for, meu programa está errado.


Eu não gosto de afirmar intensamente. Eu não iria tão longe quanto dizer que eles são malvados.

Basicamente, uma afirmação fará o mesmo que uma exceção não verificada, a única exceção é que a afirmação (normalmente) não deve ser mantida para o produto final.

Se você construir uma rede de segurança para si mesmo durante a depuração e a construção do sistema, por que negaria essa rede de segurança para o seu cliente, seu suporte técnico ou qualquer pessoa que consiga usar o software que você está construindo atualmente? Use exceções exclusivamente para afirmações e situações excepcionais. Ao criar uma hierarquia de exceções apropriada, você será capaz de discernir muito rapidamente um do outro. Exceto que desta vez a afirmação permanece no lugar e pode fornecer informações valiosas em caso de falha que de outra forma seria perdida.

Então, eu entendo completamente os criadores do Go, removendo completamente as assertivas e forçando os programadores a usar exceções para lidar com a situação. Há uma explicação simples para isso, a exceção é apenas um mecanismo melhor para o trabalho por que ficar com as afirmações arcaicas?


Eu nunca uso assert (), exemplos geralmente mostram algo como isto:

int* ptr = new int[10];
assert(ptr);

Isso é ruim, eu nunca faço isso, e se o meu jogo está alocando um monte de monstros? Por que eu deveria travar o jogo, em vez disso você deve lidar com os erros graciosamente, então faça algo como:

CMonster* ptrMonsters = new CMonster[10];
if(ptrMonsters == NULL) // or u could just write if(!ptrMonsters)
{
    // we failed allocating monsters. log the error e.g. "Failed spawning 10 monsters".
}
else
{
    // initialize monsters.
}

Eu senti vontade de chutar o autor na cabeça quando vi isso.

Eu uso afirma o tempo todo em código e, eventualmente, substituo todos eles quando escrevo mais código. Eu os uso quando não escrevo a lógica necessária e quero ser alertado quando me deparo com o código, em vez de escrever uma exceção que será excluída à medida que o projeto se aproxima da conclusão.

Exceções também se misturam com o código de produção mais facilmente que eu não gosto. Uma afirmação é mais fácil de notar do que throw new Exception("Some generic msg or 'pretend i am an assert'");


Isso surge muito, e acho que um problema que torna as defesas de afirmações confusas é que elas são frequentemente baseadas na verificação de argumentos. Então, considere este exemplo diferente de quando você pode usar uma afirmação:

build-sorted-list-from-user-input(input)

    throw-exception-if-bad-input(input)

    ...

    //build list using algorithm that you expect to give a sorted list

    ...

    assert(is-sorted(list))

end

Você usa uma exceção para a entrada porque espera obter uma entrada ruim às vezes. Você afirma que a lista está organizada para ajudá-lo a encontrar um erro no seu algoritmo, o qual, por definição, você não espera. A declaração está somente na compilação de depuração, portanto, mesmo que a verificação seja cara, você não se importará em fazer isso em todas as invocações da rotina.

Você ainda precisa testar seu código de produção, mas isso é uma forma diferente e complementar de garantir que seu código esteja correto. Os testes de unidade garantem que sua rotina atinja sua interface, enquanto as asserções são uma maneira mais detalhada de garantir que sua implementação esteja fazendo exatamente o que você espera.


Não tanto mal quanto geralmente contraproducente. Há uma separação entre a verificação permanente de erros e a depuração. Assert faz as pessoas pensarem que toda a depuração deve ser permanente e causa problemas enormes de legibilidade quando muito usada. O tratamento permanente de erros deve ser melhor do que o necessário, e como a afirmação causa seus próprios erros, é uma prática bastante questionável.


Não, não há nada de errado em assert , desde que você a use como pretendido.

Isto é, é para capturar casos que "não podem acontecer", durante a depuração, ao contrário do tratamento de erros normal.

  • Assert: uma falha na própria lógica do programa.
  • Manipulação de Erros: Uma entrada incorreta ou estado do sistema não devido a um erro no programa.

Por essa lógica, os pontos de ruptura também são maus.

As afirmações devem ser usadas como ajuda de depuração e nada mais. "Mal" é quando você tenta usá-las em vez de lidar com erros.

As afirmações estão aí para ajudá-lo, o programador, detectar e corrigir problemas que não devem existir e verificar se suas suposições permanecem verdadeiras.

Eles não têm nada a ver com o tratamento de erros, mas, infelizmente, alguns programadores abusam deles e depois os declaram "malvados".


Resposta curta: Não, acredito que afirmações podem ser úteis


Sim, as afirmações são más.

Muitas vezes eles são usados ​​em locais onde o tratamento de erros adequado deve ser usado. Acostume-se a escrever o tratamento de erros de qualidade de produção desde o início!

Geralmente eles ficam no caminho de escrever testes de unidade (a menos que você escreva uma declaração personalizada que interaja com o seu equipamento de teste). Geralmente, isso ocorre porque eles são usados ​​quando o tratamento adequado de erros deve ser usado.

Na maioria das vezes, eles são compilados a partir de compilações de lançamento, o que significa que nenhum de seus "testes" está disponível quando você está executando o código que você realmente libera; dado que em situações multi-thread os piores problemas muitas vezes só aparecem no código de release, isso pode ser ruim.

Às vezes eles são uma muleta para projetos quebrados; ou seja, o design do código permite que um usuário o chame de uma forma que não deveria ser chamada e a declaração "evita" isso. Corrigir o design!

Eu escrevi sobre isso mais no meu blog em 2005 aqui: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html


assert é muito útil e pode poupar muito retorno quando ocorrem erros inesperados ao parar o programa logo nos primeiros sinais de problemas.

Por outro lado, é muito fácil abusar de assert .

int quotient(int a, int b){
    assert(b != 0);
    return a / b;
}

A versão correta correta seria algo como:

bool quotient(int a, int b, int &result){
    if(b == 0)
        return false;

    result = a / b;
    return true;
}

Então ... a longo prazo ... no quadro geral ... devo concordar que a assert pode ser abusada. Eu faço isso o tempo todo.







assert