Existem casos reais de C ++ sem exceções? [fechadas]

40

Em Quando usar C sobre C ++ e C ++ sobre C? há uma declaração errada. para codificar exceções de tamanho / C ++:

Jerry responde (entre outros pontos):

(...) tende a ser mais difícil produzir executáveis ​​verdadeiramente minúsculos com C ++. Em sistemas realmente pequenos, você raramente escreve muito código, e o (...)

ao qual perguntei por que isso seria, ao qual Jerry respondeu:

o principal é que o C ++ inclui manipulação de exceção, que (pelo menos geralmente) adiciona um mínimo ao tamanho do executável. A maioria dos compiladores permitirá que você desabilite o tratamento de exceções, mas quando você faz o resultado, não é mais C ++. (...)

o que realmente não duvido no nível técnico do mundo real.


Portanto, estou interessado (por curiosidade) em ouvir exemplos do mundo real em que um projeto escolheu C ++ como idioma e, em seguida, optou por desativar as exceções. (Não apenas "não use" exceções no código do usuário, mas desative-as no compilador, para que você não possa lançar ou capturar exceções.) Por que um projeto optou por fazê-lo (ainda usando C ++ e não C, mas não exceções) - quais são / foram as razões (técnicas)?


Adendo: Para aqueles que desejam elaborar suas respostas, seria bom detalhar como são tratadas as implicações das não-exceções:

  • Coleções STL ( vector, ...) não funcionam corretamente (falha na alocação não pode ser relatada)
  • new não pode jogar
  • Construtores não podem falhar
Martin Ba
fonte
11
JSF C ++. Jatos e exceções não se misturam.
Coder
11
Algumas informações esclarecedoras sobre problemas com exceções (e C ++ em geral) 250bpm.com/blog:4
Ambroz Bizjak
11
@AmbrozBizjak - vou citar o DeadMG de um comentário ao post para o qual você vincula: quote: "Parece-me que você não entende exceções". Concordo. O autor claramente fez uma bagunça no tratamento de exceções, olhando para os exemplos que ele dá nesse post.
Martin Ba

Respostas:

35

Quase todos os jogos de console disponíveis em C ++, com a exceção desativada, ainda hoje. Em vista disso, é a configuração padrão para compiladores C ++ direcionados para esses consoles. Às vezes, não é garantido que alguns recursos do C ++ funcionem corretamente nesses compiladores, como herança múltipla (por exemplo, estou pensando em um compilador padrão do console muito conhecido).


Além disso, outro exemplo é o SDK do hardware do Arduino, usando o gcc sem exceção, ativado no C ++, e outras coisas como nenhum STL fornecido .


Existem algumas razões técnicas, boas ou ruins, qualquer que seja, não é meu conselho, mas razões que ouvi:

  1. A maioria dos consoles são realmente sistemas embarcados com memória e tempo de processamento limitados. Talvez não seja tão verdadeiro em futuros consoles, mas os atuais ainda são bastante restritivos em comparação com um PC. Alguns consoles portáteis são mais difíceis de programar do que qualquer smartphone, por exemplo, o NDS. O recurso de exceção adiciona memória e um pouco de custo de velocidade, mesmo que você não o utilize. Você pode verificar a si mesmo, é verdade mesmo no PC.
  2. Jogos de vídeo no console não podem falhar. Eles devem ser testados de forma a evitar qualquer falha ou beco sem saída, qualquer showstoper. É por isso que os fabricantes de console solicitam que os jogos sejam fortemente verificados antes da publicação. Isso também significa que o gerenciamento de exceções adiciona um custo que não é realmente útil no caso de jogos de console. É melhor no smartphone, por exemplo, porque pode haver algumas maneiras de recuperação ou adicionar algum código para enviar o problema por e-mail. Plataformas fechadas como a maioria dos consoles não permitem isso. Portanto, um sistema de exceção não é realmente necessário, afinal. Você "apenas precisa fazê-lo funcionar corretamente". ;)
  3. O gerenciamento de exceções, quando você não permite erros e falhas, significa que você deve implementar uma estratégia de gerenciamento de erros. Esse sistema pode ser complexo o suficiente para fazer com que alguém trabalhe muito tempo para torná-lo útil. Os desenvolvedores de jogos não têm luxo para trabalhar em recursos que são considerados úteis em falhas ...
  4. O compilador não permite (ainda). Sim, isso acontece.

Acho que as exceções podem ser úteis, mesmo em jogos, mas é verdade que em jogos de console não é realmente útil.


Atualizar:

Estou adicionando outro exemplo surpreendente aqui: o LLVM / CLang não usa exceção nem RTTI pelos seguintes motivos :

Em um esforço para reduzir o código e o tamanho do executável, o LLVM não usa RTTI (por exemplo, dynamic_cast <>) ou exceções. Esses dois recursos de linguagem violam o princípio geral do C ++ de "você paga apenas pelo que usa", causando inchaço executável mesmo se nunca forem usadas exceções na base de código ou se o RTTI nunca for usado para uma classe. Por esse motivo, os desativamos globalmente no código.

Dito isto, o LLVM faz uso extensivo de uma forma de RTTI rolada à mão que usa modelos como isa <>, cast <> e dyn_cast <>. Este formulário de RTTI é opcional e pode ser adicionado a qualquer classe. Também é substancialmente mais eficiente que o dynamic_cast <>.

O CLang é conhecido por sua velocidade de compilação e erros explícitos, mas também é um compilador raro que possui um código realmente fácil de seguir.

Klaim
fonte
9
(3): você precisa de uma estratégia de gerenciamento de erros, não importa como, se você vai concordar com (2). Os problemas surgirão e o software do console precisará falhar com suavidade e graça. Não é óbvio para mim que evitar exceções facilita tudo isso.
David Thornley 10/10
6
Todos os ótimos exemplos de ambientes de tempo de execução rigidamente controlados, onde os erros são tratados adequadamente e não há eventos "excepcionais".
Patrick Hughes
11
@DavidThornley Porque você supõe que o sistema do console gerencie algum tipo de erro, mas não o fará. Bem, talvez seja no PS3 ou no XBox, mas isso não será permitido nos testes do construtor de console. De qualquer forma, o firmware dos consoles mais avançados não é tão flexível quanto você imagina. Um jogo de console precisa ter acesso a quase todo o hardware do console, para que você possa pensar em um jogo rodando em um console quase como se fosse também o "SO" do console ... quanto mais temos consoles poderosos, menos é verdade. Então, eu concordo com você que parece estranho para alguns casos.
Klaim
2
Essa é uma boa resposta, gostaria de acrescentar que, mesmo que uma exceção tenha ocorrido e apresentasse um erro ao usuário, o que ele deveria fazer com um D-pad? A única solução real para o usuário final é uma reinicialização.
anon
2
Eu removi o exemplo de insensatez porque alguém da equipe disse que, ao aumentar o NO EXCEPTION, não significa "nenhum uso de exceção", mas "nenhuma exceção às regras". Veja permalink.gmane.org/gmane.comp.lib.boost.devel/231377
Klaim
9

Jerry disse: ... o resultado não é mais C ++ , enquanto minha metáfora é que é claramente C ++, apenas um dialeto ligeiramente diferente porque os programas utilizam outras formas, convenções e estilos de escrita.

Aqui estão meus principais motivos para desativá-los:

Compatibilidade binária

O cruzamento de fronteiras de idioma e tradução não é universalmente bem definido ou indefinido. Se você deseja garantir que seu programa opere no domínio do comportamento definido, será necessário colocar em quarentena as exceções nos pontos de saída do módulo.

Tamanho do executável

Aqui estão os tamanhos binários de um programa livre de exceção que escrevi, construído sem e com as exceções ativadas:

Sem exceções:

  • executável + dependências: 330
  • executável final separado (compilação do release): 37

Com exceções:

  • executável + dependências: 380
  • executável com remoção final (compilação do release): 44

Lembrete: é uma coleção de bibliotecas e programas que contêm zero jogadas / capturas. A bandeira compilador faz permitir exceções na biblioteca padrão do C ++. Portanto, o custo no mundo real é superior a 19% visto neste exemplo.

Compilador: apple gcc4.2 + llvm. Tamanhos em MB.

Rapidez

Apesar do termo "exceções de custo zero", elas ainda adicionam alguma sobrecarga, mesmo quando nada é lançado. No caso acima, é um programa crítico de desempenho (processamento, geração, apresentação, conversões de sinais, com grandes conjuntos de dados / sinais etc.). Exceções não são um recurso necessário neste design, enquanto o desempenho é muito importante.

Correção do programa

Parece um motivo estranho ... Se jogar não é uma opção, você deve escrever programas relativamente rigorosos, corretos e bem testados para garantir que seu programa seja executado corretamente e que os clientes usem as interfaces corretamente (se você me der um argumento ruim ou não) Para verificar um código de erro, você merece UB). O resultado? A qualidade da implementação melhora muito e os problemas são corrigidos rapidamente.

Simplicidade

As implementações de manipulação de exceção nem sempre são atualizadas. Eles também adicionam muita complexidade, porque uma implementação pode ter muitas sequências de saída. É mais simples ler e manter programas altamente complexos quando eles usam um pequeno conjunto de estratégias de saída bem definidas e digitadas, que passam por bolhas e são tratadas pelo cliente. Em outros casos, as implementações podem, com o tempo, implementar mais lances ou suas dependências podem introduzi-las. Os clientes não podem se defender fácil ou adequadamente contra todas essas saídas. Eu escrevo e atualizo muitas bibliotecas, há evolução e aprimoramento freqüentes. Tentar manter isso tudo sincronizado com as seqüências de saída de exceção (em uma grande base de código) não seria um bom uso do tempo e provavelmente adicionaria muito ruído e sujeira. Devido ao aumento da correção do programa e a mais testes,

Histórico / Código Existente

Em alguns casos, eles nunca foram introduzidos por razões históricas. Uma base de código existente não os utilizava, alterar os programas poderia levar anos-homem e tornar a manutenção realmente feia devido à sobreposição de convenções e implementações.

Desvantagens

Obviamente, existem desvantagens, as maiores são: Incompatibilidade (inclusive binária) com outras bibliotecas e o fato de que você precisará implementar uma boa quantidade de programas para se ajustar a esse modelo.

justin
fonte
+1 excelente informação! (embora eu não concorde com o parágrafo Simplicidade) #
Martin Ba
Seria interessante se você tivesse apenas construtores sem falhas e como lida com a alocação (- falha).
Martin Ba
@ Martin 1) Discordar é muito bom. Entendo que a maioria dos desenvolvedores discorda de desabilitar exceções por vários motivos. Por simplicidade, ele acompanha a correção do programa. Problemas / estados inválidos simplesmente não podem viajar para longe. Isso significa que eles verificam se há falhas e saem normalmente. A publicação de jheriko reflete isso.
Just
11
@Martin 2b) a alocação é um pouco mais complexa. Primeiro, o número de alocações de heap é reduzido em número e aumentado em tamanho - há relativamente poucas alocações de heap para começar. Segundo, as alocações passam por alocadores personalizados. Se uma alocação não for fornecida inicialmente para o alocador (por exemplo, mallocdevoluções 0), o alocador entra em um while (1)que possui uma alternância de contexto, seguido por outra tentativa de alocação. Nessa base de código, costumava ser novo por meio do alocador retornar 0, mas a implementação mais recente estava funcionando bem. (cont)
justin
2
sim, há um wrapper compatível com stl nas interfaces personalizadas do alocador. tecnicamente, o programa não abortará na liberação; ele apresentará opções de contexto (por exemplo, permitirá que outro encadeamento funcione, que esperamos liberar alguma memória), tente novamente e registre se isso falhar - para sempre. testes de unidade podem ser executados por dias sem problemas. a única ameaça real são grandes alocações, muitas das quais serão capturadas antes de solicitadas. novamente, as taxas de falha do sistema também estão no radar neste momento; existe a possibilidade de que eles falhem antes da implementação principal nesse cenário.
Just
7

O Google não aprova exceções no Guia de Estilo C ++ , principalmente por motivos históricos:

Em face deles, os benefícios do uso de exceções superam os custos, especialmente em novos projetos. No entanto, para o código existente, a introdução de exceções tem implicações em todos os códigos dependentes. Se as exceções puderem ser propagadas além de um novo projeto, também será problemático integrar o novo projeto ao código livre de exceção existente. Como a maioria dos códigos C ++ existentes no Google não está preparada para lidar com exceções, é comparativamente difícil adotar um novo código que gera exceções.

Dado que o código existente do Google não é tolerante a exceções, os custos de uso de exceções são um pouco maiores que os custos em um novo projeto. O processo de conversão seria lento e propenso a erros. Não acreditamos que as alternativas disponíveis para exceções, como códigos de erro e asserções, apresentem uma carga significativa.

Nosso conselho contra o uso de exceções não se baseia em bases filosóficas ou morais, mas práticas. Como gostaríamos de usar nossos projetos de código aberto no Google e é difícil fazê-lo se esses projetos usarem exceções, precisamos aconselhar contra exceções também nos projetos de código aberto do Google. As coisas provavelmente seriam diferentes se tivéssemos que fazer tudo de novo do zero.

Há uma exceção a esta regra (sem trocadilhos) para o código do Windows.

(Ênfase do editor)

SuperElectric
fonte
5

O Qt quase nunca usa exceções. Erros no Qt são significados com códigos e sinais de erro. O motivo declarado oficialmente é:

Quando o Qt foi iniciado, as exceções não estavam disponíveis para todos os compiladores que precisavam ser suportados pelo Qt. Hoje, estamos tentando manter as APIs consistentes; portanto, os módulos que têm um histórico de não usar exceções geralmente não obtêm novo código usando as exceções adicionadas.

Por que o QT usa tão poucas exceções?

Hoje, uma crítica comum ao Qt é que sua exceção de segurança não está completa.

user16764
fonte
11
Obrigado. Boa informação, embora eu estou querendo saber se a maioria das bases de código QT provavelmente tem exceções habilitado no compilador ...
Martin Ba
4

O Symbian C ++ (usado em alguns telefones celulares Nokia) não usa exceções, pelo menos não diretamente, porque os compiladores C ++ não os implementaram de maneira confiável quando o Symbian foi desenvolvido.

Keith Thompson
fonte
11
Isso não vale para versões modernas do Symbian. TRAPs e folhas Symbian são implementadas como exceções reais em C ++ , mas o EPOC32 e versões mais antigas do Symbian dependiam de outros meios (setjmp / longjmp, se bem me lembro).
otto 10/10
11
@OttoHarju: Isso é o que eu quis dizer com "pelo menos não diretamente".
Keith Thompson
3

Eu / nunca / uso exceções. Há várias razões para isso, mas as duas principais razões são que eu nunca precisei delas para produzir código robusto e que reduzam o desempenho em tempo de execução.

Eu trabalhei no código de produção que está usando e desativando exceções - o código que permitia exceções era uniformemente pior. Em alguns lugares, foram usadas exceções para controle de fluxo genuíno, em vez de tratamento de erros, que é muito pesado, anti-desempenho e difícil de depurar. Em geral, os problemas de depuração no código cheio de exceção eram mais difíceis do que no código livre de exceção - em parte devido à pilha e às dificuldades intrínsecas do mecanismo de exceção, mas muito mais do que isso, devido ao código lento que era incentivado como resultado de ter manipulação de exceção disponível.

Não há nada seriamente errado com as exceções em si / se você não se importa com desempenho / e não tem tempo para fazer algo corretamente - elas são um recurso de linguagem para tratamento de erros e um bom substituto para um mecanismo adequado de tratamento de erros. No entanto, quase sempre existem / melhores / maneiras de lidar com erros - como com sua própria lógica (é difícil elaborar isso - é quase senso comum). Se não for um erro seu, mas for proveniente de uma biblioteca (por exemplo, a biblioteca padrão), você deverá respeitar a escolha de exceções ou sofrer alguma falha, mas eu sempre questionaria essa opção. Nunca vi uma situação em que as exceções fossem realmente a melhor solução.

As asserções são mais debugáveis, os códigos de erro são menos pesados ​​... entre os dois, se usados ​​corretamente, você fica mais fácil de ler, depurar e manter o código mais rápido. Suas vitórias por toda parte ...

jheriko
fonte
11
Não. É mais fácil usar mal as exceções, mas, além disso, elas funcionam bem. É pelo menos tão fácil de raciocinar, desde que todas as suas funções sejam seguras contra exceções (e isso geralmente é apenas uma questão de usar RTTI para todos os recursos, o que você deve fazer de qualquer maneira). A execução correta dos códigos de erro pode ser extremamente tediosa e resultar em enterrar o programa em uma pilha de códigos de manipulação de erros; nesse caso, as exceções são melhores. Lembre-se, fazer as coisas de maneira diferente de você não é uma evidência conclusiva de que não me importo em fazer as coisas corretamente.
David Thornley 11/11
... por que o RTTI é ruim, isso é outra história - o RTTI embutido em todos os compiladores que eu vi é trivialmente imbatível - se você precisar de RTTI. No entanto, é tudo sobre contexto - coisas como RTTI e exceções podem ser ótimas para aplicativos corporativos e RAD - eles não têm lugar no mundo da computação de alto desempenho (por exemplo, jogos). Eu nunca vi um motor de jogo de produção usando em qualquer alvo, onde eles poderiam ser desligado ...
jheriko
2
Desculpe - RAII é o que eu quis dizer. Se você não estiver usando isso, não apenas as exceções serão confusas. (No entanto, moldes dinâmicos funcionam muito bem no meu trabalho, e eu não precisa se preocupar com o desempenho.)
David Thornley
Tópico interessante: acho que as exceções permitem uma codificação mais concisa da manipulação de erros, mas os códigos de erro são mais robustos porque permitem manipular erros localmente (você pode verificar o código de retorno imediatamente no chamador, enquanto a exceção pode aumentar a pilha de chamadas antes de serem capturadas e às vezes não são capturados, resultando em um acidente, a menos que você use o catch (...)).
Giorgio
3

No padrão de codificação Joint Strike Fighter C ++ de Bjarne et. al., as exceções são proibidas devido aos rígidos requisitos em tempo real dos caças.

O JSF ++ é para aplicações em tempo real e críticas à segurança (software de controle de vôo). Se um cálculo demorar muito, alguém pode morrer. Por esse motivo, temos que garantir tempos de resposta e não podemos - com o nível atual de suporte à ferramenta - fazer isso por exceções. Nesse contexto, até a alocação gratuita de loja é proibida! Na verdade, as recomendações do JSF ++ para tratamento de erros simulam o uso de exceções antecipando o dia em que temos as ferramentas para fazer as coisas corretamente, ou seja, usando exceções.

Citado nas perguntas frequentes em C ++ de Bjarne .

Lembre-se, o C ++ provavelmente executa a mais ampla variedade de software de todos os idiomas ...

Macke
fonte
O JSF ++ também proíbe o uso de "novo" (exceto posicionamento novo) e "exclusão". Que é uma resposta ao "Como você lida com nova falha sem exceções" pouco da questão ...
ARMB
@armb: Sim. Consulte "Nesse contexto, até a alocação gratuita de loja é proibida!" acima.
22313 Macke
2

É meio importante no design da biblioteca C ++. Muitas vezes, com uma interface C, é meio desagradável lançar uma exceção da sua biblioteca de terceiros para o cliente. Aponte que, se sua biblioteca é lançada, você está cortando um conjunto de clientes que a teriam usado, uma vez que ela tinha uma garantia de exclusão (por qualquer razão que o cliente tenha para restringir as exceções).

Pessoalmente, vi exceções abusadas quando a equipe é instruída a "lançar uma exceção" sempre que algo não tão terrível acontece. É claro que você vê o erro aqui - a exceção foi lançada no código antes que alguém descobrisse o que fazer com ele. Esse projeto teve algumas falhas, já que esses arremessos profundos se revezavam ocasionalmente.

anon
fonte
1

Talvez a esse respeito, vale a pena mencionar o C ++ incorporado . O C ++ incorporado é uma variante do C ++ projetada (obviamente o suficiente) para sistemas embarcados. É basicamente um subconjunto adequado de C ++, com (entre outras coisas) modelos, namespaces e manipulação de exceção removidos.

Devo acrescentar que, embora o EC ++ tenha causado algum estrago quando era novo, eles parecem ter ficado bastante silenciosos. Não sei se as pessoas perderam o interesse ou se a primeira tentativa foi tão perfeita que ninguém viu motivo para mexer com isso por uma década.<closed captioning for the humor impaired>Yeah, right!</closed captioning>

Jerry Coffin
fonte
Observando as perguntas e respostas - caravan.net/ec2plus/question.html - eu diria que está praticamente morto.
Martin Ba
1

Um bom exemplo é a programação no modo kernel.

Você pode usar o C ++ lá para obter um código mais limpo, mas sem exceções. Não há biblioteca de tempo de execução para eles, e o tratamento de exceções usa muita memória de pilha que é muito limitada no kernel (experimentei exceções C ++ no kernel do Windows NT e o lançamento e desenrolamento de exceção consome metade da pilha disponível - muito fácil de obter o estouro da pilha e travar todo o sistema).

Normalmente você define seus próprios operadores newe delete. Também achei bastante úteis as referências a placement newe c ++ 11 rvalue. Nenhuma STL ou outras bibliotecas em modo de usuário C ++ que dependem de exceções podem ser usadas.

Sergius
fonte
0

Quando você está tentando manter a memória executável baixa. Normalmente feito em sistemas embarcados (ou móveis). Para aplicativos de desktop, nunca é necessário, no entanto, em servidores, pode ser considerado se você deseja o máximo de memória ram possível para o servidor web ou para sql.


fonte