On m'a déjà dit qu'un programme C ++ devrait finalement intercepter toutes les exceptions. Le raisonnement donné à l'époque était essentiellement que les programmes qui permettent aux exceptions de se propager en dehors d' main()
entrer dans un état de zombie étrange. On me l'a dit il y a plusieurs années et, rétrospectivement, je crois que le phénomène observé était dû à la longue génération de décharges de noyau exceptionnellement grandes à partir du projet en question.
À l'époque, cela semblait bizarre mais convaincant. Il était totalement insensé que C ++ "punisse" les programmeurs pour ne pas avoir intercepté toutes les exceptions, mais les preuves dont je disposais semblaient corroborer cela. Pour le projet en question, les programmes qui ont levé des exceptions non capturées semblaient entrer dans un état de zombie étrange - ou comme je soupçonne que la cause était maintenant, un processus au milieu d'un vidage de mémoire indésirable est exceptionnellement difficile à arrêter.
(Pour quiconque se demande pourquoi cela n'était pas plus évident à l'époque: le projet a généré une grande quantité de sortie dans plusieurs fichiers à partir de plusieurs processus qui ont effectivement masqué toute sorte de aborted (core dumped)
message et dans ce cas particulier, l'examen post mortem des vidages de mémoire n'a pas été Ce n'est pas une technique de débogage importante, donc les vidages de mémoire n'ont pas été beaucoup réfléchis. Les problèmes avec un programme ne dépendent généralement pas de l'état accumulé à partir de nombreux événements au fil du temps par un programme de longue durée, mais plutôt des entrées initiales d'un programme de courte durée (< 1 heure), il était donc plus pratique de simplement réexécuter un programme avec les mêmes entrées à partir d'une version de débogage ou dans un débogueur pour obtenir plus d'informations.)
Actuellement, je ne sais pas s'il y a un avantage ou un inconvénient majeur à intercepter des exceptions uniquement dans le but d'empêcher les exceptions de quitter main()
.
Le petit avantage main()
auquel je peux penser pour permettre aux exceptions de se propager dans le passé est qu'il provoque l' std::exception::what()
impression du résultat sur le terminal (au moins avec les programmes compilés par gcc sous Linux). D'un autre côté, cela est trivial à réaliser en capturant à la place toutes les exceptions dérivées de std::exception
et en imprimant le résultat de std::exception::what()
et s'il est souhaitable d'imprimer un message à partir d'une exception qui ne dérive pas, std::exception
il doit être capturé avant de quitter main()
afin d'imprimer le message.
Le désavantage modeste auquel je peux penser pour permettre aux exceptions de remonter dans le passé main()
est que des vidages de mémoire indésirables peuvent être générés. Pour un processus utilisant une grande quantité de mémoire, cela peut être assez gênant et le contrôle du comportement de vidage du noyau à partir d'un programme nécessite des appels de fonction spécifiques au système d'exploitation. D'un autre côté, si un vidage de mémoire et une sortie sont souhaités, cela peut être réalisé à tout moment en appelant std::abort()
et une sortie sans vidage de mémoire peut être obtenue à tout moment en appelant std::exit()
.
Pour l'anecdote, je ne pense pas avoir jamais vu le what(): ...
message par défaut imprimé par un programme largement distribué lors du plantage.
Quels sont, le cas échéant, les arguments solides pour ou contre l’autorisation de voir les exceptions C ++ se multiplier main()
?
Edit: Il y a beaucoup de questions générales sur la gestion des exceptions sur ce site. Ma question concerne spécifiquement les exceptions C ++ qui ne peuvent pas être gérées et qui ont réussi jusqu'à main()
- peut-être qu'un message d'erreur peut être imprimé mais c'est une erreur d'arrêt immédiat.
la source
Réponses:
Un problème lié au fait de laisser les exceptions dépasser le cadre principal est que le programme se terminera par un appel
std::terminate
auquel le comportement par défaut est d'appelerstd::abort
. Il n'est défini par l'implémentation que si le déroulement de la pile est effectué avant l'appelterminate
afin que votre programme puisse se terminer sans appeler un seul destructeur! Si vous avez une ressource qui avait vraiment besoin d'être restaurée par un appel de destructeur, vous êtes dans un cornichon ...la source
std::abort
ne le font pas et donc les assertions qui ont échoué ne le sont pas non plus. Il est également intéressant de noter que sur les systèmes d'exploitation modernes, le système d'exploitation lui-même nettoiera de nombreuses ressources (mémoire, descripteurs de fichiers, ...) qui sont liées à l'ID de processus. Enfin, je faisais également allusion à "Défense en profondeur": il n'est pas fiable de s'attendre à ce que tous les autres processus soient à l'épreuve des bogues et libéreront toujours les ressources qu'ils ont acquises (sessions, fin de l'écriture des fichiers, ...) que vous devez planifier ça ...WM_POWERBROADCAST
message. Cela ne fonctionne que si votre ordinateur est alimenté par batterie (si vous utilisez un ordinateur portable ou un onduleur).La principale raison pour ne pas laisser les exceptions s'échapper
main
est parce que sinon vous perdez toute possibilité de contrôler la façon dont le problème est signalé à vos utilisateurs.Pour un programme qui n'est pas destiné à être utilisé pendant longtemps ou largement diffusé, il peut être acceptable que des erreurs inattendues soient signalées de quelque manière que le système d'exploitation décide de le faire (par exemple, en affichant une boîte de dialogue d'erreur sur Windows sous Windows). ).
Pour les programmes que vous vendez ou qui sont fournis au grand public par une organisation qui a une réputation à défendre, il est généralement préférable de signaler de manière agréable que vous avez rencontré un problème inattendu et d'essayer d'enregistrer autant de données de l'utilisateur que possible. Ne pas perdre une demi-journée de travail de votre utilisateur et ne pas tomber en panne de façon inattendue, mais l'arrêt semi-gracieux est généralement bien meilleur pour la réputation de votre entreprise que l'alternative.
la source
main()
a beaucoup à voir avec le système d'exploitation? L'OS pourrait ne rien savoir du C ++. Je suppose que c'est déterminé par le code que le compilateur insère quelque part dans le programme.TL; DR : Que dit la spécification?
Un détour technique ...
Lorsqu'une exception est levée et qu'aucun gestionnaire n'est prêt pour cela:
std::terminate
est appelé, qui par défaut abandonneCe dernier peut être utile pour des bugs très peu fréquents (car les reproduire est un processus qui fait perdre du temps).
La capture ou non de toutes les exceptions est, en fin de compte, une question de spécification:
Pour tout programme de production, cela doit être spécifié et vous devez suivre la spécification (et peut-être faire valoir qu'elle doit être modifiée).
Pour que les programmes rapidement regroupés ne soient utilisés que par des techniciens (vous, vos coéquipiers), tout va bien. Je recommande de le laisser planter et de configurer l'environnement pour obtenir un rapport ou non en fonction de vos besoins.
la source
Une exception que vous attrapez vous donne la possibilité d'imprimer un joli message d'erreur ou même d'essayer de récupérer de l'erreur (peut-être en relançant simplement l'application).
Cependant, en C ++, une exception ne contient pas d'informations sur l'état du programme lors de son lancement. Si vous l'attrapez, tout cet état est oublié, tandis que si vous laissez le programme planter, l'état est généralement toujours là et peut être lu à partir du vidage du programme, ce qui facilite le débogage.
C'est donc un compromis.
la source
Au moment où vous savez que vous devez abandonner, allez-y et appelez
std::terminate
déjà pour limiter tout dommage supplémentaire.Si vous savez que vous pouvez vous détendre en toute sécurité, faites-le à la place. N'oubliez pas que le déroulement de la pile n'est pas garanti lorsqu'une exception n'est jamais interceptée, donc attrapez et relancez.
Si vous pouvez signaler / enregistrer l'erreur en toute sécurité mieux que le système ne le fera tout seul, allez-y.
Mais assurez-vous vraiment de ne pas aggraver les choses par inadvertance.
Il est souvent trop tard pour enregistrer des données lorsque vous détectez une erreur irrécupérable, bien que cela dépende de l'erreur spécifique.
Quoi qu'il en soit, si votre programme est écrit pour une récupération rapide, le tuer peut être le meilleur moyen de le terminer, même s'il ne s'agit que d'un arrêt normal.
la source
Crasher gracieusement est une bonne chose la plupart du temps - mais il y a des compromis. Parfois, c'est une bonne chose de planter. Je dois mentionner que je pense principalement au débogage dans le très grand. Pour un programme simple - bien que cela puisse encore être utile, il est loin d'être aussi utile qu'avec un programme très complexe (ou plusieurs programmes complexes interagissant).
Vous ne voulez pas planter en public (bien que ce soit vraiment inévitable - les programmes se bloquent, et un programme non-crash vraiment vérifiable mathématiquement n'est pas ce dont nous parlons ici). Pensez à Bill Gates BSODing au milieu d'une démo - mauvais, non? Néanmoins, nous pouvons essayer de comprendre pourquoi nous nous sommes crashés et ne plus planter de la même manière.
J'ai activé une fonctionnalité du rapport d'erreurs Windows qui crée des vidages sur incident locaux sur les exceptions non gérées. Cela fonctionne à merveille si vous avez les fichiers de symboles associés à votre build (en panne). Chose intéressante, parce que j'ai configuré cet outil, je veux planter davantage - car j'apprends de chaque crash. Ensuite, je peux corriger les bugs et planter moins.
Donc pour l'instant, je veux que mes programmes se lancent complètement et se bloquent. À l'avenir, je pourrais vouloir manger gracieusement toutes mes exceptions - mais pas si je peux les faire fonctionner pour moi.
Vous pouvez en savoir plus sur les vidages sur incident locaux ici si vous êtes intéressé: Collecte des vidages en mode utilisateur
la source
En fin de compte, si une exception se propage au-delà de main (), cela va planter votre application, et dans mon esprit, une application ne devrait jamais se bloquer. Si vous pouvez planter une application au même endroit, pourquoi pas n'importe où? Pourquoi se soucier du traitement des exceptions? (Sarcasme, ne suggérant pas vraiment cela ...)
Vous pouvez avoir un essai / capture global qui imprime un message élégant indiquant à l'utilisateur qu'une erreur irrécupérable s'est produite et qu'il doit envoyer un e-mail à Bob dans le service informatique à ce sujet ou quelque chose de similaire, mais le plantage est complètement non professionnel et inacceptable. Il est trivial d'empêcher et vous pouvez informer un utilisateur de ce qui vient de se passer et comment réparer les choses pour que cela ne se reproduise plus.
Le crash est une heure strictement amateur.
la source
main
. Si vous ne savez pas comment le gérer, faire semblant de le faire est évidemment un bug vraiment horrible.