74 votes

Si vous êtes dans le camp "nous n'utilisons pas d'exceptions", comment utilisez-vous la bibliothèque standard?

Note: je ne suis pas à jouer l'avocat du diable ou quelque chose comme ça ici, je suis juste vraiment curieux car je ne suis pas dans ce camp de moi-même.

La plupart des types de la bibliothèque standard, soit ont une mutation des fonctions que l'on peut lancer des exceptions (par exemple, si l'allocation de la mémoire échoue) ou de la non-mutation des fonctions que l'on peut lancer des exceptions (par exemple en dehors des limites indexés accesseurs). En plus de cela, de nombreuses fonctions gratuites peut lancer des exceptions (par exemple, operator new et dynamic_cast<T&>).

Comment avez-vous pratiquement traiter dans le contexte de la "nous n'utilisons pas d'exceptions"?

  • Êtes-vous essayer de ne jamais appeler une fonction qui peut jeter? (Je ne vois pas comment que j'avais de l'échelle, donc je suis très intéressé de savoir comment vous le faire si c'est le cas)

  • Êtes-vous ok avec la bibliothèque standard de projection et de vous traiter de "nous n'utilisons pas d'exceptions" comme "nous n'avons jamais jeter les exceptions de notre code et nous n'avons jamais attraper les exceptions à partir de l'autre le code"?

  • Êtes-vous en désactivant la gestion des exceptions au total via des commutateurs du compilateur? Si oui, comment faire l'exception des jets de pièces de la bibliothèque standard de travail?

  • MODIFIER Votre constructeur, peuvent-ils échouer, ou pensez-vous, par convention, l'utilisation d'un 2-l'étape de la construction avec un init fonction qui permet de retourner un code d'erreur en cas d'échec (dont le constructeur ne peut pas faire), ou faites-vous autre chose?

MODIFIER des clarifications Mineures 1 semaine après le début de la question... Beaucoup de contenu dans les commentaires et les questions ci-dessous se concentrer sur le pourquoi aspects des exceptions vs "autre chose". Mon intérêt n'est pas que, mais quand vous choisissez de faire "autre chose", comment réagissez-vous à la bibliothèque standard de pièces qui ne lancer des exceptions?

70voto

KevinZ Points 134

Je vais répondre pour moi et mon coin du monde. J'écris en c++14 (17 une fois que les compilateurs ont un meilleur soutien) temps de latence financière critique dans les applications que les processus de quantités gigantesques d'argent et ne peut pas toujours aller vers le bas. La base de règles est:

  • pas d'exceptions
  • pas de rtti
  • pas de runtime expédition
  • (presque) pas d'héritage

La mémoire est mise en commun et la pré-alloué, donc il n'y a pas de malloc appels après l'initialisation. Structures de données sont soit immortel ou trivialement copiables, de sorte que les destructeurs sont presque absents (il existe quelques exceptions, comme la portée des gardes). Fondamentalement, nous faisons de C + de sécurité + modèles + lambdas. Bien sûr, des exceptions sont désactivés via le commutateur de compilateur. Comme pour la STL, les bonnes parties de celui-ci (c'est à dire: algorithme, numérique, type_traits, itérateur, atomique, ...) sont utilisables. L'exception des jets de pièces de coïncider avec l'environnement d'exécution mémoire de l'allocation des pièces et de la semi-OO pièces joliment nous obtenons donc de se débarrasser de tous les fichiers inutiles en une seule fois: cours d'eau, les contenants à l'exception std::array, std::string.

Pourquoi faire cela?

  1. Parce que, comme OO, exception offre illusoire propreté par la dissimulation ou de déplacer le problème ailleurs, et fait le reste du programme plus difficile à diagnostiquer. Lorsque vous compilez sans "-fno-exceptions", tous vos propre et bien comportés fonctions ont à supporter le soupçon d'être failable. Il est beaucoup plus facile d'avoir une vaste vérifications autour du périmètre de votre base de code, que de faire efery opération failable.
  2. Car les exceptions sont fondamentalement longue portée GOTOs qui ont une quelconque destination. Vous n'utiliserez pas longjmp(), mais des exceptions sont sans doute bien pire.
  3. Parce que les codes d'erreur sont de qualité supérieure. Vous pouvez utiliser [[nodiscard]] à force d'appeler le code pour vérifier.
  4. Parce exception hiérarchies sont inutiles. La plupart du temps, il n'a guère de sens à distinguer ce qui erronées, et quand il le fait, c'est probablement parce que les différentes erreurs nécessitent différents de nettoyage et il aurait été beaucoup mieux pour signaler explicitement.
  5. Parce que nous avons complexe invariants à maintenir. Cela signifie qu'il y a de code, cependant profondément dans les entrailles, qui doivent avoir transnationale des garanties. Il y a deux façons de faire: soit vous faites votre impératif de procédures aussi pur que possible (c'est à dire: assurez-vous que vous ne manquez jamais), ou vous avez immuable des structures de données (c'est à dire: faire de la récupération de défaillance possible). Si vous avez immuable structures de données, puis de sûr, vous pouvez avoir des exceptions, mais vous ne pourrez pas les utiliser parce que lorsque vous utilisez les types sommes. Fonctionnelle des structures de données sont lents, donc l'autre solution est d'avoir des fonctions pures et faire une exception un langage comme le C, pas-à l'exception de C++, ou de la Rouille. Peu importe comment jolie D semble, tant qu'il n'est pas nettoyé de GC et les exceptions, c'est un non-choix.
  6. Avez-vous déjà tester vos exceptions, comme vous le feriez avec un code explicite chemin? Quid des exceptions qui "ne peut jamais arriver"? Bien sûr, vous ne le faites pas, et quand vous touchez ces exceptions, vous êtes foutus.
  7. J'ai vu quelques "belles" exception neutre de code en C++. C'est, il fonctionne de manière optimale avec aucune arête cas, indépendamment du fait que le code qu'il appelle utilise des exceptions ou pas. Ils sont très difficiles à écrire, et je crois, difficile à modifier si vous voulez garder tous vos exception des garanties. Cependant, je n'ai pas vu tout le "beau" code que soit jetés ou des captures d'exceptions. Tous les code que j'ai vu qui interagit avec les exceptions directement ont été universellement laid. Le montant de l'effort qui est entré dans l'écriture d'exception-neutre code complètement nains le montant de l'effort qui a été sauvé de la merde code que soit jetés ou des captures d'exceptions. Le "beau" est entre guillemets car il n'est pas réelle beauté: il est généralement fossilisés parce que l'édition elle nécessite le fardeau supplémentaire de maintien d'exception à la neutralité. Si vous n'avez pas de tests unitaires qui, délibérément et de manière exhaustive, l'abus des exceptions pour déclencher ces cas limites, même "beau" exception neutre code désintègre en fumier.

20voto

Zouch Points 1254

Dans notre cas, nous désactivons les exceptions par le compilateur (e.g -fno-exceptions pour gcc).

Dans le cas de la gcc, ils utilisent une macro appelée _GLIBCXX_THROW_OR_ABORT qui est définie comme

#ifndef _GLIBCXX_THROW_OR_ABORT
# if __cpp_exceptions
#  define _GLIBCXX_THROW_OR_ABORT(_EXC) (throw (_EXC))
# else
#  define _GLIBCXX_THROW_OR_ABORT(_EXC) (__builtin_abort())
# endif
#endif

(vous pouvez le trouver en libstdc++-v3/include/bits/c++config sur les dernières versions de gcc).

Ensuite, il suffit de composer avec le fait que les exceptions levées tout abandonner. Vous pouvez encore attraper le signal et de l'impression de la pile (il y a une bonne réponse sur cela explique cela), mais vous avez mieux éviter ce genre de choses (au moins dans les versions).

Si vous voulez un exemple, au lieu d'avoir quelque chose comme

try {
   Foo foo = mymap.at("foo");
   // ...
} catch (std::exception& e) {}

vous pouvez le faire

auto it = mymap.find("foo");
if (it != mymap.end()) {
    Foo foo = it->second;
    // ...
}

19voto

abl Points 181

Je tiens aussi à préciser, que quand on parle de ne pas utiliser des exceptions, il y a une question plus générale sur la bibliothèque standard: Êtes-vous à l'aide de la bibliothèque standard lorsque vous êtes dans l'un des "nous n'utilisons pas d'exceptions" camps?

La bibliothèque Standard est lourd. "Nous n'utilisons pas d'exceptions" des camps, comme beaucoup de GameDev entreprises par exemple, les mieux adaptés alternatives pour STL - principalement basée sur EASTL ou TTL. Ces bibliothèques ne pas utiliser les exceptions de toute façon et c'est parce que la huitième génération de consoles ne gère pas trop (voire pas du tout). Pour une arête de coupe AAA code de production, les exceptions sont trop lourds de toute façon, donc c'est un scénario gagnant - gagnant dans de tels cas.

En d'autres termes, pour de nombreux programmeurs, tournant exceptions off va de pair avec la non utilisation de la STL.

11voto

David Haim Points 3696

Ne pas essayer de répondre pleinement aux questions que vous avez posées, je vais juste donner google comme un exemple pour le code de base qui ne fait pas intervenir les exceptions, comme un mécanisme pour traiter les erreurs.

Dans Google code C++ de base, toutes les fonctions qui peuvent échouer retour un status d'objets qui ont des méthodes comme l' ok de spécifier le résultat de l'appel.
Ils ont configurés GCC à l'échec de la compilation si le développeur a ignoré le retour status objet.

Aussi, à partir du peu de code open source (tels que les LevelDB de la bibliothèque), il semble qu'ils ne sont pas à l'aide de la STL que beaucoup de toute façon, de sorte que la gestion des exceptions sont devenus rares. comme Titus Hivers dit dans ses conférences dans CPPCon, ils le "Respect de la norme, mais ne pas idolâtrer".

10voto

Niall Points 6133

Remarque - je utiliser des exceptions... mais j'ai été forcé de ne pas.

Êtes-vous essayer de ne jamais appeler une fonction qui peut jeter? (Je ne vois pas comment que j'avais de l'échelle, donc je suis très intéressé de savoir comment vous le faire si c'est le cas)

Ce serait probablement impossible, au moins à grande échelle. De nombreuses fonctions peuvent atterrir jeter, de les éviter entièrement paralyse votre base de code.

Êtes-vous ok avec la bibliothèque standard de projection et de vous traiter de "nous n'utilisons pas d'exceptions" comme "nous n'avons jamais lancer des exceptions à partir de notre code et nous n'avons jamais attraper les exceptions de l'autre le code"?

Vous devez être ok avec ça... Si le code de la bibliothèque va lever une exception et que ton code ne va pas à la gérer, la résiliation est le comportement par défaut.

Êtes-vous en désactivant la gestion des exceptions au total via des commutateurs du compilateur? Si oui, en quoi l'exception des jets de pièces de la bibliothèque standard de travail?

C'est possible (à l'époque il avait été un peu plus populaire pour certains types de projet); les compilateurs ne/peut soutenir cela, mais vous aurez besoin de consulter leur documentation pour que le résultat(s) serait et pourrait être (et dans quelle langue fonctionnalités sont prises en charge en vertu de ces conditions).

En général, lorsqu'une exception sera levée, le programme aurait besoin d'abandonner ou autre sortie. Certaines normes de codage toujours l'exiger, de la JSF norme de codage vient à l'esprit (IIRC).

Stratégie générale pour ceux qui "ne pas utiliser les exceptions"

La plupart des fonctions ont un ensemble de conditions préalables qui peuvent être vérifiés avant l'appel est effectué. Vérifiez pour ceux-ci. Si elles ne sont pas remplies, alors ne faites pas l'appel; revenir à ce que la gestion des erreurs est dans ce code. Pour les fonctions que vous ne pouvez pas vérifier pour s'assurer que les conditions préalables sont réunies... pas beaucoup, le programme va probablement abandonner.

Vous pourriez chercher à éviter les bibliothèques de lancer des exceptions - vous demandé ce, dans le contexte de la bibliothèque standard, de sorte que ce n'est pas tout à fait adapter le projet de loi, mais il reste une option.

D'autres stratégies possibles; je sais que cela semble banal, mais choisir une langue qui ne les utilisez pas. C pourrait faire bien...

...cœur de ma question (votre interaction avec la bibliothèque standard, le cas échéant), je suis assez intéressé par les constructeurs. Peuvent-ils échouer, ou pensez-vous, par convention, l'utilisation d'un 2-l'étape de la construction avec un init fonction qui permet de retourner un code d'erreur en cas d'échec (dont le constructeur ne peut pas faire)? Ou quelle est votre stratégie?

Si les constructeurs sont utilisés, il y a généralement deux approches qui sont utilisées pour indiquer la défaillance;

  1. Définir un code d'erreur interne ou enum pour indiquer l'échec et que l'échec est. Cela peut être interrogé après l'objet de la construction et de prendre les mesures appropriées.
  2. Ne pas utiliser un constructeur (ou du moins que de construire ce qui ne peut manquer dans le constructeur - si quoi que ce soit) et ensuite utiliser un init() méthode de quelque sorte de faire (ou de compléter) de la construction. La méthode de membre peut alors retourner une erreur si il y a un échec.

L'utilisation de l' init() technique est généralement favorisée car elle peut être enchaînés et les échelles de mieux que de l'intérieur "erreur" du code.

Encore une fois, ce sont des techniques qui viennent de milieux où les exceptions n'existent pas (comme le C). À l'aide d'un langage comme C++ sans exceptions près des limites de sa facilité d'utilisation et l'utilité de l'ampleur de la bibliothèque standard.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X