08 Gestion des exceptions .pdf
À propos / Télécharger Aperçu
Ce document au format PDF 1.5 a été généré par LaTeX with Beamer class version 3.33 / pdfTeX-1.40.14, et a été envoyé sur fichier-pdf.fr le 18/01/2017 à 08:36, depuis l'adresse IP 194.199.x.x.
La présente page de téléchargement du fichier a été vue 541 fois.
Taille du document: 418 Ko (58 pages).
Confidentialité: fichier public
Aperçu du document
INFO0402 : Méthodes de programmation
orientée objet
Gestion des exceptions
Pascal Mignot
2015-2016
INFO0402 :
Méthodes de
programmation
orientée objet
Introduction
Pascal Mignot
Introduction
Gestion
classique
La gestion des erreurs est une des tâches qu’un programme doit
nécessairement effectuer afin de :
Bases
• apporter une réponse aux cas exceptionnels,
Exception STL
• fournir des informations sur le problème,
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
2/ 58
• ne pas laisser l’erreur se propager
Afin de répondre à cette problématique, nous aborderons :
• les méthodes classiques de gestion des erreurs,
• la gestion des exceptions (méthode C++ ) et les méthodes à
adopter afin de les utiliser correctement.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
Introduction
Les exceptions sont un mécanisme du langage pour gérer les situations
exceptionnelles (=anormales).
Par situation exceptionnelle, on entend :
• une condition d’erreur,
dans ce cas, la gestion d’exception permet de gérer les erreurs.
• un cas où le code ne peut pas réaliser ce qui lui a été demandé,
dans ce cas, la gestion d’exception permet de traiter des cas
(normalement) exceptionnels.
Comportement d’un code :
• un code qui rencontre une erreur qu’il n’est pas capable de gérer lance
une exception,
• l’exception propage l’information depuis l’endroit où l’erreur est détecté
jusqu’au point où elle sera gérée,
• un code qui gère l’erreur attrape l’exception, et effectue le traitement
nécessaire pour gérer l’erreur.
L’exception fournit un moyen pratique par lequel on sépare la détection d’erreur
3/ 58
de la gestion de l’erreur.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Généralité
Gestion d’erreur C
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
4/ 58
Gestion classique
Approche traditionnelle de la gestion d’erreur :
1 terminaison : s’il y a une erreur, terminer le programme.
analyse : trop draconien.
2 le fonction dans laquelle l’erreur se produit retourne un code d’erreur
L’appelant de la fonction doit vérifier le code retourné.
analyse :
• les erreurs sont ignorés par défaut (i.e. des actions explicites sont
requises pour vérifier si une erreur a eu lieu),
• si l’appelant oublie de vérifier le code de retour, l’erreur peut ne pas
être détectée.
• le code peut devenir parsemé de vérifications de code de retour, ce
qui peut affecter la lisibilité et la maintenabilité du code.
3 appeler un gestionnaire d’erreur (handler) si une erreur est détectée
analyse : il peut être impossible (ou difficilement réalisable) de traiter
certaines erreurs (le handler peut ne pas avoir accès à l’ensemble des
informations pour passer l’erreur).
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Généralité
Gestion d’erreur C
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
5/ 58
Gestion classique
Exemple 1 : terminaison
void f u n ( ) { . . .
i f ( error_condition ) e x i t ( −1);
... }
Exemple 2 : code d’erreur
/ / error .h
typedef u i n t 3 2 _ t
Status ;
# define SUCCESS
0x80000000 / / MSB à 1
# define SUCCEEDED( x ) ( x & 0x80000000 )
# define FAILED ( x )
( ! SUCCEEDED( x ) )
# define EUNKNOWN_FILE 0x00040054 / / 0004=mod,0054= e r r .
# include " e r r o r . h "
/ / f a i t s u i v r e l e code d ’ e r r e u r
ErrorCode fun1 ( . . . ) { . . .
ErrorCode fun2 ( . . . ) { . . .
i f ( error_condition )
S t a t u s s = fun1 ( . . . ) ;
r e t u r n EUNKNOWN_FILE ;
i f ( FAILED ( s ) ) r e t u r n s ;
. . . r e t u r n SUCCESS; }
. . . r e t u r n SUCCESS; }
/ / t r a i t e m e n t de l ’ e r r e u r
S t a t u s s= fun2 ( . . . ) ;
i f ( FAILED ( s ) ) {
/ / g e s t i o n de l ’ e r r e u r
}
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Généralité
Gestion d’erreur C
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
6/ 58
Gestion classique
Exemple 3 : gestionnaire d’erreur
/ / p o i n t e u r de f o n c t i o n
typedef i n t p t _ e r r o r _ h a n d l e r ( const char * , const char * ,
i n t , i n t , const char * ) ;
/ / gestionnaire d ’ erreur
i n t m y_ er ro r_ ha nd l er ( const char * f i l e , const char * f u n c t i o n ,
i n t l i n e , i n t code , const char * msg ) {
...
}
/ / d é f i n i t i o n du g e s t i o n n a i r e d ’ e r r e u r
pt_error_handler
* h a n d l e r = m y_ e rr or _h an dl er ;
/ / macro pour e r r o r
# define E r r o r ( code , reason ) ( * h a n d l e r ) ( __FILE__ , __FUNCTION__ , \
__LINE__ , code , reason )
// utilisation
i n t fun ( i n t n ) {
. . . i f ( e r r n o ==EDOM) E r r o r (EDOM, " argument o u t o f domain " ) ;
...
}
INFO0402 :
Méthodes de
programmation
orientée objet
Gestion d’erreur C
Pascal Mignot
De nombreuses fonctions de la bibliothèque C utilisent errno pour retourner
leurs erreurs.
Introduction
Gestion
classique
Généralité
Gestion d’erreur C
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
7/ 58
On en rappelle le fonctionnement :
• errno est une variable globale entière,
• initialisée à 0 au lancement du programme (0 signifie pas d’erreur),
• lorsqu’une erreur survient, sa valeur est changée en un code
correspondant à l’erreur rencontrée.
• les codes d’erreur générés peuvent être trouvés dans la documentation
de la fonction de la bibliothèque C utilisée (voir errno.h).
Conséquences :
• sa valeur correspond à la dernière erreur rencontrée,
• charge à l’utilisateur de la réinitialiser (à 0).
• si errno est initialisée à 0 avant un bloc de code, alors
• si errno = 0, alors aucune erreur n’a été rencontrée,
• si errno , 0, alors au moins une erreur s’est produite dans le bloc,
et le code de cette erreur est celui de errno.
INFO0402 :
Méthodes de
programmation
orientée objet
Gestion d’erreur C
Pascal Mignot
Fonctions utiles :
Introduction
Gestion
classique
Généralité
Gestion d’erreur C
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
8/ 58
• char* std::strerror(int err) renvoit la chaine de caractères
associées au code d’erreur contenu dans l’entier err.
• std::perror(const char *msg) : affiche sur std::stderr le
message d’erreur associé au contenu de errno (i.e. strerror(errno))
précédé de msg.
Exemple :
double not_a_number = s t d : : l o g ( − 1 . 0 ) ;
i f ( e r r n o ) s t d : : p e r r o r ( " l o g ( − 1) f a i l e d " ) ;
Sortie : log(-1) failed: Numerical argument out of domain
Notes :
• A partir du C++
11 , cette variable devient locale au thread (ouf !).
• inclure <cerrno>
• il existe des classes standards (basées sur std::error_category) qui
permettent de gérer ses propres codes d’erreur.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Gestion par exception
La gestion d’erreur standard en C++ s’effectue avec les exceptions.
Gestion des erreurs avec les exceptions :
Introduction
Gestion
classique
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
1 un bloc try contient le code qui pouvant engendrer une erreur.
aucun traitement d’erreur n’a lieu dans ce bloc.
2 si une condition d’erreur est rencontrée dans le bloc, alors throw lance
une exception.
3 le bloc try est suivi par un bloc catch qui effectue le traitement associé
à l’exception (contient le handler de l’exception).
Conséquences :
• le handler de l’exception peut être dans une fonction différente de celle
qui l’a lancée (propagation dans les fonctions appelantes),
• les parties de code qui ne génèrent pas d’erreur sont simples, puisqu’il
n’y a pas besoin de tester explicitement les erreurs.
• les cas d’erreurs ont peu de chance de ne pas être détectée, puisqu’une
exception lancée mais non traitée provoque la terminaison du
programme.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Gestion par exception
Fonctionnement élémentaire :
• Le code qui peut lancer une exception est placé dans un bloc try,
Introduction
Gestion
classique
• Le code qui peut gérer l’exception est placé dans un bloc catch,
• Pour un même bloc try-catch,
Bases
• il peut avoir plusieurs clauses catch,
• les clauses catch sont testés dans l’ordre spécifié, et seulement la
première clause ayant un type correspondant au throw est utilisée
(upcasting compris).
• la clause catch(...) peut être utilisée pour capturer toute
exception non encore gérée. Dangeureux :
• on ne connait pas l’exception capturée,
• devrait être remplacée par catch(std::exception&) (voir
partie sur les exceptions standards).
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Exemple :
try {
catch
catch
catch
/ * code q u i peut l a n c e r une e x c e p t i o n * / }
( const s t d : : l o g i c _ e r r o r & e ) { / * e r r e u r s l o g i q u e s * /
}
( const s t d : : r u n t i m e _ e r r o r& e ) { / * e r r e u r s d ’ e x é c u t i o n * / }
( . . . ) { / * toutes les autres erreurs * / }
INFO0402 :
Méthodes de
programmation
orientée objet
Gestion par exception
Pascal Mignot
Qu’est-ce-qui est lancé lorsqu’une exception est lancée ?
Introduction
• les exceptions sont des objets :
Gestion
classique
• le type de l’erreur est indiqué par le type de l’objet.
Bases
• la valeur de l’objet est utilisée pour fournir les détails sur l’occurence
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
particulière de l’erreur associée,
• le type de l’objet peut être être soit issu de la bibliothèque standard, soit
fourni par l’utilisateur.
Conséquences :
• c’est l’objet exception lui-même qui est propagé de la partie du code qui
génère l’exception (throw) vers la partie du code qui le gère (catch).
• c’est le type de l’exception qui permet de déterminer quel est le handler
(catch) qui traitera l’exception.
• le lancement d’une exception interrompt le déroulement normal du code.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Gestion par exception
Règles générales :
• lancer l’exception par valeur,
et non par pointeur ou référence : l’objet auquel il faut référence peut ne
plus exister lorsqu’il sera traité.
Introduction
Gestion
classique
• récupérer l’exception par référence dans un catch
• éviter la copie, qui pourrait générer aussi une exception,
• évite l’object slicing (voir le cours sur l’héritage) ou la copie.
• permet à l’objet d’être modifié, afin de lever une exception modifiée,
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Pour un throw A(), quels catches ?
• un catch ne peut pas être une référence à une rvalue, une classe
abstraite, un type incomplet ou un pointeur vers un type incomplet.
• catch(A) (par valeur) possible mais déconseillé,
• catch(A&) ou catch(const A&)
• catch(B) aussi possible si upcasting possible (i.e. B est une classe de
•
•
•
•
base de A), mais déconseillé car par valeur.
catch(B&) ou catch(const B&) sous la même condition,
catch(void*) si A est un pointeur (capture tout pointeur),
également en version volatile,
catch(...)
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Exception personnalisée
Il est recommandé pour un module de disposer de sa propre
classe d’exception
si possible héritée d’une classe d’exception standard (voir
exception de la STL).
voir cette partie.
Cela réduit la vraisemblance qu’une exception générée par un
module soit captée par un autre de manière conflictuelle.
Si un throw lancé est un objet, alors les constructeurs par
copie/déplacement et le destructeur doivent être
accessibles (c’est le moyen avec lequel l’exception est
propagée) :
• throw x; initialise un objet temporaire du type de x avec x.
• cet objet temporaire peut être copié/déplacé plusieurs fois
avant d’atteindre le handler qui le gère (catch du type de x).
INFO0402 :
Méthodes de
programmation
orientée objet
Gestion par exception
Pascal Mignot
Fonctionnement multi-niveau :
Introduction
Gestion
classique
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
• lorsqu’une exception est levée, la gestion de l’exception est transférée à
la clause catch du bloc try le plus récemment traversé (et toujours en
cours d’exécution) qui permet de traiter l’exception.
si aucune clause adaptée n’est trouvée, alors std::terminate() est
appelé.
• lors du passage de l’exception à la clause, les destructeurs des objets
automatiquement construits dans le bloc try sont appelés dans l’ordre
inverse de leurs constructions (note : ce procédé s’appelle "stack
unwinding")
conséquence :
• ne seront détruits que les objets automatiques effectivement
construit au moment de l’exception (les suivants dans le bloc try
n’ayant pas encore été construits).
• ne jamais lancer d’exception dans un destructeur
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Gestion par exception
Exemple :
void fun3 ( . . . ) { . . . A a1 ; . . .
void fun2 ( . . . ) { . . . A a3 : . . .
void fun1 ( . . . ) { . . . A a5 : . . .
void f u n ( . . . ) {
t r y { . . . A a7 ; . . . fun1 ( ) ;
catch ( const i n t &x ) { . . .
}
throw 1 ; . . . A a2 ; . . . }
fun3 ( ) ; . . . A a4 ; . . . }
fun2 ( ) ; . . . A a6 ; . . . }
. . . A a8 ;
... }
}
Ce code fonctionne de la manière suivante :
Exception STL
• l’exception levée dans fun3, appel du destructeur de a1, pas de catch
Spécification
d’exception
• pas de catch dans fun2, appel du destructeur de a3, gestion
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
dans fun3, gestion transférée à l’appelant,
transférée à l’appelant,
• pas de catch dans fun1, appel du destructeur de a5, gestion
transférée à l’appelant,
• catch pouvant traiter l’exception, appel du destructeur de a7,
initialisation de x, et exécution du traitement.
INFO0402 :
Méthodes de
programmation
orientée objet
Gestion par exception
Pascal Mignot
Introduction
Gestion
classique
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Remarque sur le stack unwinding :
si une exception est levée entre le moment où l’exception est
levée, et le moment où elle est traitée (par exemple, lors du
stack unwinding), alors le programme se termine
immédiatement (std::terminate()).
conséquence : ne jamais lever d’exception :
• dans un destructeur : il pourrait être lancé dans un stack
unwinding,
• dans le constructeur par copie/déplacement d’un objet
gérant des exceptions (effectué au moment du throw).
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Gestion par exception
Forwarding d’exception :
• il est possible de relancer une exception (éventuellement la même) dans
un catch.
la gestion de l’exception est alors transférée au bloc supérieur (voir
gestion multi-niveau).
• l’utilisation de la commande throw; (sans argument) dans un catch
permet de relancer l’exception (et donc de délèguer son traitement dans
un bloc catch supérieur)
Exemple :
try {
/ / lance l ’ exception std : : l e n g t h _ e r r o r
s t d : : s t r i n g ( " abc " ) . s u b s t r ( 1 0 ) ;
} catch ( const s t d : : e x c e p t i o n& e ) {
s t d : : c o u t << e . what ( ) << ’ \ n ’ ;
/ / r e l a n c e une e x c e p t i o n par c o p i e de e
/ / throw e ;
/ / r e l a n c e l ’ e x c e p t i o n reçue
throw ;
}
INFO0402 :
Méthodes de
programmation
orientée objet
Fonction Try-bloc
Pascal Mignot
Introduction
Gestion
classique
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Une fonction try-bloc est obtenue lorsque qu’un try est placé
sur la totalité du corps d’une fonction
Elle capture les exceptions qui peuvent avoir lieu :
• lors de la construction des paramètres (i.e. avant l’exécution
de la fonction),
• lors de la destruction des variables locales (après l’accolade
fermante),
• dans un constructeur, lors de la construction des membres
et des objets de base,
• dans un destructeur, lors de la destruction des membres et
des objets de base (rappel : mauvaise idée de lancer une
exception dans un destructeur)
INFO0402 :
Méthodes de
programmation
orientée objet
Fonction Try-bloc
Pascal Mignot
Syntaxe :
Introduction
1
i n t fun ( . . . ) {
t r y { / * code de l a f o n c t i o n * / }
catch ( ? ) { . . . }
}
2
i n t fun ( . . . )
t r y { / * code de l a f o n c t i o n * / }
catch ( ? ) { . . . }
Gestion
classique
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
à savoir :
• la totalité du code de la fonction est incluse dans le try-bloc,
• la fonction se termine au moment où le flux de contrôle atteint la fin du
catch block.
Différence entre les deux syntaxes : la syntaxe 1 permet d’effectuer un
return dans un catch, alors que la syntaxe 2 oblige le return à s’effectuer
dans le corps de la fonction.
INFO0402 :
Méthodes de
programmation
orientée objet
Fonction Try-bloc
Pascal Mignot
Introduction
Gestion
classique
Bases
Introduction
Fonctionnement
élémentaire
Rôle du type
Stack unwinding
Forwarding
d’exception
Fonction Try-bloc
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Note : la version 2 de la fonction try-bloc peut être utilisée aussi dans
les constructeurs ou les destructeurs.
Exemple :
Foo : : Foo ( i n t a )
t r y : Base ( a ) , member ( a ) { . . . }
catch ( s t d : : e x c e p t i o n& ex ) { . . . }
Remarques : sur les portées
• tout paramètre de la fonction est aussi dans la portée du catch
dans le cas du constructeur ci-dessus, a est accessible dans le
catch.
• les variables locales du bloc try ne sont évidemment pas
accessible dans un catch.
• dans le cas de la version constructeur/destructeur, le catch ne
peut ni faire référence à la base, ni au membre.
INFO0402 :
Méthodes de
programmation
orientée objet
Exception de la bibliothèque standard
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Exceptions standards
Récupération
Exception utilisateur
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
21/ 58
La STL fournit des types basiques d’exception. Toutes les
exceptions de la STL dérivent directement ou indirectement de la
classe std::exception (incluse dans <exception>).
•
•
•
•
•
logic_error : classe des erreurs logiques
runtime_error : erreur hors de portée du programme
bad_typeid : opérateur invalide pour l’opérateur typeid,
bad_cast : opération invalide pour un dynamic_cast,
bad_weak_ptr : opération invalide sur un std::weak_ptr
(C++
)
11
• bad_function_call : appel std::function sans fonction
définie (C++
)
11
• bad_alloc : erreur d’allocation mémoire (voir plus loin)
• bad_exception : mauvaise gestion des exceptions dans le
cas d’exception dynamique ou de pointeur d’exception.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception de la bibliothèque standard
Pour les allocations mémoires, deux types d’exceptions peuvent être levée :
• bad_alloc : erreur d’allocation mémoire (cas général)
• bad_array_new_length : spécialisation si l’erreur est liée à la taille
demandée (négative, dépassant la taille maximale de l’implémentation,
trop d’initialisations par rapport à la taille demandée) (C++
11 ))
On peut ainsi faire la différence entre une erreur d’allocation liée à l’absence
de mémoire et liée à une erreur d’utilisation de la fonction d’allocation.
Exception STL
Exceptions standards
Récupération
Exception utilisateur
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
22/ 58
Exemple :
/ / e r r e u r d ’ u t i l i s a t i o n de l a f o n c t i o n d ’ a l l o c a t i o n
try {
i n t n e g a t i v e = − 1, s m a l l = 1 , l a r g e = INT_MAX ;
new i n t [ n e g a t i v e ] ;
/ / negative size
new i n t [ s m a l l ] { 1 , 2 , 3 } ;
/ / t o o many i n i t i a l i z e r s
new i n t [ l a r g e ] [ 1 0 0 0 0 0 0 ] ; / / t o o l a r g e
} catch ( const s t d : : bad_array_new_length &e ) {
s t d : : c o u t << e . what ( ) << ’ \ n ’ ;
}
/ / e r r e u r d ’ a l l o c a t i o n c a r p l u s de mémoire
try {
while ( t r u e ) { new i n t [100000000 u l ] ; }
} catch ( const s t d : : b a d _ a l l o c& e ) {
s t d : : c o u t << " A l l o c a t i o n f a i l e d : " << e . what ( ) << ’ \ n ’ ;
}
INFO0402 :
Méthodes de
programmation
orientée objet
Exception de la bibliothèque standard
Pascal Mignot
Introduction
Classe des exceptions logiques dans logic_error :
Gestion
classique
• invalid_argument : argument invalide
Bases
• domain_error : erreur de domaine (exemple : racine carrée
Exception STL
Exceptions standards
Récupération
Exception utilisateur
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
23/ 58
d’un nombre négatif)
• length_error : longueur trop grande (exemple : resize
d’un vector plus grand que max_size)
• out_of_range : argument hors de l’intervalle autorisé
(exemple : vector::at à l’extérieur des bornes).
• future_error (C++
) : operation invalide sur le résultat d’une
11
opération asynchrone (voir std::future).
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Exceptions standards
Récupération
Exception utilisateur
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
Exception de la bibliothèque standard
Classe des exceptions système dans runtime_error :
• range_error : le résultat d’un calcul ne peut être représenté dans le
•
•
•
•
type de destination,
overflow_error : erreur d’overflow arithmétique
underflow_error : erreur d’underflow arithmétique
regex_error : erreur de la bibliothèque d’expression régulière (C++
11 )
system_error : erreur issue du système d’exploitation, ou autre erreur
bas niveau (C++
11 )
• ios_base::failure : erreur issue de la bibliothèque iostream
(C++
11 )
Attention :
• les erreurs arithmétiques standards ne génèrent pas d’exception (i.e. tout
ce qui était déjà dans le C ne lance pas d’exception : il faut utiliser le
gestionnaire standard du C), ou utiliser une bibliothèque mathématique
alternative comme boost::math.
• même remarque pour system_error, les erreurs systèmes en question
sont par exemple celles issues de la bibliothèque thread du C++
11 .
• pas moyen d’attraper une erreur sérieuse (par exemple segmentation
fault) avec des exceptions.
24/ 58
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Exceptions standards
Récupération
Exception utilisateur
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
25/ 58
Récupération d’une exception
Ces classes fonctionnent toutes de la manière suivante :
• le catch peut être effectué avec le type de l’exception elle-même,
ou tout type dont elle dérive.
Exemple :
std : : vector <int >
v;
try { v . at ( 4 ) ; }
/ / c a t c h par l ’ une de ces e x c e p t i o n s
/ / de l a p l u s s p é c i a l i s é e à l a p l u s g é n é r a l e
catch ( const s t d : : o ut _o f_ ra n ge &e ) { . . . }
catch ( const s t d : : l o g i c _ e r r o r &e ) { . . . }
catch ( const s t d : : e x c e p t i o n &e ) { . . . }
out_of_range dérive de logic_error, qui dérive de exception.
• la méthode what() sur toute classe dérivant de std::exception
retourne la cause de la levée de l’exception.
Dans l’exemple précédent, e.what() retourne
invalid vector<T> subscript.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Exceptions standards
Récupération
Exception utilisateur
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
26/ 58
Exception utilisateur
Quelques règles de conceptions d’une classe de gestion d’exception :
• la faire dériver de std::exception : c’est une classe d’exception
raisonnable, et qui permet de capter l’ensemble des exceptions (i.e. sans
à avoir à utiliser catch(...)).
• il est prudent d’hériter virtuellement de std::exception afin d’éviter les
problèmes d’ambiguïté, au cas où une exception dériverait de bases
multiples qui ont une classe de base en commun.
si catch(std::exception const& e) avec un objet throw défini
comme indiqué, le compilateur ne sait pas alors vers quelle classe de
base résoudre.
• ne pas inclure d’objet dans une exception dont le constructeur par copie
ou dont les constructeurs de base/membre peut lancer une exception.
Exemple : i.e. de std::string
• le formatage du message fournit par what() prend de la mémoire, et
peut potentiellement lancer une exception : donc, protéger what() avec
un try-bloc (au cas où le formatage échoue).
• faire en sorte que la classe d’exception soit immunisée contre une
destruction double (en mettant à zéro les pointeurs), car certains
compilateurs détruisent occasionnellement les exceptions deux fois.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Exceptions
Avantages des exceptions :
• le code de gestion d’erreur peut être facilement séparé du code qui
génère l’erreur,
Introduction
Gestion
classique
Bases
Exception STL
Exceptions standards
• les exceptions peut facilement passer les informations d’erreur au travers
autant de niveaux de la pile d’appel que nécessaire afin d’atteindre le
code de gestion de l’exception,
• la transmission d’exception est gérée par le langage (pas de code
explicite requis)
Récupération
Exception utilisateur
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
27/ 58
Désavantages des exceptions :
• écrire des codes qui se comportent correctement en cas d’exception
demande beaucoup de soin,
• s’il peut potentiellement être possible de ne pas avoir de coût d’exécution
lorsque des exceptions ne sont pas lancées, il y a toujours un coût
mémoire (pour stocker les informations nécessaires pour gérer le
changement de contexte dans la pile d’appel lorsqu’une exception est
levée),
• elles ne permettent pas de récupérer d’erreurs sérieuses (comme un
segmentation fault). Les solutions pour faire ceci dépendent de la
plateforme (Windows :SEH, Linux :récupérer SIGSEGV avec sigaction).
INFO0402 :
Méthodes de
programmation
orientée objet
Spécification d’exception
Pascal Mignot
Introduction
Gestion
classique
Les spécifications d’exception sont des mécanismes permettant
de restreindre le lancement d’exception dans certaines parties
du code.
Bases
Exception STL
Spécification
d’exception
Exception dynamique
noexcept
Spécificateur
Opérateur
Usages de noexcept
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
28/ 58
Deux moyens sont utilisés pour cela :
• les exceptions dynamiques qui permettent de spécifier
pour une fonction les exceptions qu’elle a le droit de lancer,
• noexcept qui permet d’indiquer qu’une fonction n’a pas le
droit de lancer d’exception.
La maitrise des spécifications d’exception est un mécanisme
important dans l’implémentation correcte des exceptions dans
un code C++ .
INFO0402 :
Méthodes de
programmation
orientée objet
Spécifications d’exception dynamique
Cette fonctionnalité est considérée comme obsolète et ne doit pas être utilisée.
Pascal Mignot
Introduction
Gestion
classique
Bases
Mécanisme permettant de spécifier la liste de tous les types d’exception qui
peuvent être lancées.
Exemple : / / f peut l a n c e r des e x c e p t i o n s de t y p e X ou Y
void f ( ) throw ( X , Y ) ;
Fonctionnement :
Exception STL
• à l’exécution de la fonction, lorsqu’une exception est lancée, son type est
Spécification
d’exception
comparé à l’ensemble de celles autorisées. Si elle ne l’est pas,
std::unexpected() est exécuté, sinon elle est effectivement lancée.
• set_unexpected( void (*)() ) permet de fixer la fonction qui sera
appelée par std::unexpected() (le défaut est std::terminate()).
• throw() est équivalent à noexcept.
Exception dynamique
noexcept
Spécificateur
Opérateur
Usages de noexcept
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
29/ 58
Remarques : (répétition) obsolète, ne doit plus être utilisée.
• remplacée par noexcept.
• en pratique, la liste d’autorisation est plus une entrave qu’une aide à la
gestion des exceptions,
• le test dynamique est un coût supplémentaire à la gestion d’exception,
• en terme d’optimisation du compilateur, seul compte s’il peut y avoir une
exception, et non quel est le type de l’exception lancée (donc, n’optimise
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
spécificateur noexcept
Préalable :
• Par défaut, toute fonction ou méthode est susceptible de lancer une
exception,
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Exception dynamique
noexcept
• à l’exception du destructeur qui n’ont pas le droit d’en lancer sans
provoquer la terminaison du programme.
Le spécificateur noexcept permet de changer ce comportement :
• noexcept(true) ou noexcept indique que la fonction ne génère pas
d’exception
• noexcept(false) indique que la fonction peut lancer des exceptions.
Spécificateur
Opérateur
Usages de noexcept
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
30/ 58
Exemple :
void
void
void
void
fun1 ( ) ;
fun2 ( ) noexcept ( f a l s e ) ;
fun3 ( ) noexcept ( t r u e ) ;
fun4 ( ) noexcept ;
//
//
//
//
peut l a n c e r une e x c e p t i o n
peut l a n c e r une e x c e p t i o n
ne l a n c e pas d ’ e x c e p t i o n
ne l a n c e pas d ’ e x c e p t i o n
Quel est la conséquence de ce spécificateur ?
une fonction marquée nonexcept qui génère une exception provoque une
terminaison immédiate du programme avec std::terminate().
Exemple : void die_scum_die ( ) no_except { throw 0 ; }
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Exception dynamique
noexcept
Spécificateur
Opérateur
Usages de noexcept
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
31/ 58
spécificateur noexcept
Dans le cas des templates, noexcept peut être utilisé pour disposer un
noexcept conditionnel.
Exemple :
# include < t y p e _ t r a i t s >
# include < u t i l i t y >
/ / ne l a n c e pas d ’ e x c e p t i o n s i s i z e o f ( T ) <= 4
templace <class T> void f u n c ( T ) noexcept ( s i z e o f ( T) <= 4 ) ;
/ / échange de deux v a l e u r s
template <class T> void exchange ( T& a , T& b )
noexcept (
s t d : : i s _ n o t h r o w _ m o v e _ c o n s t r u c t i b l e <T > : : v a l u e &&
s t d : : is_nothrow_move_assignable <T > : : v a l u e
) {
T tmp ( s t d : : move ( a ) ) ; / / move c o n s t r u c t i o n
a = s t d : : move ( b ) ;
/ / move assignment
b = s t d : : move ( tmp ) ;
/ / move assignment
}
La fonction exchange est marquée noexcept si la construction et
l’assignation par déplacement pour le type T sont marqués eux-aussi
comme noexcept.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Exception dynamique
noexcept
Spécificateur
Opérateur
spécificateur noexcept
Attention de bien comprendre toutes les implications de noexcept :
Un appel de fonction peut lancer des exceptions comme résultat du passage
de paramètres, de l’exécution de la fonction, et du retour de l’argument de
fonction.
C’est-à-dire :
•
•
•
•
au passage de paramètre : construction de chaque paramètre,
lors de l’exécution de la fonction,
lors de la destruction des variables locales et des paramètres,
dans le cas d’un retour par valeur, la construction de l’objet temporaire
destinée à contenir la valeur dans le contexte de la fonction appelante
(s’il n’y a pas d’élision) .
Usages de noexcept
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
32/ 58
Pour éviter une levée d’exception lors de passage de paramètres :
• passer les arguments par référence,
• faire en sorte que les constructeurs par copie/déplacement soient
noexcept.
Pour éviter une levée d’exception lors d’un retour par valeur, faire en sorte
que les constructeurs par copie/déplacement soient noexcept.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
)
Opérateur noexcept (C++
11
Le rôle de l’opérateur noexcept permet de vérifier à la compilation que
l’expression passée à l’opérateur ne génère pas d’exception.
Exemple :
void may_throw ( ) ;
void no_throw ( ) noexcept ;
/ / noexcept ( may_throw ( ) ) = f a l s e
/ / noexcept ( no_throw ( ) ) = t r u e
Plus précisément,
• l’expression passée entre parenthèse n’est pas évaluée,
• il est vérifié que toutes les fonctions nécessaires à l’évaluation de
l’expression sont toutes noexcept (= déclarée avec le spécificateur).
Exception dynamique
noexcept
Spécificateur
Opérateur
Usages de noexcept
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
33/ 58
Cet opérateur est généralement utilisé de la manière suivante :
• dans un code actif, permet de vérifier qu’une branche de l’exécution est
bien complètement en noexcept.
• avec static_assert, permet de faire en sorte que des fonctions ne
compilent que si des spécifications noexcept sont bien vérifiées.
• dans les templates, afin de vérifier que des spécifications noexcept sont
bien vérifiées.
Exemple : template < class T> T add ( const T& x , const T& y )
noexcept ( noexcept ( x + y ) &&
s t d : : i s _ n o t h r o w _ m o v e _ c o n s t r u c t i b l e <T > : : v a l u e )
{ return x + y ; }
INFO0402 :
Méthodes de
programmation
orientée objet
Usages de noexcept
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Exception dynamique
noexcept
Spécificateur
Opérateur
Usages de noexcept
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
34/ 58
Éviter d’utiliser ce spécificateur à moins qu’il soit clair qu’aucun
usage raisonnable (courant ou futur) de la fonction ne peut
générer d’exception,
Toutes les fonctions de calcul intensif devraient être marquée
nonexcept afin de n’avoir la gestion des exceptions que sur les
fonctions haut-niveaux :
• Les conditions d’erreur sur les fonctions bas-niveau relèvent
plus de de l’assert (code actif seulement en mode debug).
• L’utilisation d’une exception exige un code actif (les tests de
condition d’erreur) y compris après la phase de debug.
• Autant que possible, essayer de faire en sorte que les
conditions d’erreur soient évacuées de la partie la plus
active du code.
INFO0402 :
Méthodes de
programmation
orientée objet
Cadre approprié d’utilisation des exceptions
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Remarque : le standard ne précise pas comme les exceptions doivent être
implémentée, mais seulement le comportement qu’elles doivent respecter.
⇒ dépend l’implémentation dans le compilateur
Potentiellement, la gestion interne des exceptions :
• utilise une quantité de mémoire significative pour stocker les exceptions,
et les informations nécessaires au stack unwinding,
Spécification
d’exception
• si aucune exception est levée, ne génère quasiment pas de surcoût en
Cadre
d’utilisation
• si une exception est levée, le surcoût peut être significatif.
temps d’exécution
Introduction
Cadre d’utilisation
Exceptions contre
assertions
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
35/ 58
En moyenne, si les exceptions sont utilisées, les codes sont :
• plus gros (5 à 10%),
• sur un compilateur moderne, aussi rapide si aucune exception n’est
lancée (5 à 10% plus lent sinon).
Ceci dépend évidemment de compilateur, et de l’implémentation utilisée.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Introduction
Cadre d’utilisation
Exceptions contre
assertions
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
36/ 58
Cadre approprié d’utilisation des exceptions
Conséquences pratiques : l’utilisation des exceptions
• n’est pas appropriée en toutes circonstances,
• est déconseillée dans toutes les applications ou la mémoire
est limitée (systèmes embarqués),
• est déconseillée dans toutes les applications ou les
performances sont critiques (système temps-réel, rendu 3D,
...), à savoir pour lesquels les opérations doivent se terminer
dans un temps spécifique maximum déterminé,
car la longueur du chemin qui est pris pour gérer l’exception
peut être difficile à majorer
• ne doit être effectuée que dans des situations qui ne se
produisent pas fréquemment (ne pas les utiliser pour
retourner des erreurs anodines ou des warnings),
• ne doit être effectuée qu’avec des codes "exception safe" (la
plupart des codes ne le sont pas)
INFO0402 :
Méthodes de
programmation
orientée objet
Exceptions contre assertions
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Introduction
Cadre d’utilisation
Exceptions contre
assertions
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
37/ 58
On a vu que les assertions étaient une autre manière de vérifier que le cadre
d’exécution d’une méthode était approprié.
Savoir si les invariants doivent être assurés par les exceptions ou les
assertions est controversé,
Règle : n’utiliser les exceptions que pour les erreurs pour lesquels une
récupération est possible,
Si la condition d’erreur détectée indique une sérieuse erreur de programmation
(corruption du heap ou stack), l’état du programme ne permet de toute façon
pas de continuer l’exécution, et l’utilisation d’une exception n’est alors pas
envisageable.
Attention : tendance forte pour les programmeurs débutants à utiliser les
exceptions à de nombreux endroits où elles sont très discutables ou clairement
inappropriés.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Garantie "exception
safe"
Idiome RAII
Implémentation
interne
Exceptions
avancées
Résumé
Problème d’utilisation : introduction
La levée d’exception pose potentiellement des problèmes importants de fuite
mémoire, et plus généralement les fuite de ressources.
1 Que se passe-t-il dans le code suivant si useBuffer() lance une
exception ?
void u s e B u f f e r ( char * b u f ) { . . . }
void doWork ( ) {
char * b u f = new char [ 1 0 2 4 ] ;
useBuffer ( buf ) ;
delete [ ] buf ;
}
Réponse : le code qui libère buf n’est jamais atteint.
2 Pour la structure du code suivant, est-elle fondamentalement
dangeureuse (sous l’hypothèse qu’aucune fonction ci-dessus ne gère les
exceptions, et que celle-ci est reléguée dans les fonctions appelantes) ?
void f u n c ( ) {
initialize ();
do_work ( ) ;
cleanup ( ) ;
}
Ce code n’est pas "exception safe" : si do_work() lance une exception,
cleanup() n’est jamais effectué.
38/ 58
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Garantie "exception
safe"
Idiome RAII
Implémentation
interne
Exceptions
avancées
Résumé
Problème d’utilisation des exceptions : exemples
Exemple 1 :
T& T : : operator =(T const& x ) {
i f ( t h i s ! = &x ) {
t h i s −>~T ( ) ; / / d e s t r o y i n p l a c e
new ( t h i s ) T ( x ) ; / / c o n s t r u c t i n p l a c e
}
return * this ;
}
Problème : si la construction par copie échoue, alors laisse this détruit, donc
problème.
Exemple 2 :
template <class T> T Stack <T > : : pop ( ) {
i f ( t o p < 0 ) throw " poponempty s t a c k " ;
r e t u r n v [ top − − ];
}
Problème : le retour de l’objet se fait par valeur, donc appelle le constructeur
par copie, et si ce dernier lance une exception, il n’y a aucun moyen de
récupérer la valeur.
Solution : faire un T& top() qui permet de récupérer la valeur, et void pop()
qui permet de dépiler. On sépare donc l’opération en deux étapes.
39/ 58
INFO0402 :
Méthodes de
programmation
orientée objet
Exception safe
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Garantie "exception
safe"
Idiome RAII
Implémentation
interne
Exceptions
avancées
Résumé
40/ 58
De façon à ce que le mécanisme d’exception soit utile, il est nécessaire de
savoir ce qui peut être supposé sur l’état d’un programme lorsqu’une exception
est lancée.
On dit alors qu’une opération est "exception safe" si elle laisse le programme
dans un état valide lorsque celle-ci s’est terminée en raison d’une exception.
Il existe différents garantie d’un composant vis-à-vis des exceptions :
• garantie de base : les invariants sont préservés, et aucune ressource
n’est perdue.
toutes les données stockées contiennent des valeurs valides, mais il peut
y avoir des effets de bord (valeurs modifiées par l’exception).
• garantie forte = garantie de base + pas d’effets de bord
toutes les données conservent leurs valeurs avant exception.
• garantie nothrow : garantie qu’aucune exception ne sera levée dans
toutes les situations. Si une exception est levée, elle sera traitée de
manière interne et non visible depuis l’extérieur du composant.
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Garantie "exception safe"
Règles pour la gestion d’exception :
• supposer que toutes les fonctions peuvent lever une exception, sauf si
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Garantie "exception
safe"
Idiome RAII
l’on a la connaissance certaine du contraire.
• tout code doit au minimum fournir une garantie de base,
• la garantie nothrow doit toujours être garantie par les destructeurs, les
constructeurs et assignations par déplacement, les opérations de swap.
• ne fournir une garantie forte que lorsqu’elle est naturelle, et qu’elle n’est
pas plus coûteuse qu’une garantie de base,
Exemples dans la STL :
• garantie forte : push_back pour un conteneur STL, insert dans une
std::list
• garantie nothrow : swap entre deux conteneurs STL, pop_back pour un
conteneur STL.
Implémentation
interne
Notes : voir section exception de la documentation des méthodes.
Exceptions
avancées
Extrait de la section Exception de std::vector::push_back
Résumé
41/ 58
I f an e x c e p t i o n i s thrown , t h i s f u n c t i o n has no e f f e c t ( s t r o n g e x c e p t i o n guarantee ) .
I f T ’ smove c o n s t r u c t o r i s n o t noexcept andT i s n o t C o p y I n s e r t a b l e i n t o * t h i s ,
v e c t o r w i l l use t h e t h r o w i n g move c o n s t r u c t o r . I f i t throws , t h e guarantee i s
waivedand t h e e f f e c t s are u n s p e c i f i e d . ( s i n c e C++11)
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Garantie "exception
safe"
Idiome RAII
Implémentation
interne
Exceptions
avancées
Résumé
42/ 58
Idiome RAII
RAII est l’acronyme de Resource Acquisitation Is Initialization.
L’idiome RAII est utilisé pour éviter les fuite de ressources et fournir une sureté
d’exception.
Principe :
• faire toujours en sorte que la ressource soit détenue par un objet (dit
objet RAII),
• la durée de vie de la ressource est directement liée à la vie de l’objet
RAII,
• la ressource est acquise lors de la création de l’objet RAII (i.e. dans son
constructeur),
• la ressource est libérée lors de la destruction de l’objet RAII (i.e. dans
son destructeur).
Du fonctionnement des exceptions lors du stack unwinding, il apparaît que les
objets locaux sont toujours correctement libérés.
En conséquence, tout objet RAII qui contient une ressource devra être un objet
local (au niveau du code où il est nécessaire) de façon à ce que son
destructeur soit toujours appelé (si nécessaire) lors du traitement de
l’exception.
INFO0402 :
Méthodes de
programmation
orientée objet
Idiome RAII
Pascal Mignot
Exemple :
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Garantie "exception
safe"
Idiome RAII
Implémentation
interne
Exceptions
avancées
Résumé
43/ 58
/ / peut l a n c e r une e x c e p t i o n
void u s e B u f f e r ( char * b u f ) { . . . }
/ / s i u s e B u f f e r l a n c e une e x c e p t i o n , l a r e s s o u r c e p o i n t é e
/ / par b u f n ’ e s t j a m a i s l i b é r é e
void doWorkUnsafe ( ) {
char * b u f = new char [ 1 0 2 4 ] ;
useBuffer ( buf ) ;
delete [ ] buf ;
}
/ / idiome RAII : l a r e s s o u r c e e s t associée à une v a r i a b l e
/ / l o c a l e b u f q u i l i b è r e l a r e s s o u r c e dans son d e s t r u c t e u r
/ / ( appelé l o r s du s t a c k unwinding )
void doWorkSafe ( ) {
s t d : : u n i q u e _ p t r <char [ ] > b u f
= s t d : : make_unique<char [ ] > ( 1 0 2 4 ) ;
useBuffer ( buf ) ;
}
INFO0402 :
Méthodes de
programmation
orientée objet
Implémentation interne des exceptions
Pascal Mignot
Essentiellement, on a besoin de stocker :
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
44/ 58
• l’objet associé à l’exception :
• en général, pas pratique de créer l’objet associé à l’exception dans
le stack, puisque l’objet a fréquemment besoin d’être propagé à
travers de nombreux niveaux de la pile d’appel,
• il est normalement petit, mais une partie peut être stockée dans le
heap si plus de mémoire est nécessaire.
• les informations nécessaires pour le stack unwinding :
• elles doivent contenir suffisamment d’information pour permettre de
libérer tous les objets temporaires, et remonter la pile d’appel
jusqu’à le handler capable de traiter l’exception,
• deux stratégies principales :
• stratégie basée sur le stack
• stratégie basée sur une table
INFO0402 :
Méthodes de
programmation
orientée objet
Implémentation interne des exceptions
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
Stratégie basée sur le stack
• Principe :
• les informations sont placées dans le stack (liste des
destructeurs à exécuter, et handlers des exceptions à
exécuter lorsqu’une exception est lancée),
• lorsqu’une exception est lancée, on parcours le stack en
exécutant les destructeurs jusqu’à ce qu’un handler soit
trouvé.
• Remarques :
• utilise du temps à l’exécution pour pousser dans le stack les
informations nécessaires à cette gestion.
• utilise de la place dans le stack (qui peut être conséquente)
en plus des informations pour exécuter le programme,
⇒ gaspillage de ressources lorsqu’aucune exception n’est
lancée.
45/ 58
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
Implémentation interne des exceptions
Stratégie basée sur une table
• Principes :
• stocke les information pour permettre le stack unwind dans
des tables statiques à l’extérieur du stack,
• la pile d’appel est utilisée pour déterminer le point
d’exécution courant à chaque niveau,
• une recherche dans les tables statiques permettent de
déterminer où l’exception lancée sera traitée, et quels sont
destructeurs à exécuter (déduit du point d’éxecution
courant).
• Remarques :
• utilise moins d’espace sur le stack (seulement la position
des try-bloc),
• requiert un stockage important pour les tables statiques,
• pas de coût supplémentaire à l’exécution
⇒ gaspillage de ressources lorsqu’aucune exception n’est
lancée.
46/ 58
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Implémentation interne des exceptions
Construction de la table statique :
• une fonction est découpée en segments de code consécutifs (chaque
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
segment est numéroté).
voir ci-dessous comment (blocs try + variables locales)
• données à disposer lors de gestion des exceptions :
à chaque étage de la pile des appels,
• exception handler (pointeur de fonction) sur la fonction à utiliser
pour gérer les exceptions.
• une liste de tryblocks qui contient pour chaque try :
1 le plage de segments de code qui correspond au try.
2 une liste de catchblock qui contient pour chaque catchblock :
Implémentation
interne
• le type d’exception géré par ce catchblock
• un pointeur vers le code du catchblock
Exceptions
avancées
généré par le compilateur.
Résumé
47/ 58
• une liste de nettoyage qui contient la liste des variables locales à
libérer ainsi que le pointeur vers la fonction de libération
(destructeur). ordonnée, générée par le compilateur
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Implémentation interne des exceptions
Utilisation de la table statique lors de la génération d’une exception :
1 à partir du numéro du segment, recherche dans la liste des tryblocks à
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
quel tryblock appartient l’instruction qui a généré l’exception.
2 si il existe un tel tryblock alors
(a) recherche d’un catch permettant de gérer cette exception (RTTI)
dans la liste des catchblocks.
(b) s’il en existe un, alors aller en 4.
3 si on est au sommet de la pile
alors sortie du programme sur erreur (exception non gérée)
sinon remonter à l’appellant dans la pile des appels, et aller en 1.
4 dépiler la pile des appels pour se trouver sur le "frame block" contenant
le catch.
utiliser la liste de nettoyage de chaque "frame block" pour libérer la
mémoire locale allouée au point courant de l’exécution.
5 exécuter le catchblock trouvé en 2.b.
6 poursuivre le code qui suit le catchblock.
48/ 58
INFO0402 :
Méthodes de
programmation
orientée objet
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
Résumé
49/ 58
Implémentation interne des exceptions
La gestion d’une exception dans une méthode ajoute donc :
• un prologue (support des exceptions).
• un épilogue (code à exécuter en cas d’exception).
pour toutes les fonctions (y compris celle qui n’ont pas de try/catch).
noexcept indique que la fonction ne doit pas contenir cet ajout.
En terme de performance :
• L’exécution du prologue est court :
ajout essentiellement de pointeurs de fonction et de pointeurs vers des
structures précompilées.
• L’exécution de l’épilogue (gestion d’une exception lancée) peut
prendre du temps :
• identification du try et du catch avec recherche éventuelle
dans la pile, nettoyage des variables locales allouées
jusqu’au point d’exception.
• d’autant plus que l’exception n’est pas gérée dans la
fonction qui l’engendre.
ajouter le coût du constructeur/destructeur de l’objet de
gestion de l’exception (si géré avec un objet).
INFO0402 :
Méthodes de
programmation
orientée objet
Outils avancés pour les exceptions
Pascal Mignot
Introduction
Gestion
classique
Bases
Exception STL
Spécification
d’exception
Cadre
d’utilisation
Problème
d’utilisation
Implémentation
interne
Exceptions
avancées
stack unwinding
Stockage et
récupération
Exceptions
imbriquées
Terminate
Résumé
50/ 58
Dans cette partie, nous abordons des outils avancés pour la
gestion d’exception, à savoir :
• Comment détecter que la portion de code actuellement
exécutée s’effectue lors d’un "stack unwinding" ?
• Comment stocker, récupérer et transférer une exception ?
• Comment gérer l’imbrication exception ?
• Comment gérer la terminaison d’un code ?