Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

De l'usage des exceptions

106 réponses
Avatar
Guillaume Gourdin
Bonjour à tous,

selon ma (modeste) expérience, les exceptions en C++sont très largement sous
utilisées, et on en reste (me semble t-il) toujours à la bonne vieille
méthode du retour d'un code d'erreur. Je suis un peu surpris de cet état de
fait, les exceptions étant supposées apporter une gestion plus performantes
des erreurs. J'ai dons quelques questions: est-il vrai que les exceptions
sont sous utilisées? Quels sont vos retours d'expérience? Ou bien y a t'il
une raison plus profonde à la sous-utilisation des exceptions?

Merci!

- Guillaume -

10 réponses

1 2 3 4 5
Avatar
James Kanze
On Jul 5, 7:01 pm, Sylvain SF wrote:
James Kanze a écrit :



[...]
je te l'accorde, mais bon comme je me trouve un peu répétitif
à qualifier de "n'importe quoi" certaines belles généralités
de Fabien, j'ai préféré ce découpage fort malhonnête.



Le problème (en ce qui me concerne), c'est que l'effet final
donnait l'impression que tu dialoguais avec moi, et non avec
Fabien.

> (Le « n'importe quoi », en fait, faisait référence à la
> pratique en Java. Surtout l'idée de vouloir utiliser une
> exception pour quelque chose comme VirtualMachineError.)



j'ai noté. l'avis me parait néanmoins bien tranché, les VMError
(out of mem, stack oveflow, ...) sont - à mon sens - des erreurs
difficiles à traiter: elles ne peuvent *pas* ne concerner que
l'appelant qui traiterait d'un simple code d'erreur (surtout
lorsqu'il n'y a pas d'appel explicite (débordement de pile)
ou que cet appel est cascadé (allocation); bien souvent ces
exceptions ne seront pas traités par le programme et seront
attrapées au plus bas niveau par la VM qui mettra fin à son
exécution; mais cela n'empêche pas un traitement custom et
la gestion de cette exception - qui donc, pour moi, est un
bon moyen de sortir / de notifier l'erreur.



En général, je crois qu'il y a trois types d'erreur :

-- Les erreurs « attendues ». Dans un certain sens, ce ne sont
pas des erreurs, puisqu'on s'y attend, ou qu'on doit s'y
attendre. Ou plutôt, elles sont des erreurs de la part de
l'utilisateur : des fautes de frappe en entrée, par exemple.
En général, traiter ces erreurs doit faire partie de
l'« algorithme », ce qui fait que la meilleur façon de les
annoncer, c'est par un code de retour.

-- Les erreurs « exceptionnelles ». Ce sont des erreurs
auxquelles on ne s'attend pas normalement, mais qu'on sait
peuvent arriver, et qu'on peut vouloir traiter, mais à un
niveau plus élevé, loin d'où l'erreur est detectée. Ce sont
les erreurs pour lesquelles les exceptions conviennent.

-- Les erreurs « impossible ». Ce sont des erreurs qui ne
peuvent pas arriver, tant que le hardware marche et qu'il
n'y a pas d'erreur dans le code. Une violation d'un
invariant, par exemple, ou une faute de parité mémoire
(panne ou défaillance hardware). En général, devant de
telles erreurs, on ne peut rien, et il faut mieux avorter le
programme le plus vite possible.

Mais une telle classification (encore si généralement reconnu
par les experts) est assez catégorique. D'une part, selon
l'application, la même erreur peut être considérer dans une
catégorie ou dans une autre -- considérons un serveur LDAP, qui
traite des requêtes arbitraires formulées en ASN.1, avec un
parseur à déscent récursif pour les interprêter. Comment
catégoriser un débordement de pile dans le parseur Logiquement,
selon la description ci-dessus, c'est une erreur à laquelle il
faut s'y attendre, puisqu'il faut s'attendre à tout d'un
utilisateur. Pratiquement, en revanche, le seul traitement
possible, c'est d'avorter la requête entière, c-à-d de le
traiter à un niveau bien plus élevé dans la pile d'appels, ce
qui argue très fortement pour une exception. Et dans bien des
cas, pratiquement, on ne va pas pouvoir le detecter, même si
elle se produit, parce qu'on ne sait pas (au niveau C++)
détecter un débordement de pile -- du coup, elle devient
« impossible » (et dans la pratique, sous Windows ou Unix, fera
avorter le programme).

Et il y a des considérations très pragmatiques aussi dans
certains cas. Des fonctions comme les constructeurs ou les
opérateurs surchargés ne peuvent pas renvoyer un code de retour.
Alors, même dans le cas d'une erreur « attendue », on a resort à
une exception.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
James Kanze
On Jul 5, 7:01 pm, Gabriel Dos Reis wrote:
James Kanze writes:



[...]



> > > D'ailleurs, par défaut, les iostream ne lancent pas
> > > d'exception en cas d'erreur.



> > C'est un tort !



> Tu rigoles, non ? Tu ne vas pas dire qu'une erreur de format
> dans une entrée d'utilisateur est un cas « exceptionnel ». Ou
> arriver à la fin du fichier.



À noter cependant que les IOStreams peuvent lever des
exceptions, si on le désire.



Certes. Si ça a un sens, c'est une autre question -- une
exception si le eofbit est positionné n'a jamais de sens, par
exemple ; en revanche, on ne peut pas avoir une exception si
l'ouverture d'un fichier échoue dans un constructeur (parce
qu'on ne peut démander les exceptions que sur un objet
complètement construit), alors qu'il peut y avoir des cas où ça
pourrait se justifier. (En général, je crois qu'une exception se
justifierait sur badbit, mais pas autrement.)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Michael Doubez
On 5 juil, 19:01, Gabriel Dos Reis wrote:
James Kanze writes:

[...]

| > > D'ailleurs, par défaut, les iostream ne lancent pas
| > > d'exception en cas d'erreur.
|
| > C'est un tort !
|
| Tu rigoles, non ? Tu ne vas pas dire qu'une erreur de format
| dans une entrée d'utilisateur est un cas « exceptionnel ». Ou
| arriver à la fin du fichier.

À noter cependant que les IOStreams peuvent lever des exceptions, si on
le désire.



J'ai utilisé ce mode une seule fois: lorsque je maitrisais à la fois
le flux émetteur et le flux récepteur pour asserter que le code
fonctionnait.

AMA, en général, une erreur de format est plutôt un problème local et
l'erreur est alors plus facile à localiser.

--
Michael
Avatar
ld
On 6 juil, 10:24, James Kanze wrote:
On Jul 5, 7:01 pm, Gabriel Dos Reis wrote:

> James Kanze writes:
> [...]
> > > > D'ailleurs, par défaut, les iostream ne lancent pas
> > > > d'exception en cas d'erreur.
> > > C'est un tort !
> > Tu rigoles, non ? Tu ne vas pas dire qu'une erreur de format
> > dans une entrée d'utilisateur est un cas « exceptionnel ». Ou
> > arriver à la fin du fichier.
> À noter cependant que les IOStreams peuvent lever des
> exceptions, si on le désire.

Certes. Si ça a un sens, c'est une autre question -- une
exception si le eofbit est positionné n'a jamais de sens,



Tu viens de donner toi-meme un exemple ou ca a un sens: un parser
descendant. Si le parser voit eofbit alors qu'il a des regles "en
cours", c'est que le format des donnees est incorrect. Donc dans un
cas (regles terminees), le eofbit n'est pas une erreur alors que dans
l'autre cas (format invalide), c'est une erreur normalement
exceptionnelle. On peut evidement mettre le parseur dans etat special
(son propre badbit) et le retouner a tous les etages. Mais comme tu
l'as dit, c'est un cas typique ou les exceptions simplifient le code
de gestion des erreurs.

a+, ld.
Avatar
Jean-Marc Bourguet
ld writes:

On 6 juil, 10:24, James Kanze wrote:
> On Jul 5, 7:01 pm, Gabriel Dos Reis wrote:
>
> > James Kanze writes:
> > [...]
> > > > > D'ailleurs, par défaut, les iostream ne lancent pas
> > > > > d'exception en cas d'erreur.
> > > > C'est un tort !
> > > Tu rigoles, non ? Tu ne vas pas dire qu'une erreur de format
> > > dans une entrée d'utilisateur est un cas « exceptionnel ». Ou
> > > arriver à la fin du fichier.
> > À noter cependant que les IOStreams peuvent lever des
> > exceptions, si on le désire.
>
> Certes. Si ça a un sens, c'est une autre question -- une
> exception si le eofbit est positionné n'a jamais de sens,

Tu viens de donner toi-meme un exemple ou ca a un sens: un parser
descendant. Si le parser voit eofbit alors qu'il a des regles "en
cours", c'est que le format des donnees est incorrect. Donc dans un
cas (regles terminees), le eofbit n'est pas une erreur alors que dans
l'autre cas (format invalide), c'est une erreur normalement
exceptionnelle. On peut evidement mettre le parseur dans etat special
(son propre badbit) et le retouner a tous les etages. Mais comme tu
l'as dit, c'est un cas typique ou les exceptions simplifient le code
de gestion des erreurs.



Les parseurs que j'ai ecrits devaient de toute maniere gerer le cas
d'erreur de format et donner un message d'erreur comprehensible par
l'utilisateur. Faire un cas particulier pour la fin de fichier ne me semble
pas simplifier les choses (on peut envisager une exception pour traiter
l'erreur, mais elle ne sera pas jetee par les streams).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Michael Doubez
On 5 juil, 12:07, James Kanze wrote:
On Jul 4, 12:34 pm, "Guillaume Gourdin" wrote:

> selon ma (modeste) expérience, les exceptions en C++sont très
> largement sous utilisées,

Ça dépend. D'après mon expérience, elles servent souvent trop.


[snip]
> Quels sont vos retours d'expérience? Ou bien y a t'il une
> raison plus profonde à la sous-utilisation des exceptions?

Mon expérience, c'est, précisement, qu'elles sont sur-utilisées.
Surtout parmi les programmeurs plus jeunes.



AMA il y a aussi une mode de l'expressivité d'un programme (style DSL)
et comme la gestion d'erreur brise la linéarité de l'expression, les
exceptions sont alors vues comme une solution pour /préserver/ la
lecture du programme en segmentant le programme en "logique
d'application"/"gestion d'erreur".

--
Michael
Avatar
ld
On 6 juil, 12:00, Jean-Marc Bourguet wrote:
ld writes:
> On 6 juil, 10:24, James Kanze wrote:
> > On Jul 5, 7:01 pm, Gabriel Dos Reis wrote:

> > > James Kanze writes:
> > > [...]
> > > > > > D'ailleurs, par défaut, les iostream ne lancent pas
> > > > > > d'exception en cas d'erreur.
> > > > > C'est un tort !
> > > > Tu rigoles, non ? Tu ne vas pas dire qu'une erreur de format
> > > > dans une entrée d'utilisateur est un cas « exceptionnel ». Ou
> > > > arriver à la fin du fichier.
> > > À noter cependant que les IOStreams peuvent lever des
> > > exceptions, si on le désire.

> > Certes. Si ça a un sens, c'est une autre question -- une
> > exception si le eofbit est positionné n'a jamais de sens,

> Tu viens de donner toi-meme un exemple ou ca a un sens: un parser
> descendant. Si le parser voit eofbit alors qu'il a des regles "en
> cours", c'est que le format des donnees est incorrect. Donc dans un
> cas (regles terminees), le eofbit n'est pas une erreur alors que dans
> l'autre cas (format invalide), c'est une erreur normalement
> exceptionnelle. On peut evidement mettre le parseur dans etat special
> (son propre badbit) et le retouner a tous les etages. Mais comme tu
> l'as dit, c'est un cas typique ou les exceptions simplifient le code
> de gestion des erreurs.

Les parseurs que j'ai ecrits devaient de toute maniere gerer le cas
d'erreur de format et donner un message d'erreur comprehensible par
l'utilisateur. Faire un cas particulier pour la fin de fichier ne me semb le
pas simplifier les choses (on peut envisager une exception pour traiter
l'erreur



je ne le vois pas comme un cas particulier, c'est une erreur de
parsing. On peut certe lui attribuer un message d'erreur different
pour mentionner que la fin du fichier a ete vue.

mais elle ne sera pas jetee par les streams



Je ne vois pas trop le rapport entre une erreur vue par le parseur et
une exception levee par le stream?

a+, ld.
Avatar
Michael Doubez
On 5 juil, 11:41, "Patrick 'Zener' Brunet"
wrote:
Bonjour.

"Guillaume Gourdin" a écrit dans le message
 de news 4a4f3042$0$30992$

> selon ma (modeste) expérience, les exceptions en C++sont très
> largement sous utilisées, et on en reste (me semble t-il) toujours
> à la bonne vieille méthode du retour d'un code d'erreur. Je suis un
> peu surpris de cet état de fait, les exceptions étant supposées
> apporter une gestion plus performantes des erreurs. J'ai dons
> quelques questions: est-il vrai que les exceptions sont sous
> utilisées? Quels sont vos retours d'expérience? Ou bien y a t'il
> une raison plus profonde à la sous-utilisation des exceptions?

En complément aux arguments de Fabien Le Lez, en lisant du code C++
utilisant des exceptions, on voit souvent des choses aussi lourdes que
risquées, qui peuvent donc "discréditer" les exceptions aux yeux de
certains.

Lancer une exception implique de la construire, donc il vaut mieux que ce tte
partie-là soit garantie.



C'est pour ça qu'il y a la garantie nothrow. Mais dans le cas général ,
à moins d'un état grave de manque de mémoire, la construction des
exceptions n'est pas un problème.

Il n'est pas rare de voir des exceptions créées par un new, et pour f aire
bonne mesure, on insère tout un tas de diagnostics dedans sous forme de
strings...



C'est du code à la Java.

Tout ça pour traiter un incident tels qu'un échec d'allocation de m émoire ?
Il doit bien y avoir des gens que ça ennuie...



Je prefère une exception à un NULL à vérifier chaque fois. De toute s
façons, je ne sais pas quoi faire si j'ai plus de mémoire, il y a peux
de chance qu'essayer de nouveau marche. Le mieux est encore de revenir
à un état antérieur qui se prétend capable de gérer la situation.


Ensuite, comment passe-t-on cette exception (que "throwe"-t-on) ?
Par adresse, référence ou par copie ?



Pour autant que je sache, on ne peux pas lancer par référence.
Un bonne règle est de lancer par valeur et de catcher par référence
pour éviter le slicing.

La copie est plus naturelle, mais elle implique effectivement de copier t out
ça (donc de mouliner intérieurement du new et du delete).



Je ne comprends pas ce que tu veux dire ici.


Reste à voir ce qu'on en fait à la fin (le catch).

Si le système d'exceptions est bien fait, d'une part on a une hiérarc hie
bien claire permettant de cascader les catch spécifiques et de se passe r du
catch-all (sauf pour faire un cleanup intermédiaire suivi d'un rethrow) .



Si je catche quelque chose, c'est que j'ai prévu de le traiter et que
personne ne pense pouvoir le faire dans une sosu-couche donc je sais
bien ce que je doit catcher.
Un cas spécial est la racine d'un thread où je fais un catch sur
plusieurs hiérarchies pour reporter l'erreur à l'application.

D'expérience, souvent on voit un catch-all avec dedans un printf("oh zu t, ça
a foiré") suivi de return(ERROR).



Il y a beaucoup de cas où il est difficile de trouver une façon
gracieuse de gérer l'erreur. S'assurer que l'utilisateur n'a pas perdu
de données et lui donner une idée du diagnostique est un minimum mais
souvent c'est déjà pas mal.

Généralement on est bien embêté parce qu'on se tape pour l'occasi on une
librairie plus ou moins exotique et on n'a pas trop le temps de creuser s on
fonctionnement intime (c'est même ce qu'on veut éviter - on a la pres sion
bien sûr):
- Pour le détail des causes d'échec possibles, normalement, on regard e les
retours de la fonction appelée, et on fait un switch avec un default...
- Pour une exception, ça peut venir de plus profond et donc c'est assez
obtus. On ne veut pas en oublier une parce que ça conduit à un planta ge et
c'est pas vendeur. Et ça peut arriver par la suite avec une nouvelle ve rsion
de ladite librairie. D'où le choix du catch-all équivalent du défau lt...



Il y a plein de façon d'avoir une bibliothèque mal conçue en dehors
des exceptions.

La gestion des exceptions au bord d'une bibliothèque est sujet à
débat. Mon avis est de n'en laisser passer aucunes sauf les exceptions
standard (allocation ...) et sauf si la logique le justifie (throw
dans un constructeur ou assimilé).

Et donc le sort de l'exception elle-même, on s'en désintéresse (ce qui en
soi "justifie" le passage par copie, car alors on peut espérer que le
destructeur prenne en charge tout ce qui doit l'être).
Moi j'aime bien les exceptions passées par adresse avec une méthode
Release() dedans, et je n'ai jamais de memory leak avec ça, mais je sui s un
cas...

Ce qui est amusant, c'est que l'usage des exceptions devrait permettre de
démonter proprement une tentative d'opération, pour laisser au proces s
maître une chance de corriger la situation (libérer des ressources pa r
exemple) et réessayer.



Pour moi c'est plutôt de retourner à un état connu dans un cas
exceptionnel.

Si il s'agit juste de ressayer, alors on est dans une stratégie locale
et une valeur de retour aurait dû être utilisée.

Moi j'aime pas trop les logiciels qui disent "au
secours, j'y arrive pas", de plus je fais dans le process automatisé, a lors
souvent il n'y a personne pour lire le message.



Un petit mail à l'administrateur, ça fait toujours plaisir :)

En général, on va seulement sortir un message "désolé ça a foir é" puis
avorter. Très souvent, la cause précise de l'erreur n'est pas remont ée
jusque-là.

Pour faire ça, il faudrait que la nomenclature des erreurs de chaque
sous-librairie soit définie (et figée) et intégrée dans la nomenc lature de
la librairie qui l'utilise, etc.
jusqu'au process maître. Il faudrait aussi
pouvoir retourner un contexte assez détaillé dans la même démarch e
encyclopédique. Il faudrait ensuite que process maître ait une strat égie
correctrice pour chaque cas recensé.



Je pense que dans ce cas, il y a confusion entre les logs et le
mécanisme d'exception.


En général on ne se donne pas cette peine, ou on n'a pas de doc utili sable
pour les insides, et donc la mission de diagnostic détaillé de l'exce ption
devient presque inutile (tout au plus on ramène le complément du mess age
d'erreur à afficher pour faire plus humain "erreur système lors de l' accès
à: ujk35585.dat").
Ne reste que la lourdeur apparente du mécanisme d'exception par rapport à un
bon vieux return(code), spécialement si on dispose d'un enum de valeurs
génériques (ou l'équivalent en #define) dans un header global (beu âârk).



Tester chaque retour d'erreur tout en sachant que la probabilité
d'erreur dans une sous-couche dont on est même pas conscient à un
effet déprimant (pour moi). Surtout quand la seule chose que je peux
faire est de prévenir la couche au dessus que ça a pas marché.

L'exception a au moins l'avantage d'automatiser cela.


--
Michael
Avatar
Wykaaa
Gabriel Dos Reis a écrit :
Wykaaa writes:

[...]

| C'est une question de méthodologie, de lisibilité et de maintenabilité.
| De plus, on peut facilement changer le catch au profit de la capture
| d'une exception plus générale que l'exception spécifique qu'on a
| "attrapé" dans la première version d'un logiciel.

Et il savoir apprécier le cas où un code d'erreur de retour est
préférable à une exception.

-- Gaby



Désolé mais ton message est incompréhensible.
Avatar
Wykaaa
Gabriel Dos Reis a écrit :
Amrein-Marie Christophe writes:

| Gabriel Dos Reis wrote:
|
| > Wykaaa writes:
| >
| > [...]
| >
| > | C'est une question de méthodologie, de lisibilité et de maintenabilité.
| > | De plus, on peut facilement changer le catch au profit de la capture
| > | d'une exception plus générale que l'exception spécifique qu'on a
| > | "attrapé" dans la première version d'un logiciel.
| >
| > Et il savoir apprécier le cas où un code d'erreur de retour est
| > préférable à une exception.
| >
| > -- Gaby
|
| Personnellement, depuis que j'ai touché à Java et .Net, je gère les erreurs
| en C++ sous forme d'exceptions. Toujours.
|
| C'est deux façons différentes de traiter les erreurs et on est libre de
| faire comme on veut mais franchement, de mon point de vu, on a beau avoir
| le choix, le code est plus propre et plus flexible avec les exceptions.
|
| A chacun son truc.

Une erreur courante chez les nouveaux convertis, c'est de croire que
l'un des mécanismes est universellement supérieur à l'autre.

-- Gaby



Ok, mais en l'occurrence c'est le cas. Je dirai même que le traitement
par les exceptions est INFINIMENT supérieur à la méthode des codes
d'erreurs. D'ailleurs tous les langages "modernes" ont une syntaxe pour
le traitement des exceptions. C'est même un critère de choix de langage
dans beaucoup de milieux.
1 2 3 4 5