OVH Cloud OVH Cloud

Définition d'une hiérarchie d'exceptions

80 réponses
Avatar
Michael
Bonsoir à tous,

achevant l'écriture de ma bibliothèque servant à utiliser l'API DirectShow,
j'en arrive au sujet un peu brulant pour moi que sont les exceptions...

Je ne sais pas comment les implémenter correctement.

J'ai distingué trois types d'exception qui peuvent être levées:

1) Les exceptions qui surgissent pendant l'initialisation des modules et
qui interdisent l'utilisation du module (initialisation d'un filtre qui
échoue par exemple, ou bien tentative de connecter deux filtres qui ne
peuvent pas être mis en relation ensemble)

2) Les exceptions qui surgissent lors de l'utilisation d'un module, mais
qui n'entravent pas son fonctionnement (impossibilité de se déplacer dans
une vidéo manuellement par exemple, impossibilité d'extraire une image
d'une vidéo)

3) Les exceptions qui sont levées suite à une action de l'utilisateur du
programme final (Problème dans le choix d'un fichier par exemple (un ZIP au
lieu d'un AVI))


Les exceptions de la première catégorie sont plutôt destinés au programmeur
qui utilise la bibliothèque et n'aident en rien l'utilisateur du programme
final, puisqu'il ne pourra rien modifier.


Les exceptions de la deuxième catégorie peuvent être utiles à l'utilisateur
de la librairie et à l'utilisateur du programme


Enfin les exceptions de la dernière catégorie sont exclusivement destinées
à l'utilisateur du programme, afin qu'il change de fichier qui doit être lu
par exemple.



Bref toujours est-il que je ne sais pas comment m'y prendre...

Est-ce que je dois encadrer les fonctions susceptibles de lever une
exception par un try catch()? Lesquelles?

Est-ce à l'utilisateur de la bibiliothèque de le faire?
L'utilisateur du programme?

Ou bien je laisse les exceptions se déclencher et c'est au programmeur de
se débrouiller comme il veut?

Un peu d'aide serait la bienvenue :)

Merci d'avance

Michael

10 réponses

1 2 3 4 5
Avatar
Gabriel Dos Reis
Fabien LE LEZ writes:

| On 16 Aug 2006 06:14:33 -0700, "kanze" :
|
| > -- Exception : ce sont les erreurs auxquelles on ne s'attend
| > pas dans un fonctionnement normal, mais qui peuvent se
| > produire dans des cas limites,
|
| Je serais moins manichéen que toi sur la distinction
| code de retour / exception. J'ai plutôt tendance à choisir suivant la
| lisibilité de code plutôt que d'après une règle fixe.

la correction d'un programme est-elle un corollaire de « lisibilité » ?


-- Gaby
Avatar
Pierre Barbier de Reuille
On Wed, 16 Aug 2006 17:07:00 +0100, Pierre Barbier de Reuille
:

Le fait que la fonction assert est redéfinit en fonction vide dès que le
programme n'est plus compilé en debug ...


Ça, c'est une spécificité de ton compilo.



Non, le assert définit dans assert.h ou cassert par la norme spécifie
bien que si la macro NDEBUG n'est pas définit au moment de l'inclusion
du fichier d'entête, alors la macro ne fait rien. Or, NDEBUG est la
macro qui est définit pour indiquer une compilation en mode debug. Ca
n'a rien de compilateur spécifique ...


Avatar
Fabien LE LEZ
On 16 Aug 2006 23:11:23 +0200, Gabriel Dos Reis
:

la correction d'un programme est-elle un corollaire de « lisibilité » ?


Pas forcément, mais ça aide beaucoup, effectivement.

De toutes façons, les deux méthodes offrent des possibilités d'erreurs
de programmation, mais diamétralement opposées :
- si on oublie de vérifier un code de retour, une erreur
d'exécution peut passer inaperçue ;
- si on oublie d'intercepter une exception, une erreur
d'exécution peut arrêter le programme.


En pratique, la simplicité du code peut permettre de choisir la
méthode d'indication d'une erreur :
- si tu écris plusieurs try...catch dans la même fonction, il y
a des chances pour qu'une indication d'erreur par code de retour soit
plus adaptée ;
- si tu écris plusieurs constructions du style
if (f() != OK) return ERREUR;
il y a des chances pour qu'une exception soit plus adaptée.

Avatar
Pierre Barbier de Reuille
On Wed, 16 Aug 2006 17:07:00 +0100, Pierre Barbier de Reuille
:

Le fait que la fonction assert est redéfinit en fonction vide dès que le
programme n'est plus compilé en debug ...


Ça, c'est une spécificité de ton compilo.



Non, le assert définit dans assert.h ou cassert par la norme spécifie
bien que si la macro NDEBUG n'est pas définit au moment de l'inclusion
du fichier d'entête, alors la macro ne fait rien. Or, NDEBUG est la
macro qui est définit pour indiquer une compilation en mode debug. Ca
n'a rien de compilateur spécifique ...


Oups ... c'est l'inverse, la définition de NDEBUG signifie 'No Debug' et
donc si c'est définit, assert est une macro qui ne fait rien ...



Avatar
Fabien LE LEZ
On Wed, 16 Aug 2006 22:28:57 +0100, Pierre Barbier de Reuille
:

Non, le assert définit dans assert.h ou cassert par la norme spécifie
bien que si la macro NDEBUG n'est pas définit au moment de l'inclusion
du fichier d'entête, alors la macro ne fait rien.


OK jusque-là.
Note que si je ne m'abuse, on peut #inclure assert.h plusieurs fois
dans le même .cpp, avec des valeurs différentes. Ce qui permet
d'avoir, via des options de compilation, un contrôle assez fin sur les
erreurs qui arrêteront le programme.

Or, NDEBUG est la
macro qui est définit pour indiquer une compilation en mode debug.


C'est quoi le "mode debug" ?

Avatar
Jean-Marc Bourguet
Pierre Barbier de Reuille writes:

Non, le assert définit dans assert.h ou cassert par la norme spécifie
bien que si la macro NDEBUG n'est pas définit au moment de l'inclusion du
fichier d'entête, alors la macro ne fait rien.


C'est l'inverse.

Or, NDEBUG est la macro qui est définit pour indiquer une compilation en
mode debug. Ca n'a rien de compilateur spécifique ...


Aucun des 6 compilateurs que j'utilise plus ou moins régulièrement (gcc,
como, intel pour Linux, Sun CC pour Solaris, HP aCC pour HP-UX, IBM xlC
pour AIX) ne lie la définition de NDEBUG ni à la présence des informations
de débuggage, ni au degré d'optimisation.

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
Benoît Bréholée
Pierre Barbier de Reuille wrote:
Michel Decima wrote:
Qu'est ce qui empeche de garder les assert en production ?


Le fait que la fonction assert est redéfinit en fonction vide dès que le
programme n'est plus compilé en debug ... (ce qui en dit long sur
l'usage prévu pour les assert)


Mais personne ne t'oblige à ce qu'en production le programme soit
compilé avec NDEBUG.


Avatar
kanze
Pierre Barbier de Reuille wrote:
Michael wrote:
Sinon ça se fait tout à fait d'avoir un message lisible
dans l'exception. Ça se fait aussi de faire des accesseurs
pour que l'utilisateur de la lib ait un accès facile à
l'erreur. Dans ton cas, je rajouterais, dans DSException,
une méthode error_number() qui renvoie hr. Juste au cas où
pour certains erreurs, l'utilisateur de la lib puisse
réinitialiser qqch et relancer le processus ...


C'est donc une chose courament admise qu'une lib formatte
elle-même ses messages d'erreur?



En aucun cas. Le formattage appartient à l'application, ou
éventuellement (pour certains messages) au sous-système de
logging.

Moi par exemple je peux créer un message du type:

DirectShow Error
Error Code : 0x80040200

CoCreateInstance Error : CLSID_VideoRenderer
GUID : xxxxxxx

Si on veut internationaliser les messages d'erreur, ça
devient un problème non?

Bien que je conçoive que pour des exceptions, dont le
message est amené à apparaître très peu souvent, l'anglais
ne soit pas non plus une fatalité...


Je pense que c'est en effet un chose bien admise, d'autant que
la classe standard de base std::exception définie dans la
norme C++ inclue une méthode virtuelle "what" qui renvoie une
chaîne ... à toi de renvoyer une chaîne en fonction des
locales ...


L'utilité de la chaîne « what », c'est de pouvoir encoder et
extraire une information arbitraire. Je vois mal un cas où je
l'affichera directement. Pour commencer, le format d'affichage
dépend de la contexte : je ne mettrais pas les mêmes textes
dans l'email que j'envoie au support technique et dans le pop-up
d'erreur que je montre à l'utilisateur. L'internationalisation,
s'il y en a, ne se fait pas lors de la création de l'exception,
mais lors de son exploitation.

--
James Kanze GABI Software
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
kanze
Michael wrote:
Je vois trois catégories :

-- Code de rétour : erreur « attendue », donc, non
exceptionnelle. L'exemple type, c'est toute erreur dans les
entrées d'une utilisateur. (Utilisateur, ici, c'est le
bonhomme devant le clavier ; pour le code qui t'appelle, je
parlerais de client.) Pour la reste, c'est souvent une
question de jugement : d'une part, une connexion TCP qui
tombe, il faut s'y attendre de temps en temps, mais de
l'autre, si tu es sur un reseau local, et même en général
sur l'internet, c'est quand même assez exceptionnel.

En gros, je dirais que s'il y a des chances de pouvoir
continuer l'opération en cours, c'est le code de rétour qui
s'impose. (Donc, par exemple, on peut toujours démander que
l'utilisateur rentrer les informations erronées de nouveau,
sans avorter la session.)


Au sein de ma lib, quand un paramètre de fonction est le
chemin d'un fichier vidéo à lire par exemple, et que ce chemin
est invalide, je fais comment?


Tu démandes au client de valider le chemin avant. C'est encore
ce qu'il y a de plus facile et de plus propre ; je verrais
plutôt une fonction qui prend un istream&. (Mais il n'y a pas de
règle absolue. Le client peut facilement aller jusqu'à
l'ouverture du fichier, et en traiter les erreurs à son niveau.
En revanche, lui démander de lire le fichier, pour vérifier
qu'il s'agit bien d'un fichier vidéo, et non d'un fichier de
texte, par exemple, va un peu loin.)

Je balance une exception, ou bien c'est au client de s'en charger?


Que tu balance une exception, ou que tu renvoie un code de
retour, c'est bien au client de s'en charger.

Parce que de mon côté, la construction des élèments permettant
de lire un fichier vidéo se font dans le constructeur d'une
classe, donc pas de retour possible ici...


Quand ce n'est pas possible, ce n'est pas possible. Il y a
effectivement des cas où on utilise des exceptions par
nécessité, bien qu'un code de retour aurait été plus raisonable.
En plus des aspects purement conception, il faut bien tenir
compte des contraints techniques. Si on ne peut pas se servir
d'un code de retour, et qu'on ne veut pas avorter le programme,
une exception est en général préferrable aux alternatifs, tels
qu'ignorer l'erreur, ou positionner une variable globale à la
errno. Dans le cas des constructeurs, elle convient
particulièrement, parce que grace à l'exception, il n'y a pas
l'objet ; sans l'exception, il faut traiter la possibilité d'un
objet invalid.

--
James Kanze GABI Software
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
En aucun cas. Le formattage appartient à l'application, ou
éventuellement (pour certains messages) au sous-système de
logging.


C'est bien ce que je craignais...

Est-ce que tu pourrais me donner un exemple de comment les choses doivent
être faites stp? Parce que je ne vois pas comment faire...

Est-ce à moi de proposer au sein de ma lib de quoi interpréter les exceptions
lancées?
Par exemple une fonction virtuelle retournant un entier pour chaque type
d'exception, avec à côté une doc expliquant à quelle erreur correspond chaque
valeur? Et le client encadre tous les appels aux fonctions de ma lib avec un
try { } catch(const DShow::Exception & e) {} et récupère cette valeur et
formatte un texte en conséquence?

Ou bien y a-t-il d'autres solutions couramment utilisées?

1 2 3 4 5