Uma vez fui avisado de que um programa C ++ deveria capturar todas as exceções. O raciocínio dado na época era essencialmente o de programas que permitem que exceções surjam fora de main()
um estado zumbi estranho. Foi-me dito isso há vários anos e, em retrospecto, acredito que o fenômeno observado deveu-se à geração prolongada de despejos de núcleo excepcionalmente grandes do projeto em questão.
Na época, isso parecia bizarro, mas convincente. Era totalmente absurdo que o C ++ deveria "punir" os programadores por não capturar todas as exceções, mas as evidências diante de mim pareciam confirmar isso. Para o projeto em questão, os programas que lançaram exceções não capturadas pareciam entrar em um estado zumbi estranho - ou, como suspeito que a causa fosse agora, um processo no meio de um despejo de núcleo indesejado é extraordinariamente difícil de parar.
(Para quem se pergunta por que isso não era mais óbvio na época: o projeto gerou uma grande quantidade de saída em vários arquivos de vários processos que efetivamente obscureceram qualquer tipo de aborted (core dumped)
mensagem e, nesse caso em particular, o exame post mortem dos core dumps não foi É uma técnica importante de depuração, para que os despejos principais não sejam levados em consideração.Os problemas com um programa geralmente não dependem do estado acumulado de muitos eventos ao longo do tempo por um programa de longa duração, mas das entradas iniciais de um programa de curta duração (< 1 hora), por isso era mais prático executar novamente um programa com as mesmas entradas de uma compilação de depuração ou em um depurador para obter mais informações.)
Atualmente, não tenho certeza se existe alguma grande vantagem ou desvantagem de capturar exceções apenas com o objetivo de impedir a saída de exceções main()
.
A pequena vantagem em que posso pensar em permitir que as exceções surjam main()
é que ele faz com que o resultado std::exception::what()
seja impresso no terminal (pelo menos nos programas compilados pelo gcc no Linux). Por outro lado, isso é fácil de obter, capturando todas as exceções derivadas std::exception
e imprimindo o resultado std::exception::what()
e, se é desejável imprimir uma mensagem de uma exceção que não deriva std::exception
, ela deve ser capturada antes de sair main()
para imprimir a mensagem.
A modesta desvantagem em que consigo pensar em permitir que exceções passem despercebidas main()
é que podem ser gerados despejos de núcleo indesejados. Para um processo que utiliza uma grande quantidade de memória, isso pode ser um incômodo e o controle do comportamento de dumping do núcleo de um programa requer chamadas de função específicas do SO. Por outro lado, se um dump e saída de núcleo forem desejados, isso poderá ser alcançado a qualquer momento chamando std::abort()
e uma saída sem dump de núcleo poderá ser alcançada a qualquer momento chamando std::exit()
.
Curiosamente, acho que nunca vi a what(): ...
mensagem padrão impressa por um programa amplamente distribuído ao travar.
Quais são os argumentos fortes, a favor ou contra, para permitir que as exceções do C ++ passem despercebidas main()
?
Edit: Existem muitas perguntas gerais sobre manipulação de exceções neste site. Minha pergunta é especificamente sobre exceções de C ++ que não podem ser tratadas e foram feitas até o fim main()
- talvez uma mensagem de erro possa ser impressa, mas é um erro de parada imediatamente exibido.
fonte
Respostas:
Um problema ao deixar as exceções passarem do ponto principal é que o programa terminará com uma chamada para a
std::terminate
qual o comportamento padrão é chamarstd::abort
. É apenas uma implementação definida se o desenrolamento da pilha for feito antes da chamada,terminate
para que seu programa possa terminar sem chamar um único destruidor! Se você tem algum recurso que realmente precisava ser restaurado por uma chamada de destruidor, está em apuros ...fonte
std::abort
, não e, portanto, afirmações com falha também não. Também vale a pena observar que, nos sistemas operacionais modernos, o próprio sistema operacional limpará muitos recursos (memória, identificadores de arquivos, ...) vinculados ao ID do processo. Por fim, eu também estava sugerindo "Defense in Depth": não é confiável esperar que todos os outros processos sejam à prova de erros e sempre libere os recursos que adquiriram (sessões, finalize bem a gravação de arquivos, ...) que você deve planejar. -lo ...WM_POWERBROADCAST
mensagem. Isso funciona apenas se o computador estiver usando bateria (se você estiver usando um laptop ou um no-break).O principal motivo para não deixar escapar exceções
main
é porque, caso contrário, você perde toda a possibilidade de controlar como o problema é relatado aos usuários.Para um programa que não se destina a ser usado por muito tempo ou amplamente distribuído, pode ser aceitável que erros inesperados sejam relatados da maneira que o sistema operacional decidir fazer (por exemplo, mostrando uma caixa de diálogo de erro na sua cara no Windows )
Para os programas que você vende, ou que são fornecidos ao público em geral por uma organização que tem uma reputação a defender, geralmente é uma idéia melhor relatar de maneira agradável que você encontrou um problema inesperado e tentar economizar o máximo possível. dados do usuário quanto possível. Não perder meio dia de trabalho do usuário nem travar inesperadamente, mas encerrar sem graça é geralmente muito melhor para a reputação da empresa do que a alternativa.
fonte
main()
tem muito a ver com o sistema operacional? O sistema operacional pode não saber nada de C ++. Meu palpite era que ele é determinado pelo código que o compilador insere em algum lugar do programa.TL; DR : O que a especificação diz?
Um desvio técnico ...
Quando uma exceção é lançada e nenhum manipulador está pronto para isso:
std::terminate
é chamado, que por padrão abortaO último pode ser útil para erros muito pouco frequentes (porque reproduzi-los é um processo que desperdiça tempo).
Capturar todas as exceções ou não é, em última análise, uma questão de especificação:
Para qualquer programa de produção, isso deve ser especificado e você deve seguir a especificação (e talvez argumentar que ela foi alterada).
Para programas reunidos rapidamente e serem usados apenas por pessoas técnicas (você, seus colegas de equipe), tudo bem. Eu recomendo deixá-lo travar e configurar o ambiente para obter um relatório ou não, dependendo de suas necessidades.
fonte
Uma exceção que você captura oferece a oportunidade de imprimir uma boa mensagem de erro ou até mesmo tentar se recuperar do erro (possivelmente apenas reiniciando o aplicativo).
No entanto, em C ++, uma exceção não contém informações sobre o estado do programa quando foi lançado. Se você o capturar, todo esse estado será esquecido, enquanto que se você deixar o programa travar, o estado ainda estará lá e poderá ser lido no dump do programa, facilitando a depuração.
Portanto, é uma troca.
fonte
No momento em que você souber que precisa abortar, vá em frente e ligue
std::terminate
já para reduzir qualquer dano adicional.Se você sabe que pode relaxar com segurança, faça isso. Lembre-se de que o desenrolamento de pilha não é garantido quando uma exceção nunca é capturada;
Se você puder relatar / registrar com segurança o erro melhor do que o sistema por si só, vá em frente.
Mas certifique-se de não piorar inadvertidamente as coisas ao fazê-lo.
Muitas vezes, é tarde demais para salvar dados quando você detecta um erro irrecuperável, embora isso dependa de um erro específico.
De qualquer forma, se o seu programa foi escrito para recuperação rápida, apenas matá-lo pode ser a melhor maneira de finalizá-lo, mesmo que seja apenas um desligamento normal.
fonte
Falhando graciosamente é uma coisa boa na maioria das vezes - mas existem trade-offs. Às vezes, é uma boa coisa travar. Devo mencionar que estou pensando principalmente na depuração em grande escala. Para um programa simples - embora isso ainda possa ser útil, não é nem de longe tão útil quanto um programa muito complexo (ou vários programas complexos interagindo).
Você não quer travar em público (embora isso seja realmente inevitável - os programas travam e um programa sem travamento, matematicamente verificável, não é o que estamos falando aqui). Pense em Bill Gates BSODing no meio de uma demonstração - ruim, certo? No entanto, podemos tentar descobrir por que caímos e não caímos novamente da mesma maneira.
Ativei um recurso do Relatório de Erros do Windows que cria despejos de falhas locais em exceções não tratadas. É maravilhoso saber se você possui os arquivos de símbolos associados à sua compilação (travando). Curiosamente, porque configurei essa ferramenta, quero travar mais - porque aprendo com cada travamento. Então eu posso consertar os bugs e travar menos.
Então, por enquanto, quero que meus programas cheguem ao topo e falhem. No futuro, talvez eu queira comer todas as minhas exceções, mas não se puder fazê-las funcionar para mim.
Você pode ler mais sobre despejos de falhas locais aqui se estiver interessado: Coletando despejos no modo de usuário
fonte
Por fim, se uma exceção aparecer acima de main (), ela travará seu aplicativo e, em minha opinião, um aplicativo nunca deve travar. Se não há problema em travar um aplicativo em um só lugar, por que não em qualquer lugar? Por que se preocupar com o tratamento de exceções? (Sarcasmo, realmente não estou sugerindo isso ...)
Você pode ter uma tentativa / captura global que imprime uma mensagem elegante informando ao usuário que ocorreu um erro irrecuperável e que eles precisam enviar um e-mail para a TI sobre isso ou algo semelhante, mas a falha é completamente não profissional e inaceitável. É trivial prevenir e você pode informar um usuário sobre o que acabou de acontecer e como consertar as coisas para que isso não aconteça novamente.
Bater é estritamente hora amadora.
fonte
main
. Se você não sabe como lidar com isso, fingir que você é obviamente um bug realmente horrível.