Bonjour a vous,
Cela fait un bout de temps que je me demande comment fonctionne les exceptions en C++. Comment les utiliser ? Qu'est ce qui releve de la bonne pratiques ? Qu'est ce qui releve de la mauvaise pratique ? Quid des bizaritudes de C++ avec la gestion d'exceptions dans les constructeurs ? Y a t'il un lien avec les "exceptions" du systeme d'exploitation (seg fault, signaux unix) ? Quels sont les consequences en terme de performances ?
Je ne m'attends pas a ce que quelqu'un reponde a ces questions (bien que je serais tres reconnaissant si il le faisait). Je suis plutot a la recherche d'un document qui expliquerait ces choses la clairement. J'ai jetter un oeil dans les livres de scott meyers mais les explications sont assez eparses.
Un avis ?
Moi j'irais naturellement voir ici :
http://www.cplusplus.com/doc/tutorial/exceptions/
Je n'utilise jamais d'exceptions, et mes co-projets le font très peu (pas assez pour que je trouve quelque chose d'horrible en tout cas). Donc je ne peux guère t'aider plus. ![]()
Ma réponse sera assez incomplète car je ne fait que pas ou peu de C++. Mais, d'après des tests personnels, les exceptions en C++ sont très lentes (comparées à ce qu'il y a en Caml par exemple ou même à un setjmp/longjmp), donc elles semblent réservées vraiment à la gestion de situations "exceptionnelles" plutôt que pour du "control flow" comme on le fait en Caml justement. Ça donne une idée de ce qui est bonne/mauvaise pratique, mais, encore une fois, comme je n'ai pas de pratique de la POO, je ne sais pas ce qui est bon ou mauvais dans ce paradigme.
Par contre, à ma connaissance il n'y a aucun lien avec les "exceptions" du système. Même si la gestion des exceptions en C++ est certainement laissé à la liberté de la l'implémentation, j'imagine que ça se base sur une pile ad-hoc plus une grosse machinerie objet (d'où la lenteur) pour déterminer au runtime le type de l'exception reçu. Les signaux unix serait bien trop limité pour implémenter ça (et on ne veut pas d'interférence avec une éventuelle gestion de ces signaux).
Pour la gestion des exceptions dans les constructeurs, dans ma mémoire le problème vient de ce qu'on ne sait pas trop dans quel état est un objet dont le constructeur (ou pire le constructeur d'un objet hérité) lève une exception et on ne sait pas trop quoi faire avec. Comme tout le reste en C++, c'est géré par une sémantique extrêmement complexe qui donne cette impression de "bizaritude" (mais c'est une complexité certainement nécessaire, du moins s'il est nécessaire du faire du C++...).
Bon, c'était juste les quelques généralité auxquelles je peux penser sur le sujet, mais un vrai connaisseur de C++ va certainement venir nous donner ses lumières.
Oui les exceptions font parties du langage même il n'y a aucun rapport avec l'OS.
Pour les performances je ne sais pas, ce que je sais c'est que la génération qui comme moi a été formée au Java (BOURRE d'exceptions) et au C# (un peu moins) aurait plus tendance à dire d'en mettre partout MAIS pas en C++ faut croire (ou alors que les vrais cas d'exceptions comme vous dites mais pas une exception à tous les coins de rues en JAVA avec multi polymorphisme à quadruple héritage pour finalement te dire que tu as initialisé un unsigned int avec un entier négatif...).
Tbop2 -> Je pense qu'il se doute bien que les exceptions font parties du langage, mais il voudrait savoir comment celles ci sont implémentées.
En clair, que devienne nos exceptions si l'ont traduisait le code C++ vers C.
Dans un cours d'archi, un prof nous avait dit, au sujet des interruptions matérielles (ITs) que celles-ci étaient "en dessous" des signaux systemes, eux même en dessous des exceptions au niveau langage.
Autant je suis certain que la première partie de la phrase est vraie (il n'y a pas d'autre moyen de gérer les signaux que par les interruptions au niveau langage d'assemblage et matériel), autant la deuxième partie me semble plus "conceptuelle", pour situer son discours.
Je ne crois pas aux Exceptions implémentées à coup de signaux pour les même raisons que dnob : trop limitées, et puis cela mainerait à des situations etranges dans une application qui gererait les signaux.
----
Une heure après :
Je n'ai pas voulu Poster le message tout de suite.
Je viens de chercher, parce que effectivement, c'est super interessant, et je m'étais deja posé la question.
Bon, à ma très grande suprise, il semblerait que les exceptions soient assez souvent gérées au dessus des exceptions du niveau systeme d'exploitation.
C'est par exemple le cas de VC++ qui semble l'implémenter ainsi.
Par contre, je ne connais pas du tout le systeme des exceptions de windows; mais j'imagine que cela doit ressembler aux signaux Unix.
Bref, voici un lien qui a l'air sympa (pas eu le temps de le lire en entier) :
http://www.codeproject.com/kb/cpp/exceptionhandler.aspx
Voila !
J'avais parler de lien entre les exceptions de C++ et les exceptions du systeme d'exploitation parcequ'il me semble me rappeler qu'il y a un lien dans windows. Mais j'ai vu ca il y a plus de 5 ans, donc je ne sais plus bien.
J'etais tomber sur le concept d'exception au niveau du systeme d'exploitatoin dans windows en regardant comment faire de l'allocation de memoire virtuelle non mappe sur la memoire physique. On peut allouer la memoire physique independement de la memoire virtuelle. Naturellement si on utilisait la memoire virtuelle sans avoir allouer de memire physique, on prennait une exception au niveau du systeme d'exploitation. Mais cette exception est interceptable (si je me rappelle bien ca ressemble un peu a un signal unix). Et je me rappelle qu'il etait possible de rattraper cette exception au niveau de C++. Est ce que c'est une gestion tout integre ou un operation d'encapsulation, je ne sais pas (je ne sais plus en fait).
Mais je pense qu'avant d'aller voir a ce niveau la de detail sur les exceptions il me manque pas mal d'information de base. Si un des gouroux du C++ passe dans le coin, je serais heureux d'avoir leur avis.
PS: merci pour les differents lien j'irai lire ca rapidement.
Perso, aucune idée, je n'utilise jamais les exceptions en C++. (mais si ca peut avoir une utilisé concret et simple, pourquoi pas).
J'avoue que ma curiosite est assez academique ici. Je n'ai jamais trouve que la gestion d'erreur "a la C" etait plus complique que la gestoin d'erreur "a la JAVA". Mais je me rappelle avoir ete impressione par les exceptions en caml.
J'ai vu du code C++ passer avec des exceptions il y a pas longtemps et du coup je me suis pose la question. ![]()
En même temps, les exceptions en caml sont conçues pour pallier un manque au niveau du contrôle de flot (absence de break, etc.). Donc c'est normal que ça paraisse plus important que les exceptions dans un langage impératif/C-like.
Je dirais surtout que les exceptions Caml sont impressionnantes dans le sens où se sont des valeurs du langage, et pas juste des "interruptions" (comme dans les langages objets).
Après, si elles comblent un manque de contrôle de flôt, je ne sais pas, mais si l'on considère Caml dans sa dimension fonctionnelle, le break ne manque certainement pas...
De ma petite expérience, les exceptions posent des problèmes de performances, et il me semble bien comme dnob le dis qu'il n'y à aucun rapport avec les exceptions système.
Par contre je n'ai pas d'idée de comment cela fonctionne, si quelqu'un en apprend plus je veux bien savoir ![]()
le lien propose par sale gauss explique comment les exceptions etaient implemente dans visual studio, ils semblent utiliser les structured exception handler de windows et donc peuvent intercepter les segfault sous windows avec ce compilateur la (si j'ai bien compris).
Alors voici ce que j'en sais de mon expérience.
Perso j'utilise pas mal les exceptions, au niveau vitesse il faut savoir que c'est quasiment impossible de savoir en le regardant si un code déclenchant éventuellement une exception sera plus lent ou pas qu'un code sans exception du moins dans la mesure ou elle se déclenche pas.
Et même si elle se déclenche, il y a 2 choses à prendre en compte:
- Certains compilateurs peuvent *aplatir* certaines situations d'exceptions, changeant ainsi l'effet du throw-catch en quelque chose de moins lourd.
- Une bonne utilisation des smart pointers ou du RTTI peut facilement permettre de faire face aux fuites de mémoire qui seraient induites par un changement dans le flux d'exécution.
Cependant, c'est clair que c'est pas une mécanique de gestion appropriée pour les inner-loop et les opérations ou le risque est important dans un algo où on recherche avant tout la vitesse.
En revanche c'est excellent pour les opération IO, sockets, fichiers etc...
En fait, les exceptions sont particulièrement intéressantes du point de vue de la lisibilité lorsque vos programmes commencent à avoir des architectures complexes. Si vous avez 5-6 méthodes qui s'appellent consécutivement et que tout à coup la 6e part en erreur :
1) si vous bossez avec des valeurs de retours spécialisées, non seulement vous perdez en lisibilité mais vous devez vous balader l'erreur dans 6 "return" consécutif.
2) Si vous utilisez des patterns setError(int, std::string), vous introduirez du couplage, vous devrez gérer la remise à zéro de l'erreur et ce sera la merde dans tout scénario de concurrence non trivial.
Ce que les exceptions vous apportent ici, c'est le pouvoir de propagation et la possibilité de laisser à l'appelant le choix de ce qu'il va faire.
Si au fin fond d'une librairie vous faites un
if( !file.write("hello") )
{
cout << "erreur d'écriture";
exit(1);
}
vous avez l'air malin si on utilise votre code sans console, et si l'application voudrait récupérer de cette erreur et tenter autre chose...
Car il faut pas se leurrer, c'est souvent les couches les plus élevées d'un programme qui sont intéressées par la gestion d'erreur, c'est elles qui savent comment réagir, si on doit afficher un message, quitter l'applic, etc... car elles ont la vision du contexte. Tandis que dans le fin-fond d'une librairie, on n'a aucune vision du contexte vous m'suivez?
Pour reprendre le cas de dnob, un throwing constructor est dangereux car le destructeur n'est pas appelé si la construction échoue. Donc tout ce qui doit être déalloué dont on n'a pas trace dans les membres ne peut plus être détruit. On peut utiliser les smart pointers pour résoudre le souci mais en général il est pas mal d'éviter les opérations à exception dans le constructeur.
Pour ce qui est des exceptions systèmes, SEGFAULT, à ce que j'en sais ça n'a rien à voir avec la mécanique d'exception en c++ et ce n'est gérable que par un signal handler.
Plus ou moins toutes les erreurs qui ne sont pas balancées par un throw ne sont pas des exceptions c++ et sitôt qu'un code teste la longueur un buffer avant de décider de lever une exception, on tombe dans la gestion d'exception du langage et les mécanismes de protection de l'OS restent à l'écart.
Une dernière chose, en faveur des exceptions, on peut aussi constater que boost repose beaucoup sur les exceptions, et la plupart des développeurs de cette librairie sont très loin d'être des guignols. Pensez-y si vous avez l'impression que c'est une mauvaise pratique ou un abus du c++.
@Godrik
Il y a une mécanique dans les libs win32 pour convertir des interruptions en exceptions, ça passe par cette fonction :
http://msdn.microsoft.com/fr-fr/library/5z4bw5h5%28VS.80%29.aspx
Mais j'ai jamais essayé
Godrik, en quelques mots, tu sais ce que c'est toi que les Structured Exception Handler ?
Parce qu'en survolant l'article que j'ai passé, j'avais une vague idée, mais je n'ai jamais rencontré ça.
Si en pratique on devait faire l'équivalent dans le monde Unix, ça serait quoi, les signaux système ?
Bref, au final, si des gens ont plus d'infos sur comment les exceptions sont traduites, je suis preneur aussi.
D'autant plus que d'ici peu, je risque d'en avoir besoin, je vais surement devoir implémenter un mécanisme similaire pour un langage "pour jouer".
J'ai arreté de faire ma faignasse, et j'ai trouvé ça sur MSDN :
An exception is an event that occurs during the execution of a program, and requires the execution of code outside the normal flow of control. There are two kinds of exceptions: hardware exceptions and software exceptions. Hardware exceptions are initiated by the CPU. They can result from the execution of certain instruction sequences, such as division by zero or an attempt to access an invalid memory address. Software exceptions are initiated explicitly by applications or the operating system. For example, the system can detect when an invalid parameter value is specified.
Structured exception handling is a mechanism for handling both hardware and software exceptions. Therefore, your code will handle hardware and software exceptions identically. Structured exception handling enables you to have complete control over the handling of exceptions, provides support for debuggers, and is usable across all programming languages and machines. Vectored exception handling is an extension to structured exception handling
Bon, ça semble donc être une spécificité de Windows : un mécanisme qui couvre à la fois les ITs matérielles et les exceptions applicatives.
Du cou^, effectivement, pour un compilo C++ pour Windows, ça doit être bien pratique à utiliser.
Mais si on devait faire la même chose sous unix ?
Les signaux me semblent quand même etrangement légers...
Et puis que se passerait-il s'il le programme prévoit déjà des traitements pour les signaux SIGUSR1, SIGUSR2, SIGSEGFAULT, etc ... ?