pourquoi faut-il bannir std::exit | bloc try-catch fonctionnel

pourquoi faut-il bannir std::exit | bloc try-catch fonctionnel - C++ - Programmation

Marsh Posté le 07-09-2003 à 16:16:28    

pré-requis :
 - connaître le mécanisme des exceptions
 - savoir comment créer ses propres exceptions
 - savoir attraper sainement une exception
 
 
    Je crois que j'en avais déjà parlé, mais je le formalise: pourquoi ne faut-il jamais, je dis bien jamais, utiliser std::exit (et les autres fonctions de sa famille). Une première bonne raison à mes yeux: elle vient du C, donc par définition, on va avoir des problèmes avec. Souvent quand on conçoit un programme, le traitement d'erreur implique des erreurs fatales (arrêt du programme), que l'on gère ça bien par exceptions, ou bien comme un cochon avec des codes d'erreurs ("Failure is not an error" d'ailleurs, si vous voulez une petite discussion sur les exceptions -> http://forum.hardware.fr/forum2.ph [...] 499&cat=10 , devenez adepte de la méthode EAFP fin de la ). Donc cette erreur fatale, vous la matérialisez par un appel à std::exit(). Malheureusement, c'est le coup de grâce. std::exit est une fonction bête qui va tuer votre programme d'un coup. Vous programmez bien avec une foultitude d'objets, bien, mais std::exit sans fiche bien, vos destructeurs ne seront __jamais__ appelés ... c'est vraiment un arrêt brutal, imaginez les conséquences si vous devez absolument relâcher certaines ressources (fichiers, verrous, connexions réseaux, matériel ...). l'horreur !
 
    Heureusement la solution est très simple dans le cas où cette erreur est inextrippable (impossible de retourner dans le main pour faire un joli return), il vous suffit de balancer une exception et de l'attendre. J'insiste bien sur le point « attendre » car le gestionnaire d'exceptions inattendues par défaut fera un appel à un truc genre _abort dont l'effet est le même que std::exit.
 
    Donc enserrer votre main dans un joli bloc try et placer 2 bloc catch: le premier pour std::exception, c'est à dire pour ramasser tout ce que vous lancer (intentionnellement ou pas) et un deuxième catch(...) pour rammasser le reste. Le mécanisme des exceptions assurant la destruction des objets, nous voilà hors de danger, tout est propre et sur, les erreurs sont maitrisées.
 
    Notez une synthaxe un peu inhabituelle dans l'exemple: le bloc try fonctionnel, c'est à dire pour enserrer une fonction (ou bien un constructeur, c'est là une autre discussion). c'est juste un exemple, je ferais sans doute un article sur cette construction, sachez qu'elle existe.
 
c++ exit.cpp -> gestion avec std::exit
c++ -DHFR_EXCEPTION exit.cpp -> gestion avec exception
 
comptez bien les instances zombies
 

Code :
  1. #include <iostream>
  2. #ifdef HFR_EXCEPTION
  3. #include <stdexcept>
  4. #endif
  5. // attention à tous ce qui commence par un C
  6. #include <cstdlib>
  7. using namespace std;
  8. // une classe avec un compteur
  9. // pour savoir qui est qui
  10. class Foo
  11. {
  12.   static int compteur;
  13.   int id;
  14. public:
  15.   Foo()
  16.     : id(++compteur)
  17.   {
  18.     cout << "Foo() " << id << '\n';
  19.   }
  20.   Foo(const Foo & )
  21.     : id(++compteur)
  22.   {
  23.     cout << "Foo(const Foo & ) " << id << '\n';
  24.   }
  25.   ~Foo()
  26.   {
  27.     cout << "~Foo() " << id << '\n';
  28.   }
  29. };
  30. int Foo::compteur=0;
  31. // une fonction où se produit une erreur fatale
  32. void Aurevoir(Foo b)
  33. {
  34.   Foo local(b);
  35.   Foo();
  36.   // soudain c'est le drame
  37. #ifndef HFR_EXCEPTION
  38.   exit(0);
  39. #else
  40.   throw logic_error("Boom !" );
  41. #endif
  42. }
  43. int main()
  44. try
  45. {
  46.   Foo a;
  47.   Foo(); // temporaire
  48.   Aurevoir(a);
  49. }
  50. catch(const exception &ex)
  51.   cerr << ex.what() << endl;
  52. }
  53. catch(...)
  54. {
  55.   cerr << "Unknown exception !" << endl;
  56. }


 
 
remarquez que cette méthode (enchasser le main avec 1 try / 2 catch) est la marque de bons logiciels.


Message édité par Taz le 21-10-2004 à 11:29:55
Reply

Marsh Posté le 07-09-2003 à 16:16:28   

Reply

Marsh Posté le 07-09-2003 à 22:32:21    

ah, cool :) j'avais dejà un peu pensé à un truc comme ca, vu mon niveau chui fier de moi :p


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 07-09-2003 à 22:36:11    

BlackGoddess a écrit :

ah, cool :) j'avais dejà un peu pensé à un truc comme ca, vu mon niveau chui fier de moi :p

merde, t'es un mec  :sweat:

Reply

Marsh Posté le 07-09-2003 à 22:38:05    

oui, il est vrai que mon pseudo peut prêter a confusion ...


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 07-09-2003 à 22:48:51    

BlackGoddess a écrit :

oui, il est vrai que mon pseudo peut prêter a confusion ...


 
dommage pour toi, maintenant on ne te repondra plus  :lol:

Reply

Marsh Posté le 08-09-2003 à 09:27:31    

muh  :cry:  
si j'avais su j'aurais écrit fière :p


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 08-09-2003 à 10:03:32    

sauf quand tu programmes des drivers.
 
Mais bon faire exit() c'est pas une bonne idée dans ce cas la non plus :D.
 
LeGreg

Reply

Marsh Posté le 20-10-2004 à 23:10:29    

Est-ce qu'un segfault déclenche catch(...) ?


---------------
Cordialement, Xterm-in'Hate...
Reply

Marsh Posté le 20-10-2004 à 23:34:16    

non, un segfault, c'est un signal venant du noyau. En cas de segfault, le mieux est de ne rien faire. Si ça segfault, c'est que tu as corrompu ton espace mémoire : aucune contrainte n'est plus assurée, tu ne peux rien faire avec certitude. Alors autant arrêter les dégats.

Reply

Marsh Posté le 20-10-2004 à 23:42:15    

Effectivement, on peut rediriger le signal segfault mais l'execution est compromise.
 
Lorsque plusieurs bloques try-catch sont encapsulé, tu conseilles de manière générale de relancer le gestionnaire d'exception avec un throw dans les blocks catch ? ... histoire de retourner jusque dans le block catch de plus haut niveau, celui du main().


---------------
Cordialement, Xterm-in'Hate...
Reply

Marsh Posté le 20-10-2004 à 23:42:15   

Reply

Marsh Posté le 20-10-2004 à 23:46:50    

Taz a écrit :

non, un segfault, c'est un signal venant du noyau. En cas de segfault, le mieux est de ne rien faire. Si ça segfault, c'est que tu as corrompu ton espace mémoire : aucune contrainte n'est plus assurée, tu ne peux rien faire avec certitude. Alors autant arrêter les dégats.


 
Si je redirige le signal correspondant au segfault vers une simple routine qui appel throw. Est-ce le block catch de la fonction en cours d'execution va s'executer ? Je sais, c'est tordu...


---------------
Cordialement, Xterm-in'Hate...
Reply

Marsh Posté le 20-10-2004 à 23:47:19    

je comprends pas ta question. Si tu fais un catch, ne catch que ce que tu sais traiter. Tu peux également avoir des catch qui lance une nouvelle exception afin de préciser celle-ci (ou de lui donner un sens dans le contexte appelant).

Reply

Marsh Posté le 20-10-2004 à 23:51:06    

En somme ma question etait, est ce qu'on peut généraliser ton conseil (un double bloque try-catch dans le main) à toutes les fonctions et fonctions membres ? Y compris lorsqu'on ne sait pas traiter l'exception (tanpis, cela permet qd meme de la localiser finement).


---------------
Cordialement, Xterm-in'Hate...
Reply

Marsh Posté le 21-10-2004 à 00:05:20    

ben non. Ça te sert à quoi d'attraper des exceptions dont tu n'a rien à faire ?

Reply

Marsh Posté le 21-10-2004 à 00:19:39    

A peu de chose. Principalement à la localiser.


Message édité par xterminhate le 21-10-2004 à 00:19:59

---------------
Cordialement, Xterm-in'Hate...
Reply

Marsh Posté le 21-10-2004 à 00:26:57    

Ce n'est pas comme ça qu'il faudrait faire. y a pas de mécanisme de stacktrace en C++. tu veux t'aider avec __FILE__ et ses potes. La vrai question toute façon, c'est à quoi sert de localiser précisément une exception, à part afficher un message bien imbittable à l'utilisateur (n'est-ce pas Java ?).  Si tu peux traiter une exception, attrape là, et résous le problème si. Sinon, ne fais rien. Souviens toi que le code que tu manipules peux lancer n'importe quoi (surtout avec les templates). Ne présume de rien.

Reply

Marsh Posté le 21-10-2004 à 12:23:12    

Bon, déjà, Sous Windows, un segfault lance une exception rattrapable avec catch (...)
 
D'autre part, les stack-traces, c'est vraiment très très pratique lorsque tu as des merdes sur un projet énorme à l'autre bout du monde et qui tourne depuis des jours: ça te permet de voir où ça merde, éventuellement pourquoi, sans avoir à retrouver la combinaison qui l'a fait planter.
 
Bref, dans ma boite on avait carrément implémenté le méchanisme permettant de les récupérer (soit on récupère le signal, et on exécute pstack sur le programme, soit on rajoute des infos de contexte qui créent une stack-trace virtuelle).  
 
Et l'utilisateur, il est content, parce qu'il n'a pas besoin d'écrire sur un bout de papier tout ce qu'il fait pour déclencher le crash, et parce qu'il a son diagnostic plus rapidement...
 

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed