Existe-t-il des cas réels pour C ++ sans exceptions? [fermé]


40

Dans Quand utiliser C sur C ++ et C ++ sur C? il y a une déclaration concernant. pour coder la taille / les exceptions C ++:

Jerry répond (entre autres points):

(...) il est généralement plus difficile de produire des exécutables vraiment petits avec C ++. De toute façon, vous écrivez rarement beaucoup de code dans les systèmes vraiment petits (...)

ce à quoi j'ai demandé pourquoi, Jerry a répondu:

L'essentiel est que C ++ inclue la gestion des exceptions, ce qui (du moins généralement) ajoute un minimum à la taille de l'exécutable. La plupart des compilateurs vous permettent de désactiver la gestion des exceptions, mais lorsque vous le faites, le résultat n'est plus tout à fait C ++. (...)

dont je ne doute pas vraiment au niveau technique réel.


Par conséquent, je suis intéressé (par pure curiosité) à entendre des exemples concrets dans lesquels un projet a choisi le langage C ++ comme langage, puis a choisi de désactiver les exceptions. (Ne vous contentez pas simplement de "ne pas utiliser" d'exceptions dans le code utilisateur, mais désactivez-les dans le compilateur, de sorte que vous ne puissiez pas lancer ou capturer des exceptions.) Pourquoi un projet a-t-il choisi de le faire (toujours en utilisant C ++ et non C, mais pas exceptions) - quelles sont / étaient les raisons (techniques)?


Addendum: Pour ceux qui souhaitent développer leurs réponses, il serait agréable de détailler la manière dont les implications de l'absence d'exception sont gérées:

  • Les collections STL ( vector, ...) ne fonctionnent pas correctement (les échecs d'allocation ne peuvent pas être signalés)
  • new ne peut pas jeter
  • Les constructeurs ne peuvent pas échouer

1
JSF C ++. Les jets et les exceptions ne font pas bon ménage.
Coder

1
Quelques informations éclairantes sur les problèmes avec les exceptions (et le C ++ en général) 250bpm.com/blog:4
Ambroz Bizjak

1
@AmbrozBizjak - Je citerai DeadMG d'un commentaire au message auquel vous souhaitez renvoyer: quote: "Il me semble que vous ne comprenez pas les exceptions." Je suis d'accord. L’auteur a clairement mis en veilleuse le traitement des exceptions en regardant les exemples qu’il cite dans ce billet.
Martin Ba

Réponses:


35

Presque tous les jeux de console existants sont en C ++ avec une exception désactivée, même de nos jours. En fait, c'est la configuration par défaut pour les compilateurs C ++ ciblant ces consoles. Parfois, il n'est pas garanti que certaines fonctionnalités C ++ fonctionnent correctement sur ces compilateurs, comme l'héritage multiple (je pense à un compilateur par défaut de console très connu, par exemple).


En outre, un autre exemple est le SDK matériel Arduino utilisant gcc sans exception, activé en C ++, et d’autres choses comme aucune STL fournie .


Il y a des raisons techniques, bonnes ou mauvaises, peu importe, ce n'est pas mon conseil mais les raisons que j'ai entendues:

  1. La plupart des consoles sont réellement des systèmes embarqués avec une mémoire et un temps de traitement limités. Ce ne sera peut-être pas aussi vrai dans les futures consoles, mais celles-ci sont toujours très restrictives par rapport à un PC. Certaines consoles portables sont généralement plus difficiles à programmer que n'importe quel smartphone, par exemple le NDS. La fonctionnalité Exception ajoute de la mémoire et un peu de coût en vitesse, même si vous ne l'utilisez pas. Vous pouvez vérifier vous-même, c'est vrai même sur PC.
  2. Les jeux vidéo sur console ne peuvent pas planter. Ils doivent être testés de manière à éviter tout crash ou toute impasse, tout showstoper. C'est pourquoi les fabricants de consoles demandent aux jeux d'être fortement vérifiés avant publication. Cela signifie également que la gestion des exceptions ajoute des coûts qui ne sont pas vraiment utiles dans le cas d'un jeu sur console. Il est préférable d'utiliser un smartphone, par exemple, car il peut y avoir des moyens de récupérer ou d'ajouter du code pour vous envoyer le problème par courrier électronique. Les plates-formes fermées comme la plupart des consoles ne le permettent pas. Donc, un système d'exception n'est pas vraiment nécessaire après tout. Vous "devez juste le faire fonctionner correctement". ;)
  3. La gestion des exceptions, lorsque vous n'autorisez pas les erreurs et les blocages, signifie que vous devez mettre en œuvre une stratégie de gestion des erreurs. Un tel système peut être assez complexe pour que quelqu'un travaille beaucoup de temps pour le rendre utile. Les développeurs de jeux n'ont pas le luxe de travailler sur des fonctionnalités que l'on pense utiles en cas de crash ...
  4. Le compilateur ne le permet pas (encore). Oui, ça arrive.

Je pense que les exceptions peuvent être utiles même dans les jeux, mais il est vrai que sur les jeux sur console, ce n'est pas vraiment utile.


Mise à jour:

J'ajoute un autre exemple surprenant ici: LLVM / CLang n'utilise pas exception ni RTTI pour les raisons suivantes :

Dans le but de réduire le code et la taille des exécutables, LLVM n’utilise ni RTTI (par exemple, dynamic_cast <>) ni les exceptions. Ces deux fonctionnalités de langage violent le principe général du C ++ selon lequel "vous ne payez que pour ce que vous utilisez", ce qui provoque une saturation de l'exécutable, même si des exceptions ne sont jamais utilisées dans la base de code, ou si RTTI n'est jamais utilisé pour une classe. Pour cette raison, nous les désactivons globalement dans le code.

Cela dit, LLVM utilise énormément une forme RTTI roulée à la main qui utilise des modèles tels que isa <>, cast <> et dyn_cast <>. Cette forme de RTTI est optionnelle et peut être ajoutée à n’importe quelle classe. Il est également nettement plus efficace que dynamic_cast <>.

CLang est bien connu pour sa rapidité de compilation et ses erreurs explicites, mais aussi pour son compilateur rare qui possède un code très facile à suivre.


9
(3): vous avez besoin d'une stratégie de gestion des erreurs, peu importe les circonstances, si vous voulez aller avec (2). Des problèmes vont se poser et votre logiciel de console doit échouer doucement et normalement. Il n’est pas évident pour moi que le fait d’éviter les exceptions facilite tout cela.
David Thornley

6
Tous les grands exemples d’environnements d’exécution étroitement contrôlés dans lesquels les erreurs sont gérées correctement et sans événement «exceptionnel».
Patrick Hughes

1
@ DavidThornley Parce que vous supposez que le système de la console gérera une sorte d'erreur, mais ce ne sera pas le cas. Peut-être que ce sera le cas sur PS3 ou XBox, mais cela ne sera pas autorisé à passer les tests du constructeur de la console. Quoi qu'il en soit, le firmware des consoles les plus avancées n'est pas aussi flexible qu'on pourrait le penser. Un jeu sur console doit avoir accès à presque tout le matériel de la console, vous pouvez donc penser à un jeu fonctionnant sur une console presque comme s'il s'agissait également du "système d'exploitation" de la console ... plus nous avons de puissantes consoles, le moins c'est vrai. Je suis donc d’accord avec vous pour dire que cela semble étrange dans certains cas.
Klaim

2
C'est une bonne réponse. J'aimerais également ajouter que même si une exception bouillonnait et offrait une erreur à l'utilisateur, que pouvait-il ou elle supposer faire à l'aide d'un pavé tactile? La seule solution réelle pour l'utilisateur final est un redémarrage.
anon

2
J'ai supprimé l'exemple de folie parce que quelqu'un de leur équipe a déclaré que l'option NO EXCEPTION ne signifie pas "pas d'utilisation de l'exception" mais "pas d'exception aux règles". Voir permalink.gmane.org/gmane.comp.lib.boost.devel/231377
Klaim

9

Jerry a déclaré: ... le résultat n'est plus tout à fait C ++ , alors que, selon ma métaphore, il s'agit clairement de C ++, un dialecte légèrement différent , car les programmes utilisent d'autres formes, conventions et styles écrits.

Voici mes principales raisons pour les désactiver:

Compatibilité binaire

Le fait de croiser les frontières de la langue et de la traduction n’est pas universellement bien défini ni indéfini. Si vous voulez garantir que votre programme fonctionne dans le domaine de comportement défini, vous devrez mettre les exceptions en quarantaine aux points de sortie du module.

Taille exécutable

Voici les tailles binaires d'un programme sans exception que j'ai écrit, construit sans et avec les exceptions activées:

Sans exception:

  • exécutable + dépendances: 330
  • exécutable dépouillé final (release build): 37

Avec des exceptions:

  • exécutable + dépendances: 380
  • exécutable dépouillé final (release build): 44

Rappel: Il s’agit d’un ensemble de bibliothèques et de programmes ne contenant aucun jet / attrape. Le drapeau du compilateur fait activer des exceptions dans la bibliothèque standard C de. Par conséquent, le coût dans le monde réel est supérieur à 19% dans cet exemple.

Compilateur: apple gcc4.2 + llvm. Tailles en MB.

La vitesse

Malgré l'expression "exceptions à coût zéro", ils ajoutent encore des frais généraux même lorsque rien ne se produit jamais. Dans le cas ci-dessus, il s'agit d'un programme critique en termes de performances (traitement du signal, génération, présentation, conversions, avec de grands ensembles de données / signaux, etc.). Les exceptions ne sont pas une caractéristique nécessaire dans cette conception, alors que les performances sont très importantes.

Exactitude du programme

Cela semble être une raison étrange ... Si lancer n’est pas une option, vous devez écrire des programmes relativement stricts, corrects et bien testés pour vous assurer que votre programme s’exécute correctement et que les clients utilisent correctement les interfaces (si vous me donnez un mauvais argument vérifiez pas un code d'erreur, alors vous méritez UB). Le résultat? La qualité de la mise en œuvre s'améliore considérablement et les problèmes sont résolus rapidement.

Simplicité

Les implémentations de gestion des exceptions ne sont pas souvent mises à jour. Ils ajoutent également beaucoup de complexité car une implémentation peut avoir beaucoup de nombreuses séquences de sortie. Il est plus simple de lire et de gérer des programmes très complexes lorsqu'ils utilisent un petit ensemble de stratégies de sortie bien définies, typées et qui sortent du lot et sont gérées par le client. Dans d'autres cas, les implémentations peuvent au fil du temps implémenter plus de lancers ou leurs dépendances peuvent les introduire. Les clients ne peuvent pas se défendre facilement ou adéquatement contre toutes ces issues. J'écris et mets à jour beaucoup de bibliothèques, il y a des évolutions et améliorations fréquentes. Essayer de garder tout cela synchronisé avec les séquences de sortie d'exception (dans une base de code volumineuse) ne serait pas une bonne utilisation du temps, et ajouterait probablement beaucoup de bruit et de crudité. En raison de l'exactitude du programme et de la multiplication des tests,

Histoire / Code existant

Dans certains cas, ils n'ont jamais été introduits pour des raisons historiques. Une base de code existante ne les utilisait pas, la modification des programmes pouvait prendre des années-hommes et rendre vraiment moche à maintenir en raison du chevauchement des conventions et des mises en œuvre.

Inconvénients

Bien sûr, il y a des inconvénients, les plus importants sont: l'incompatibilité (y compris binaire) avec d'autres bibliothèques et le fait que vous devrez implémenter une bonne quantité de programmes pour s'adapter à ce modèle.


+1 excellente information! (bien que je ne sois pas d'accord avec le paragraphe sur la simplicité)
Martin Ba

Il serait intéressant de ne disposer que de constructeurs sans échec et de la manière dont vous gérez l'allocation (- échec).
Martin Ba

@Martin 1) Pas d'accord, c'est bien. Je comprends que la plupart des développeurs ne sont pas d’accord avec la désactivation des exceptions pour plusieurs raisons. Pour plus de simplicité, cela va de pair avec l'exactitude du programme. Les problèmes / états non valides ne sont tout simplement pas autorisés à voyager loin. Cela signifie qu'ils vérifient les échecs et sortent avec élégance. Le message de jheriko fait écho à cela.
Justin

1
@Martin 2b) L'allocation est un peu plus complexe. Premièrement, le nombre d'allocations de tas est réduit et sa taille augmentée - il y a relativement peu d'allocations de tas pour commencer. Deuxièmement, les allocations passent par des allocateurs personnalisés. Si une allocation n’est pas initialement prévue pour l’allocateur (par exemple, des mallocretours 0), l’allocateur entre un élément while (1)comportant un commutateur de contexte, suivi d’une nouvelle tentative d’allocation. Dans cette base de code, ce qui était nouveau par le biais de l'allocateur pouvait renvoyer 0, mais la nouvelle implémentation a bien fonctionné. (suite)
juste

2
oui, il existe un wrapper compatible stl sur les interfaces d'allocateur personnalisées. techniquement, le programme n'abandonnera pas en diffusion; il introduira des changements de contexte (par exemple, permettra à un autre thread de fonctionner, ce qui permettra, espérons-le, de libérer de la mémoire), réessayera et enregistrera si cela échoue - pour toujours. les tests unitaires peuvent durer plusieurs jours sans problème. la seule menace réelle réside dans les énormes allocations dont beaucoup seront capturées avant la demande. encore une fois, les taux de défaillance du système sont également sur le radar à ce stade; la possibilité existe qu'ils échoueront avant la mise en œuvre de base dans un tel scénario.
Justin

7

Google n'approuve pas les exceptions dans son Guide de style C ++ , principalement pour des raisons historiques:

De prime abord, les avantages liés à l'utilisation des exceptions dépassent les coûts, en particulier pour les nouveaux projets. Cependant, pour le code existant, l'introduction d'exceptions a des implications sur tous les codes dépendants. Si les exceptions peuvent être propagées au-delà d'un nouveau projet, il devient également problématique d'intégrer le nouveau projet au code sans exception existant. La plupart du code C ++ existant sur Google n'étant pas prêt à traiter les exceptions, il est relativement difficile d'adopter un nouveau code générant des exceptions.

Étant donné que le code existant de Google n'est pas tolérant aux exceptions, les coûts d'utilisation des exceptions sont légèrement supérieurs à ceux d'un nouveau projet. Le processus de conversion serait lent et sujet aux erreurs. Nous ne pensons pas que les solutions de remplacement possibles aux exceptions, telles que les codes d'erreur et les assertions, créent un fardeau important.

Notre conseil contre l'utilisation des exceptions ne repose pas sur des raisons philosophiques ou morales, mais sur des raisons pratiques. Parce que nous aimerions utiliser nos projets Open Source chez Google et qu'il est difficile de le faire si ces projets utilisent des exceptions, nous devons également éviter les exceptions dans les projets Google Open Source. Les choses seraient probablement différentes si nous devions tout recommencer à zéro.

Il existe une exception à cette règle (sans jeu de mots) pour le code Windows.

(Souligné par l'éditeur)


5

Qt n'utilise presque jamais d'exceptions. Les erreurs dans Qt sont signifiées par des codes d'erreur et des signaux. La raison officielle est:

Au démarrage de Qt, les exceptions n'étaient pas disponibles pour tous les compilateurs devant être pris en charge par Qt. Aujourd'hui, nous essayons de maintenir la cohérence des API. Par conséquent, les modules qui n'utilisent pas d'exceptions auparavant ne recevront généralement pas de nouveau code en ajoutant des exceptions.

Pourquoi QT utilise-t-il si peu d'exceptions?

Aujourd'hui, l'une des critiques courantes de Qt est que sa sécurité en matière d'exception n'est pas complète.


1
Merci. Bonne information, même si je me demande si la plupart des bases de code QT ont probablement des exceptions activées dans le compilateur ...
Martin Ba

4

Symbian C ++ (utilisé sur certains téléphones mobiles Nokia) n'utilise pas d'exceptions, du moins pas directement, car les compilateurs C ++ ne les ont pas implémentées de manière fiable lors du premier développement de Symbian.


1
Cela ne vaut pas pour les versions modernes de Symbian. Les feuilles et les feuilles Symbian sont implémentées comme de vraies exceptions C ++ , mais EPOC32 et les versions antérieures de Symbian reposaient sur d’autres moyens (setjmp / longjmp si je me souviens bien).
otto

1
@ OttoHarju: C'est ce que je voulais dire par "au moins pas directement".
Keith Thompson

3

Je / jamais / utilise des exceptions. Il y a plusieurs raisons à cela, mais les deux principales sont que je n'ai jamais eu besoin d'eux pour produire un code robuste et qu'ils réduisent les performances lors de l'exécution.

J'ai travaillé sur un code de production qui utilise et désactive les exceptions - le code qui autorisait les exceptions était uniformément pire. À certains endroits, des exceptions ont été utilisées pour un véritable contrôle de flux plutôt que pour la gestion des erreurs, qui est très lourd, anti-performance et difficile à déboguer. En général, les problèmes de débogage dans le code d'exception rempli étaient plus difficiles que dans le code sans exception - en partie à cause de la pile et des difficultés intrinsèques du mécanisme d'exception, mais beaucoup plus que cela était dû au code paresseux qui était encouragé en tant que résultat d'avoir la gestion des exceptions disponible.

Il n’ya rien de grave en ce qui concerne les exceptions elles-mêmes / si vous ne vous souciez pas des performances / et n’avez pas le temps de faire quelque chose correctement - elles sont une fonctionnalité de langage pour la gestion des erreurs et un bon substitut pour un mécanisme de gestion des erreurs approprié. Cependant, il existe presque toujours / mieux / moyens de gérer les erreurs - comme avec votre propre logique (il est difficile de développer cela - c'est presque du bon sens). Si ce n'est pas votre erreur, mais provient d'une bibliothèque (par exemple, la bibliothèque standard), alors vous devez respecter le choix des exceptions ou avoir un crash, mais je remettrais toujours en question ce choix. Je n'ai jamais vu une situation où les exceptions étaient en fait la meilleure solution.

Les assertions sont plus faciles à déboguer, les codes d'erreur sont moins lourds ... entre les deux, si vous l'utilisez correctement, vous obtiendrez plus facilement une lecture, un débogage et une maintenance plus rapides du code. Ses victoires tout au long de ...


1
Nan. Il est plus facile de mal utiliser les exceptions, mais à part cela, les exceptions fonctionnent bien. Il est au moins aussi facile de raisonner, à condition que toutes vos fonctions soient protégées contre les exceptions (et qu’il s’agit généralement simplement d’utiliser RTTI pour toutes les ressources, ce que vous devriez faire de toute façon). L'utilisation correcte des codes d'erreur peut s'avérer extrêmement fastidieuse et entraîner l'enfouissement de votre programme dans une pile de codes de traitement des erreurs; dans ce cas, les exceptions sont meilleures. Rappelez-vous que ce que je fais différemment de vous n’est pas une preuve concluante que je ne me soucie pas de bien faire les choses.
David Thornley

... quant à la raison pour laquelle RTTI est mauvais, c'est une toute autre histoire - le RTTI intégré dans chaque compilateur que j'ai vu est trivialement imbattable - si vous avez même besoin de RTTI. Cependant, tout est dans le contexte - ces éléments tels que RTTI et les exceptions peuvent être intéressants pour RAD et les applications d'entreprise - ils n'ont aucune place dans le monde de l'informatique haute performance (par exemple, les jeux). Je n'ai jamais vu un moteur de jeu de production utiliser soit sur une cible où ils pourraient être éteints ...
jheriko

2
Désolé - RAII est ce que je voulais dire. Si vous ne l'utilisez pas, alors non seulement les exceptions vont être gâchées. (Cependant, les castings dynamiques fonctionnent très bien dans mon travail et je dois me préoccuper de la performance.)
David Thornley

Sujet intéressant: Je pense que les exceptions permettent un codage plus concis de la gestion des erreurs, mais les codes d'erreur sont plus robustes, car ils permettent de gérer les erreurs localement (vous pouvez vérifier le code de retour immédiatement dans l'appelant, tandis qu'une exception peut faire bouillonner la pile d'appels avant d'être interceptée. et parfois ils ne sont pas pris, ce qui entraîne un crash. À moins que vous utilisiez catch (...)).
Giorgio

3

Dans la norme de codage Joint Strike Fighter C ++ de Bjarne et. al., les exceptions sont interdites en raison des exigences très strictes des avions de combat en temps réel.

JSF ++ est destiné aux applications en temps réel et critiques (logiciel de contrôle de vol). Si un calcul prend trop de temps, quelqu'un peut mourir. Pour cette raison, nous devons garantir les temps de réponse, ce que nous ne pouvons pas, avec le niveau actuel de prise en charge des outils, faire exception. Dans ce contexte, même l'attribution gratuite de magasins est interdite! En fait, les recommandations JSF ++ pour la gestion des erreurs simulent l’utilisation des exceptions en prévision du jour où nous aurons les outils pour faire les choses correctement, c’est-à-dire en utilisant des exceptions.

Cité dans la FAQ C ++ de Bjarne .

N'oubliez pas que C ++ exécute probablement la plus grande variété de logiciels de toutes les langues ...


JSF ++ interdit également l'utilisation de "nouveau" (autre que le placement nouveau) et de "supprimer". Quelle est l'une des réponses à la question "comment gérez-vous les nouvelles défaillances sans exception" ...
armb

@armb: Oui. Voir "Dans ce contexte, même l'allocation de magasin gratuit est interdite!" au dessus de.
Macke

2

C'est un peu important dans la conception de bibliothèques C ++. Souvent, avec une interface C, il est assez désagréable de lancer une exception de votre bibliothèque tierce au client. Le fait est que si votre bibliothèque lance, vous supprimez un ensemble de clients qui l'auraient utilisée, étant donné qu'elle avait une garantie de non-lancement (quelle que soit la raison pour laquelle le client dispose d'une restriction sur les exceptions).

Personnellement, j’ai vu des exceptions abusées lorsque l’on demande à l’équipe de "lancer une exception" chaque fois que quelque chose de moins terrible se produit. Bien sûr, vous voyez l'erreur ici - l'exception a été lancée dans le code avant que quiconque ne sache quoi en faire. Ce projet a eu quelques accidents, car ces jets profonds risquaient de s’élever de temps en temps.


1

Peut-être à cet égard, il convient de mentionner Embedded C ++ . Le C ++ embarqué est une variante du C ++ conçue (bien évidemment) pour les systèmes embarqués. Il s'agit essentiellement d'un sous-ensemble approprié de C ++, avec (entre autres) les modèles, les espaces de noms et la gestion des exceptions supprimés.

Je devrais ajouter que bien qu'EC ++ ait fait sensation quand il était nouveau, il semble que la plupart du temps, ils se soient relativement discrets. Je ne sais pas si les gens ont perdu tout intérêt, ou si leur première tentative a été tellement parfaite que personne n'a vu aucune raison de la gâcher depuis une dizaine d'années.<closed captioning for the humor impaired>Yeah, right!</closed captioning>


Rechercher le Q & A - caravan.net/ec2plus/question.html - Je dirais que c'est à peu près mort.
Martin Ba

1

Un bon exemple est la programmation en mode noyau.

Vous pouvez y utiliser C ++ pour du code plus propre, mais sans exception. Il n’existe pas de bibliothèque d’exécution pour eux et la gestion des exceptions utilise trop de mémoire de pile, ce qui est très limité dans le noyau (j’ai expérimenté les exceptions C ++ dans le noyau Windows NT et les exceptions de lancement et de déroulement mangent une moitié de la pile disponible - très facile d’obtenir un dépassement de capacité de pile et planter tout le système).

En général, vous définissez vous-même newet les deleteopérateurs. J'ai aussi trouvé des placement newréférences rvalue a et c ++ 11 assez pratiques . Aucune STL ni aucune autre bibliothèque en mode utilisateur C ++ qui repose sur des exceptions ne peut être utilisée.


0

Lorsque vous essayez de garder la mémoire exécutable au minimum. Généralement effectué sur des systèmes embarqués (ou mobiles). Pour les applications de bureau, cela n’est jamais nécessaire, mais sur des serveurs, il peut être utile de prendre en compte autant de RAM que possible pour le serveur Web ou pour SQL.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.