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
Patrick 'Zener' Brunet
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 cette
partie-là soit garantie.

Il n'est pas rare de voir des exceptions créées par un new, et pour faire
bonne mesure, on insère tout un tas de diagnostics dedans sous forme de
strings...
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...

Ensuite, comment passe-t-on cette exception (que "throwe"-t-on) ?
Par adresse, référence ou par copie ?
La copie est plus naturelle, mais elle implique effectivement de copier tout
ça (donc de mouliner intérieurement du new et du delete).

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érarchie
bien claire permettant de cascader les catch spécifiques et de se passer du
catch-all (sauf pour faire un cleanup intermédiaire suivi d'un rethrow).

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

Généralement on est bien embêté parce qu'on se tape pour l'occasion une
librairie plus ou moins exotique et on n'a pas trop le temps de creuser son
fonctionnement intime (c'est même ce qu'on veut éviter - on a la pression
bien sûr):
- Pour le détail des causes d'échec possibles, normalement, on regarde 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 plantage et
c'est pas vendeur. Et ça peut arriver par la suite avec une nouvelle version
de ladite librairie. D'où le choix du catch-all équivalent du défault...

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 suis 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 process
maître une chance de corriger la situation (libérer des ressources par
exemple) et réessayer. Moi j'aime pas trop les logiciels qui disent "au
secours, j'y arrive pas", de plus je fais dans le process automatisé, alors
souvent il n'y a personne pour lire le message.

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 nomenclature 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émarche
encyclopédique. Il faudrait ensuite que process maître ait une stratégie
correctrice pour chaque cas recensé.

En général on ne se donne pas cette peine, ou on n'a pas de doc utilisable
pour les insides, et donc la mission de diagnostic détaillé de l'exception
devient presque inutile (tout au plus on ramène le complément du message
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).

Ce qui finalement je pense doit plaider pour l'utilisation de codes
d'erreur.
Notez bien que personnellement je n'adhère pas du tout à cette logique, mais
je la rencontre tous les jours en faisant de la maintenance corrective... Je
DETESTE la maintenance corrective...

--
Cordialement.

* Patrick BRUNET www.ipzb.fr www.ipzb-pro.com
* E-mail: lien sur http://zener131.eu/ContactMe
Avatar
James Kanze
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.

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.



Je ne sais pas d'où tu tiens ça. Les exceptions apportent une
simplification notable dans le code intermédiaire quand l'erreur
doit être traitée à un niveau plus nettement plus haut. Elles
apportent en revanche leurs propres problèmes (cherche
« exception safety » sur la toile), et ne doivent servir qu'à
bonne échéance. Pour des « erreurs » attendues (et donc non
« exceptionnelles »), les codes de retour restent la meilleur
solution, et pour les erreurs « impossibles » (violation d'une
invariante, etc.), dans la plupart des applications, il vaut
mieux avorter le programme immédiatement que de lever une
exception. Reste les erreurs qui peuvent bien se produire, même
sans erreur de programmation, mais qui néaumoins ne sont pas
attendues, et qui font que le modèle qu'on s'est imposé ne tient
pas (manque de mémoire, etc.).

J'ai dons quelques questions: est-il vrai que les exceptions
sont sous utilisées?



Dans certaines entreprises, sûrement. Avec le nombre de gens qui
apprenent d'abord Java, en revanche, je crois qu'elles sont plus
souvent sur-utilisées.

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.

--
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 4, 4:22 pm, Wykaaa wrote:
Fabien LE LEZ a écrit :



> On Sat, 4 Jul 2009 12:34:35 +0200, "Guillaume Gourdin" :



>> 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.



> Il y a plusieurs raisons à ça :



> D'une part, les exceptions n'existent pas en C, et leur
> support en C++ est "récent". Du coup, les gens qui
> programment toujours soit en C, soit en C++ "ancien", on du
> mal à s'y faire. De plus, beaucoup de bibliothèques, écrites
> en C, ne gèrent pas les exceptions.

Qu'entends-tu par "récent" ?
Ca existait déjà dans le C++ au moins en 1990...



Ah bon. Dans quel compilateur ? Certainement g++. Ni CFront.
Ni Zortech.

> D'autre part, il n'y a pas vraiment de consensus sur
> l'étendue d'utilisation des exceptions. Certains pensent
> qu'il faut les réserver à des cas vraiment exceptionnels,
> qui ne devraient pas se produire lors du déroulement normal
> du programme. Exemple : pas assez de mémoire pour créer un
> objet, tentative de prendre le cinquième élément d'un
> tableau qui n'en comporte que trois, etc.



L'exemple du tableau est plutôt un bug de programmation. Ce
n'est pas une situation exceptionnelle comme le "pas assez de
mémoire".



Exacte. Dans la plupart des applications, il vaut mieux
carrément avorter -- la situation ne peut pas se produire, elle
s'est produite, on ne peut donc plus raisonner sur le code, et
tout ce qu'on fait risque de rendre la situation pire. (Il y a
des exceptions, évidemment. Des jeux, par exemple, mais aussi
probablement certaines services Web non critiques.)

> 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.

En ce qui concerne badbit (erreur dur, erreur d'écriture en
sortie, erreur de lecture physique en entrée), on pourrait en
discuter. Ce genre d'erreur doit être exceptionnelle, et je
crois que l'utilisation d'une exception pourrait se justifier
dans ce cas-là. Mais certainement pas dans les autres cas.

> Note aussi que si la fonction appelante comporte directement
> le code de traitement d'erreur en cas d'échec de la fonction
> appelée, utiliser un code de retour est plus facile. Les
> exceptions deviennent utiles s'il faut remonter de plusieurs
> appels de fonctions pour trouver le code de traitement
> d'erreur.



Je ne suis pas d'accord avec ton dernier paragraphe.



Il a cependant clairement raison. Quelque chose comme :

if ( someAction() != success ) {
// traiter l'erreur...
}

est bien plus simple à comprendre et à maintenir que quelque
chose du genre :

try {
someAction() ;
// qui sait quoi de plus...
} catch ( errorCode ) {
// traiter erreur concernant someAction
}

Même si c'est directement la fonction appelante qui traite
l'exception, il est préférable d'utiliser le mécanisme
d'exception plutôt qu'un code retour.
Pourquoi ?
Parce que le traitement de l'exception est isolé dans le code
(catch) et non disséminé directement derrière l'appel, ce qui
mélange le cas normal et le cas "exceptionnel".



Sauf qu'il a bien dit qu'on parle des cas où l'« erreur » n'est
pas exceptionnelle, et que justement, on veut le traiter tout de
suite ; la gestion de l'erreur est une partie essentielle de
l'algorithme (si l'algorithme est correct), et il faut
l'analyser comme telle. Le problème avec l'exception ici, c'est
justement qu'il exige qu'on sépare physiquement (textuellement,
puisqu'on parle du code source) la gestion de l'erreur de
l'endroit où elle se produit.

C'est une question de méthodologie, de lisibilité et de
maintenabilité.



Exactement. S'il faut gerer l'erreur tout de suite,
l'utilisation d'une exception est à proscrire. (Il ne faut pas
oublier qu'en fin de compte, une exception n'est autre chose
qu'un goto maquillé.)

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.



Ce qui apporte quoi ?

--
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, 6:15 am, Fabien LE LEZ wrote:
On Sun, 05 Jul 2009 05:49:54 +0200, Amrein-Marie Christophe
:



>Personnellement, depuis que j'ai touché à Java et .Net, je
>gère les erreurs en C++ sous forme d'exceptions. Toujours.



En gros, tu programmes en Java tout en utilisant un
compilateur C++ ? Ça ressemble aux gens (plus nombreux) qui
programment en C tout en utilisant un compilateur C++ (et qui,
pour en revenir au sujet, n'utilisent jamais les exceptions,
puisqu'elles n'existent pas en C++).



Même en Java, on n'utilise pas les exceptions autant que ça. En
Java, on a d'office un Fallible ; puisque tout est pointeur, on
peut toujours renvoyer un pointeur null.

Mais ça, c'est aujourd'hui, fruit d'une certaine expérience. Au
début, Java poussait bien à utiliser des exceptions pour tout.
Depuis VirtualMachineError jusqu'à FileNotFoundException.
N'importe quoi, en somme.

--
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 4, 10:21 pm, Fabien LE LEZ wrote:
On Sat, 04 Jul 2009 16:22:43 +0200, Wykaaa :



>> tentative de prendre le cinquième élément d'un tableau qui
>> n'en comporte que trois, etc.



>L'exemple du tableau est plutôt un bug de programmation.



T'es sûr ?



Imagine le cas suivant : j'ai chargé un fichier ligne par
ligne dans un vector<string>. Les spécifications de ce fichier
indiquent clairement qu'il doit contenir 5 lignes. Si le
fichier est mal formé, je ne peux qu'arrêter le programme.



Pas sans message d'erreur, j'espère.

Je pourrais écrire :



if (lignes.size() < 5)
throw quelque_chose;
f (lignes[4]);



Sauf que... la fonction at() fait déjà ça pour moi ! Pourquoi
réinventer la roue ?



Parce que le « quelque_chose » que tu lances ne serait surement
pas std::out_of_range, mais quelque chose qui indique bien une
erreur de format dans le fichier en question. (Dans la pratique,
moi, j'écrirais :

if ( lines.size() < 5 ) {
ProgramStatus::instance().setError( ProgramStatus::fatal )
<< ... error message ...
}
// ...

.)

--
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
Sylvain SF
James Kanze a écrit :
On Jul 5, 6:15 am, Fabien LE LEZ wrote:

Personnellement, depuis que j'ai touché à Java et .Net, je
gère les erreurs en C++ sous forme d'exceptions. Toujours.





En gros, tu programmes en Java tout en utilisant un
compilateur C++ ? [...]





?!

n'utilisent jamais les exceptions,
puisqu'elles n'existent pas en C.





v'oui, bien avant leur normalisation en C++ on codait les
exceptions par des setjmp() / longjmp() 100% C mais ça ne
doit pas exister puisque tu le dis.

N'importe quoi, en somme.



comme tu le dis !

Sylvain.
Avatar
James Kanze
On Jul 5, 2:16 pm, Sylvain SF wrote:
James Kanze a écrit :



> On Jul 5, 6:15 am, Fabien LE LEZ wrote:



>>> Personnellement, depuis que j'ai touché à Java et .Net, je
>>> gère les erreurs en C++ sous forme d'exceptions. Toujours.



>> En gros, tu programmes en Java tout en utilisant un
>> compilateur C++ ? [...]



?!



>> n'utilisent jamais les exceptions,
>> puisqu'elles n'existent pas en C.



v'oui, bien avant leur normalisation en C++ on codait les
exceptions par des setjmp() / longjmp() 100% C mais ça ne doit
pas exister puisque tu le dis.



Ce n'est pas moi qui l'a dit, mais setjmp/longjmp en C, on ne
s'en servait pas, à cause des problèmes qui s'en suivaient. Avec
les exceptions, en C++, au moins, on a aussi des destructeurs,
qui permet à éviter le pire.

> N'importe quoi, en somme.



comme tu le dis !



Intéressant exemple des coupes très sélectives, pour qu'on ne
voit plus de quoi il s'agit. (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.)

--
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
Gabriel Dos Reis
Amrein-Marie Christophe writes:

[...]

| > Une erreur courante chez les nouveaux convertis, c'est de croire que
| > l'un des mécanismes est universellement supérieur à l'au tre.
|
|
| Supérieur non. Mais on a tous notre façon de travailler. A l' époque ou je
| codais encore sous DOS avec Turbo C++ et Topspeed C++, je comprenais rien
| aux soit disant avantages des exceptions dont on parlait dans la press...

Indeed.

-- Gaby
Avatar
Gabriel Dos Reis
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.

-- Gaby
Avatar
Sylvain SF
James Kanze a écrit :

n'utilisent jamais les exceptions,
puisqu'elles n'existent pas en C.







v'oui, bien avant leur normalisation en C++ on codait les
exceptions par des setjmp() / longjmp() 100% C mais ça ne doit
pas exister puisque tu le dis.



Ce n'est pas moi qui l'a dit



en effet, j'avais laissé les auteurs respectifs.

N'importe quoi, en somme.





comme tu le dis !



Intéressant exemple des coupes très sélectives, pour qu'on ne
voit plus de quoi il s'agit.



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 « 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.

Sylvain.
1 2 3 4 5