• Aucun résultat trouvé

Levée (automatique) et propagation d’une exception

6.3 EXCEPTIONS 1 Motivation

6.3.3 Levée (automatique) et propagation d’une exception

Pendant l’exécution d’un programme, une exception peut être levée par une erreur dans une instruction. Dans les cas simples présentés dans l’exemple 6.14 l’exception, toujours Constraint_Error, provoque la fin du programme. En effet, lors de la levée d’une exception, les instructions restantes du corps sont abandonnées et, dans le cas d’un programme principal, celui-ci est quitté avec, en général, un message d’erreur qui dépend de l’implémentation.

Exemple 6.14 Cas simples de levée de l’exception Constraint_Error.

-- Premier exemple

procedure Exemple_6_14_1 is

Nombre : Positive;

begin -- Exemple_6_14_1

...

Nombre := 0; -- 0 n'appartient pas au sous-type

Positive ... end Exemple_6_14_1; --- -- Deuxieme exemple procedure Exemple_6_14_2 is Nombre : Integer := 0;

procedure P ( Valeur : in Natural ) is ... end P; begin -- Exemple_6_14_2

...

P ( Nombre – 3 ); -- –3 n'appartient pas au sous-type Natural

...

end Exemple_6_14_2;

Une exception peut être levée non seulement lors d’une instruction mais aussi à l’élaboration (§ 3.9.3) d’une déclaration. L’exemple 6.15, qui n’est pas écrit de manière très structurée (!), va permettre d’illustrer ces deux situations et, à nouveau

EXCEPTIONS 142

et dans les détails, le phénomène de propagation d’une exception. Exemple 6.15 Levées d’exceptions.

with Ada.Text_IO; use Ada.Text_IO;

with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; -- ...

procedure Principal is

Nombre : Integer; -- Nombre dont on calcule les

puissances

--- -- ...

function Exposant_Maximum return Natural is

Exposant : Natural; -- Exposant jusqu'auquel seront

-- calculees les puissances

begin -- Exposant_Maximum

Put ( "Veuillez donner l'exposant maximum: " );

Get ( Exposant ); -- 1 Skip_Line; return Exposant; end Exposant_Maximum; --- -- ...

procedure Afficher_Puissances ( Nombre : in Integer ) is

Limite : Positive := Exposant_Maximum;-- 2

begin -- Afficher_Puissances

Put_Line ( "Et voici la liste des puissances:" ); -- Affichage de toutes les puissances souhaitees

for N in Natural'First..Limite loop

if N rem 8 = 0 then -- Maximum huit puissances par ligne

New_Line; end if; Put ( Nombre ** N ); -- 3 end loop; New_Line; end Afficher_Puissances; begin -- Principal -- Presentation du programme

Put ( "Calcul des puissances d'un nombre entier jusqu'a " ); Put_Line ( "un exposant maximum." );

-- Lire le nombre dont on veut calculer les puissances Put ( "Veuillez donner ce nombre: " );

Get ( Nombre ); -- 4

Skip_Line;

-- Affichage des puissances du nombre donne par l'utilisateur Afficher_Puissances ( Nombre );

EXCEPTIONS 143

end Principal;

Les lignes suivies d’un commentaire formé d’un seul numéro sont celles où une exception est susceptible d’être levée. En effet, si l’utilisateur donne un nombre hors de l’intervalle des valeurs du type Integer, Data_Error (sect. 12.5) sera levée à la ligne 4 car Nombre est du type Integer et si l’utilisateur donne un exposant maximum négatif, elle sera levée à la ligne 1 car Exposant est du sous- type Natural. Si l’utilisateur donne un exposant nul, Constraint_Error sera levée à la ligne 2 car Limite est du sous-type Positive et si l’exposant est assez grand, elle sera levée à la ligne 3 car la valeur de l’expression Nombre ** N sera hors de l’intervalle des valeurs du type Integer et ne pourra donc pas être passée en paramètre à la procédure Put.

Que devient l’exception après sa levée? Elle interrompt dans tous les cas le cours normal de l’exécution et provoque soit un «saut» à la fin du corps (sect. 4.7) contenant la ligne (cas 1, 3 et 4) soit l’abandon de la partie déclarative (cas 2). La suite dépend de la ligne ayant provoqué sa levée. En l’absence de tout traitement (§ 6.3.4), comme c’est le cas dans notre exemple, si l’exception provient de la ligne 4, alors le programme Principal se termine et l’environnement Ada va afficher un message d’erreur contenant entre autres le nom de l’exception. Si l’exception provient de la ligne 3, la procédure Afficher_Puissances se termine et l’exception est à nouveau levée au point d’appel de ladite procédure (ce qui provoquera la fin du programme principal comme cela vient d’être mentionné). Ce phénomène est appelé propagation (propagation) de l’exception.

Si l’exception provient de la ligne 2, la procédure Afficher_Puissances se termine sans avoir commencé l’exécution de sa première instruction et l’exception est propagée au point d’appel dans le programme principal. Finalement, si l’exception provient de la ligne 1, Exposant_Maximum se termine sans retourner de valeur et l’exception est propagée au point d’appel, c’est-à-dire à la ligne 2.

Il faut encore insister sur le fait qu’une exception est levée à l’exécution et que sa propagation est donc dynamique: l’exception remonte selon les appels de procé- dure ou de fonction jusqu’au programme principal (en l’absence de traitement). 6.3.4 Traitement d’une exception

Il n’est jamais agréable de voir une application se terminer brutalement par un message d’erreur. Le traitement (handling) d’une exception en Ada consiste à intercepter l’exception pour l’empêcher de remonter jusqu’au système d’ex- ploitation et à rétablir la situation en supprimant ou en évitant la cause de l’erreur. Mais il faut toujours garder à l’esprit qu’il vaut mieux voir apparaître une erreur qu’obtenir des résultats faux à l’exécution d’une application (note 6.1). Il ne faut donc réagir et supprimer l’exception que si l’application est capable d’y faire face,

EXCEPTIONS 144

de retrouver un état cohérent.

Le traitement d’une exception consiste à compléter la fin d’un corps de procédure, de fonction, ou encore de bloc (§ 6.3.5), immédiatement avant le end

final, par une zone particulière appelée traite-exception (exception handler). Elle a la forme suivante, ressemblant à celle de l’instruction case (§ 5.3.2):

exception

when choix_1 => traitement_1;-- premiere branche when choix_2 => traitement_2;-- deuxieme branche

...

when others => autre_traitement;-- derniere branche

avec

• un nouveau mot réservé exception qui débute le traite-exception, après

la dernière instruction du corps et avant le end final;

• les choix_n formés d’un ou de plusieurs identificateurs d’exception séparés par des barres verticales;

• les traitements_n composés d’une ou de plusieurs instructions;

• le mot réservé others qui représente tous les autres cas d’exceptions; cette

branche est optionnelle.

Lorsqu’une exception est levée dans les instructions du corps contenant un tel traite-exception, l’exécution est transférée dans la branche comportant le nom de l’exception si celle-ci est mentionnée explicitement ou dans la dernière branche si celle-ci est présente. Puis l’exécution se poursuit normalement dans le traitement de la branche et l’exception est éliminée. Si le nom de l’exception ne fait pas partie des choix et s’il n’y a pas de branche commençant par when others, l’exception

est propagée, comme si le traite-exception n’existait pas.

Il faut d’ailleurs noter que si l’exécution d’une instruction du traite-exception lève une exception (!), celle-ci est également propagée.

Il n’est pas possible de revenir là où l’exception s’est produite, sauf par un artifice utilisant la structure de bloc, car le traitement dans une branche du traite- exception remplace le reste du corps et termine ainsi l’exécution du corps. Un exemple d’utilisation d’un bloc pour répéter une instruction ayant provoqué une exception est donné dans le paragraphe 6.3.5.

Exemple 6.16 Modification de la fonction et de la procédure de l’exemple 6.15.

-- ...

function Exposant_Maximum return Natural is

Exposant : Natural; -- Exposant jusqu'auquel seront

-- calculees les puissances

begin -- Exposant_Maximum

EXCEPTIONS 145 Get ( Exposant ); -- 1 Skip_Line; return Exposant; exception when Constraint_Error =>

Skip_Line; -- Eliminer les caracteres restants

Put_Line ( "Exposant negatif, 1 arbitrairement." );

return 1; when others =>

Skip_Line; -- Eliminer les caracteres restants

Put_Line ( "Exposant errone, 20 arbitrairement." );

return 20; end Exposant_Maximum;

--- -- ...

procedure Afficher_Puissances ( Nombre : in Integer ) is

Limite : Positive := Exposant_Maximum;-- 2

begin -- Afficher_Puissances

Put_Line ( "Et voici la liste des puissances:" ); -- Affichage de toutes les puissances souhaitees

for N in Natural'First..Limite loop

if N rem 8 = 0 then -- Maximum huit puissances par ligne

New_Line; end if; Put ( Nombre ** N ); -- 3 end loop; New_Line; exception when Constraint_Error =>

Put_Line ( "Puissance trop grande. Fin de l'affichage" );

end Afficher_Puissances;

Avec les modifications de l’exemple 6.16, la levée de l’exception Constraint_Error à la ligne 1 provoque le transfert de l’exécution dans le traite- exception de la fonction où un message d’avertissement est affiché, puis la fonction se termine en retournant la valeur arbitraire 20. Avec ces mêmes modifications, la levée de l’exception Constraint_Error à la ligne 3 provoque le transfert de l’exécution dans le traite-exception de la procédure où un message d’avertissement est affiché, puis le programme se termine normalement.

Mais malgré ces modifications, l’exception Constraint_Error levée à la ligne 2 n’est pas traitée par le traite-exception placé à la fin de la procédure Afficher_Puissances. En effet, une exception levée par une déclaration d’un

EXCEPTIONS 146

sous-programme est toujours propagée au point d’appel du sous-programme, que celui-ci ait ou non un traite-exception. Pour éviter ce problème il faudrait naturellement remplacer Positive par Natural dans la déclaration.

Finalement, il est intéressant de voir comment traiter les cas des lignes 1 et 4 si l’on veut que le programme s’exécute sans erreur, avec des valeurs autorisées. La solution va nécessiter l’utilisation de la notion de bloc.