COURS ADA 95 - irit.fr · COURS ADA 95 Excluant POO, Task ... Les plus gros frais de maintenance,...

80
1 _____________________________________________________________ ____________________________________________ COURS ADA 95 Excluant POO, Task ____________________________________________ ____________________________________________________________ Véronique Gaildrat Université Paul Sabatier, IRIT Porte : 218, téléphone : 05 61 55 74 31 Email : [email protected] Véronique Gaildrat Support de cours ADA 95

Transcript of COURS ADA 95 - irit.fr · COURS ADA 95 Excluant POO, Task ... Les plus gros frais de maintenance,...

1

_________________________________________________________________________________________________________

COURS ADA 95Excluant POO, Task

________________________________________________________________________________________________________

Véronique GaildratUniversité Paul Sabatier, IRITPorte : 218, téléphone : 05 61 55 74 31Email : [email protected]

Véronique Gaildrat

Support de cours ADA 95

2

Table des matières1. INTRODUCTION................................................................................................................................ 5

1.1 ORIGINES DE ADA.............................................................................................................................. 51.1.1 Historique................................................................................................................................... 51.1.2 Objectifs du langage.................................................................................................................. 5

1.2 ELÉMENTS DE BASE D'UN PROGRAMME ADA 95................................................................................ 51.2.1 Eléments lexicaux, séparateurs et délimiteurs........................................................................... 51.2.2 Littéraux numériques.................................................................................................................. 6

1.2.2.1 Littéraux entiers...................................................................................................................... 61.2.2.2 Littéraux réels......................................................................................................................... 6

1.2.3 Mots réservés.............................................................................................................................. 61.2.4 Identificateurs............................................................................................................................. 61.2.5 Commentaires et Pragmas......................................................................................................... 71.2.6 Opérateurs et expressions.......................................................................................................... 71.2.7 Evaluation des expressions......................................................................................................... 71.2.8 Instructions (sauf return et raise).............................................................................................. 8

1.2.8.1 Instruction d'affectation.......................................................................................................... 81.2.8.2 Instruction nulle..................................................................................................................... 81.2.8.3 Instruction bloc....................................................................................................................... 81.2.8.4 Instructions composées.......................................................................................................... 8

1.2.9 Sous-programmes..................................................................................................................... 101.2.10 Instruction return..................................................................................................................... 111.2.11 Paquetages................................................................................................................................ 111.2.12 Variable et Type d'une variable................................................................................................ 121.2.13 Caractères et chaînes littérales................................................................................................ 13

1.2.13.1 Caractères littéraux........................................................................................................... 131.2.13.2 Chaînes littérales.............................................................................................................. 13

1.2.14 Paquetages de manipulation des caractères et des chaînes..................................................... 131.2.15 Exemple d'un programme simple en ADA................................................................................ 13

2. LES TYPES........................................................................................................................................ 14

2.1 NOTION DE TYPE............................................................................................................................... 142.1.1 Définition.................................................................................................................................. 142.1.2 Règles....................................................................................................................................... 142.1.3 Classification de types ADA..................................................................................................... 142.1.4 Les modèles de types................................................................................................................ 152.1.5 Déclaration de types................................................................................................................. 152.1.6 Contraintes............................................................................................................................... 15

2.2 SOUS-TYPES ET TYPES DÉRIVÉS......................................................................................................... 162.2.1 Sous-types................................................................................................................................. 162.2.2 Types dérivés............................................................................................................................ 16

2.3 TYPES SCALAIRES.............................................................................................................................. 192.3.1 Attributs communs à tous les types scalaires........................................................................... 192.3.2 Les types discrets...................................................................................................................... 19

2.3.2.1 Attributs spécifiques aux types discrets............................................................................... 192.3.2.2 Les types entiers................................................................................................................... 202.3.2.3 Les types Modular (dits types modulo)................................................................................ 21

Véronique Gaildrat

Support de cours ADA 95

3

2.3.2.4 Les types énumérés............................................................................................................... 212.3.3 Les types réels........................................................................................................................... 232.3.4 Les types décimaux................................................................................................................... 24

2.4 TYPES COMPOSÉS PRINCIPAUX (TABLEAUX ET ARTICLES)................................................................. 252.4.1 Types tableaux......................................................................................................................... 25

2.4.1.1 Variables tableaux................................................................................................................ 252.4.1.2 Types tableaux non contraints.............................................................................................. 252.4.1.3 Contraintes d'indices............................................................................................................ 262.4.1.4 Agrégats................................................................................................................................ 262.4.1.5 Attributs de tableaux............................................................................................................ 262.4.1.6 Tableaux non contraints et Passage de paramètres............................................................... 272.4.1.7 Types tableaux contraints..................................................................................................... 272.4.1.8 Sous-types de tableaux......................................................................................................... 282.4.1.9 Opérations sur un type tableau............................................................................................. 282.4.1.10 Caractères et Chaînes de Caractères................................................................................. 292.4.1.11 Conversions explicites entre types tableaux.................................................................... 30

2.4.2 Types articles............................................................................................................................ 312.4.2.1 Exemple................................................................................................................................ 322.4.2.2 Ensemble des opérations...................................................................................................... 322.4.2.3 Sélection d'un composant : notation pointée........................................................................ 322.4.2.4 Agrégats................................................................................................................................ 322.4.2.5 Valeur par défaut d'un composant........................................................................................ 322.4.2.6 Articles à partie discriminante.............................................................................................. 322.4.2.7 Articles à variantes............................................................................................................... 332.4.2.8 Sous-types d'articles............................................................................................................. 332.4.2.9 Défauts et contraintes........................................................................................................... 342.4.2.10 Types dérivés d'articles à discriminant............................................................................ 35

2.4.3 Composition de types................................................................................................................ 352.5 LES TYPES ACCESS............................................................................................................................ 36

2.5.1 Type access spécifiques à un pool.......................................................................................... 362.5.2 Quand utiliser les types access .............................................................................................. 372.5.3 Comment créer une structure arborescente............................................................................. 382.5.4 Utilisation "historique" de l'arborescence............................................................................... 382.5.5 Contraintes sur les types access ............................................................................................ 382.5.6 Types access généralisés...................................................................................................... 402.5.7 Paramètres access de sous-programmes................................................................................ 412.5.8 Accès à un sous-programme.................................................................................................... 41

3. UNITÉS DE BIBLIOTHÈQUE........................................................................................................ 43

3.1 SOUS-PROGRAMMES.......................................................................................................................... 433.1.1 Modes de passage de paramètres............................................................................................. 433.1.2 Paramètres nommés et paramètres par défauts....................................................................... 443.1.3 Sous-programmes de type procédure....................................................................................... 453.1.4 Sous-programmes de type fonction.......................................................................................... 453.1.5 Notion de surcharge................................................................................................................. 463.1.6 Applications des sous-programmes ADA................................................................................. 47

3.1.6.1 Programmes principaux....................................................................................................... 473.1.6.2 Contrôle fonctionnel............................................................................................................. 47

Véronique Gaildrat

Support de cours ADA 95

4

3.1.6.3 Opération primitives sur des Types de Données Abstraits................................................... 483.2 PAQUETAGES (PACKAGE ADA)......................................................................................................... 49

3.2.1 Définition et généralités........................................................................................................... 493.2.1.1 Définition............................................................................................................................. 493.2.1.2 Spécification et corps de package........................................................................................ 49

3.2.2 Types classiques de packages................................................................................................... 503.2.2.1 Collection de déclarations communes.................................................................................. 503.2.2.2 Exemple de bibliothèque de sous-programmes.................................................................... 503.2.2.3 Exemple de définition de package exportant un nouveau type ( !! /= TDA)....................... 513.2.2.4 Exemple de définition de machine abstraite........................................................................ 51

3.2.3 Clients de packages.................................................................................................................. 523.2.4 Résumé...................................................................................................................................... 543.2.5 Types privés.............................................................................................................................. 543.2.6 Types limités............................................................................................................................. 553.2.7 Types limites privés................................................................................................................... 56

3.3 SURNOMMAGE.................................................................................................................................. 573.4 COMPILATION SÉPARÉE..................................................................................................................... 58

3.4.1 Dépendances............................................................................................................................ 583.4.2 Sous-unités................................................................................................................................ 593.4.3 Bibliothèque hiérarchique........................................................................................................ 59

3.4.3.1 Règles de visibilité............................................................................................................... 593.4.3.2 Exemple................................................................................................................................ 60

3.4.4 Impact méthodologique............................................................................................................ 62

4. EXCEPTIONS.................................................................................................................................... 63

4.1 DÉCLARATION D'UNE EXCEPTION...................................................................................................... 634.1.1 Exceptions prédéfinies.............................................................................................................. 63

4.2 RÉCUPÉRATION ET TRAITEMENT D'UNE EXCEPTION.......................................................................... 644.3 INSTRUCTION RAISE.......................................................................................................................... 644.4 OCCURRENCES D'EXCEPTIONS........................................................................................................... 66

4.4.1 Récupération............................................................................................................................ 664.4.2 Levée d'une occurrence d'exception......................................................................................... 67

4.5 PACKAGE ADA.EXCEPTIONS............................................................................................................. 67

5. LA GÉNÉRICITÉ............................................................................................................................... 68

5.1 SYNTAXE........................................................................................................................................... 695.2 PARAMÈTRE GÉNÉRIQUE "VARIABLE"............................................................................................... 70

5.2.1 Passage noté : in...................................................................................................................... 705.2.2 passage noté : in out................................................................................................................ 70

5.3 PARAMÈTRE GÉNÉRIQUE "TYPE"..................................................................................................... 705.4 QUELQUES TYPES FORMELS GÉNÉRIQUES.......................................................................................... 715.5 EXEMPLE : ADA.TEXT_IO................................................................................................................. 725.6 PARAMÈTRES GÉNÉRIQUES "SOUS-PROGRAMMES"........................................................................... 735.7 PARAMÈTRE GÉNÉRIQUE "PAQUETAGE"............................................................................................ 735.8 UNE APPLICATION DES UNITÉS GÉNÉRIQUES...................................................................................... 745.9 UNITÉS DE BIBLIOTHÈQUE HIÉRARCHIQUES ET GÉNÉRIQUES............................................................ 75

6. ANNEXE : BIBLIOGRAPHIE........................................................................................................ 76

Véronique Gaildrat

Support de cours ADA 95

5

7. ANNEXE : L'ENVIRONNEMENT DE PROGRAMMATION.................................................. 77

7.1 PROGRAMME SOURCE....................................................................................................................... 777.2 COMPILATION ET ÉDITION DE LIENS.................................................................................................. 77

7.2.1 Unité de Bibliothèque et programme objet.............................................................................. 777.2.2 Compilation dans le cas où le programme est composé de plusieurs unités........................... 78

7.3 ORGANISATION DES FICHIERS............................................................................................................ 787.3.1 Répertoires............................................................................................................................... 797.3.2 Utilisation d'un Makefile......................................................................................................... 797.3.3 Utilisation des variables d'environnement............................................................................... 80

Véronique Gaildrat

Support de cours ADA 95

6

1. Introduction

1.1Origines de ADA

1.1.1Historique

• DOD : Budget logiciel du DOD 3 milliards de dollars pour supporter 400 langages et dialectes ... => nécessité d'améliorer les outils de programmation et la méthodologie.

• En janvier 1975, le Ministère Américain de la Défense (DOD) a convoqué un groupe d'experts, le High Order Language Working Group (HOLWG), pour trouver une solution problèmes de qualité et de coût des logiciels militaires. Les plus gros frais de maintenance, était pour les systèmes temps-réels embarqués.

• Objectif : langage de haut niveau permettant le développement de très grosses applications industrielles, ainsi que d'applications critiques (avionique, signalisation des chemins de fer, contrôle de processus et d'applications médicales…).

• Au printemps de 1977, une équipe française, dirigée par Jean Ichbiah, remporta l'appel d'offre. Le langage fut alors baptisé Ada, du nom d'Augusta Ada Byron, Comtesse Lovelace (1815-1852), le premier programmeur sur la "machine analytique" de Babbage.Après quelques modifications de moindre importance, le langage fut standardisé par l'ANSI1 en 1983.Il fallut attendre 1987 pour obtenir une norme française satisfaisante et la standardisation isO2.

• En 1988 le processus de révision de la norme a été relancé par, encore une fois le ministère américain de la Défense (DoD), et la révision a été conduite par l'Ada 9X Project Office, dirigé par Chris Anderson pour aboutir à l'approbation simultanée par l'ANSI et l'ISO au tout début de 1995.

1.1.2Objectifs du langage

Ada a été conçu pour répondre à un cahier des charges précis, dont l'idée directrice était de diminuer le coût des logiciels, en tenant compte de tous les aspects du cycle de vie :• Privilégier la maintenance par rapport à la facilité d'écriture: le coût de codage représente environ 6%

de l'effort de développement d'un logiciel; la maintenance représente plus de 60%.• Typage fort : Plus une erreur est diagnostiquée tôt, moins elle est coûteuse à corriger. Le langage fournit

des outils permettant de diagnostiquer beaucoup d'erreurs de cohérence dès la compilation. • Offrir un support à une industrie du composant logiciel: en fournissant des interfaces standard et des

garanties de portabilité des applications indépendamment des machine.• Permettre une programmation intrinsèquement sûre: ceci signifie qu'un programme doit être capable de

traiter toutes les situations anormales, y compris celles résultant d'erreurs du programme (auto-vérifications, fonctionnement en mode dégradé).

• Permettre des implémentations efficaces et des accès de bas niveau: exigence indispensable à la réalisation notamment de systèmes "temps réel".

• Ada dispose d'outils d'aide à la conception et à la mise en œuvre d'applications en permettant la programmation modulaire: paquetages, compilation séparée, généricité, exceptions.

1.2Eléments de base d'un programme ADA 95

1.2.1Eléments lexicaux, séparateurs et délimiteurs

Délimiteurs simples : & ' ( ) * + , - . / : ; < = > | Délimiteurs composés : => .. ** := /= >= <= << >> <>Séparateurs : espace tabulation fin de ligneCaractères autres :

• # " _• a..z A..Z• 0..9

Véronique Gaildrat

Support de cours ADA 95

1American National Standard Institute

2International Standard Organization

7

1.2.2Littéraux numériques

1.2.2.1Littéraux entiers 1900 = 19E2 = 190e1 1_000_000 2#100110010# = 256+32+16+2 = 306 16#1FAB# = 11 + 10*16 + 15*16*16 + 1*16*16*16 = 8107 1.2.2.2Littéraux réels 98.4 = 98.4e0 = 0.984e2 = 984.0e+2 984e-2 est illégal !

1.2.3Mots réservésAttention à ne pas déclarer des identificateurs égaux à un de ces termes : abort else new return abs elsif not reverse abstract end null accept entry select access exception separate aliased exit of subtype all or and for others tagged array function out task at terminate generic package then begin goto pragma type body private if procedure case in protected until constant is use raise declare range when delay limited record while delta loop rem with digits renames do mod requeue xor

1.2.4Identificateurs

Remarque : Règles d'écritures pour les identificateurs• Seuls les noms de types commencent par une majuscule• Les noms de variables et de sous programmes commencent toujours par une minuscule • Un paquetage porte le nom du type qu'il contient pré ou postfixé par _p

Contrairement au C, Ada n'est pas "case sensitive" : pour les identificateurs, majuscules et minuscules sont identiques.L'identificateur obéit à des règles de construction usuelles : il est composé de caractères alphabétiques, de chiffres et du caractère "_". La vérification est effectuée à la compilation.

Exemples d'identificateurs corrects :CENTIMES = centimes = Centimes /= cent_imes

COMPTER_LA_MONNAIE TABLE_128_A Ada UMO164g

Exemples d'identificateurs incorrects : fich&chips "&" RATE-of-FLOW "-" TIME__LAG "__"

Véronique Gaildrat

Support de cours ADA 95

8

77E2 "7"___ X_ "_ " tax rate " " => deux identificateurs with mot réservé

1.2.5Commentaires et Pragmas

Les commentaires sont indiqués par les caractères : --

-- ceci est un commentaire-- un commentaire commence aux doubles tirets et se poursuit jusqu'à la fin de la ligne-- les commentaires sont destinés aux lecteurs-- les pragmas sont destinés au compilateur

Il peut être nécessaire de donner des avis au compilateur. Ces remarques ne font pas partie du programme, mais sont faites à partir de constructions appelées pragma.Un certain nombre de pragmas sont prédéfinis :

Exemples de pragma : pragma optimize (identificateur); -- identificateur = time, space ou off,

pragma pack (String); -- incite le compilateur à compresser les données de ce type

1.2.6Opérateurs et expressions

Les opérateurs sont classés selon six niveaux de précédence :

Du plus faible : opérateurs logiques : and or xor opérateurs de relation : = /= < >= > >= in not in opérateurs additifs : + - & opérateurs unaires : + - opérateurs multiplicatifs : * / mod remAu plus fort : ** not abs

1.2.7Evaluation des expressions

On applique les opérateurs dont le niveau de précédence est le plus élevé.Les parenthèses imposent un ordre déterminé.• Cas général : l'ordre d'évaluation des deux opérandes d'un opérateur n'est pas défini. Tous les opérandes

d'une expression sont évalués. • Il existe deux formes dévaluation (and then et or else) où le premier opérande est évalué et le second

ne le sera que si c'est nécessaire pour obtenir le résultat du test.

Exemple : ensoleillé and then chaud où chaud ne sera évalué que si ensoleille est vrai ensoleillé or else chaud où chaud ne sera évalué que si ensoleillé est faux p.genre = f and then p.enfants >= 2 où le nombre d'enfants n'est testé que pour une personne de genre féminin p.genre = m or else p.enfants = 0 où le nombre d'enfants n'est testé que si la personne n'est pas de genre masculin

1.2.8Instructions (sauf return et raise)

1.2.8.1Instruction d'affectationvariable := expression; -- où type de la variable = type de l'expression

1.2.8.2Instruction nulle

Véronique Gaildrat

Support de cours ADA 95

9

null;Cette instruction qui semble ne faire absolument rien est obligatoire dans chaque bloc vide d'instruction.

1.2.8.3Instruction bloc

Une instruction bloc permet d'englober textuellement une séquence d'instructions accompagnée de déclarations locales et d'un traitement d'exception si on le désire. Un bloc peut être nommé ou non :

declare échange: -- déclaration de variables ou de types locaux declare -- au bloc temp : Float; begin begin -- instructions manipulant temp := v1; -- les variables locales et v1 := v2; -- les variables globales accessibles v2 := temp; [exception end échange; traite exception] end;

1.2.8.4Instructions composées

1. Instruction if :if condition_1 then …elsif condition_2 then …elsif condition_n then …else …end if;

Exemples d'utilisation d'instruction if : if discriminant > 0 then -- 2 racines réelles ... elsif discriminant < 0 then -- 2 racines imaginaires ... else -- discriminant = 0 -- une racine réelle double ... end if;On peut avoir 0, 1, ou plusieurs parties elsif.On peut avoir 0 ou 1 partie else.

2. Instruction case case expression discrète is -- les types discrets seront détaillés ultérieurement when choix_1 => -- ils sont constitués des types entiers et énumérés ... when choix_2 => … when choix_n => … when others => … end case;

Les choix doivent couvrir toutes les valeurs du type ou du sous-type de l'expression. Les expressions doivent être statiques pour pouvoir être évaluées à la compilation.others figure seul et en dernier. Il est utilisé quand on ne souhaite pas donner explicitement

Véronique Gaildrat

Support de cours ADA 95

10

l'ensemble des valeurs dans les choix.

Exemple d'utilisation d'instruction case : type jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); aujourdHui : Jour; … case aujourdHui is when lundi | mardi | mercredi | jeudi => -- ou lundi .. jeudi

travail; when vendredi =>

travail; sortie; when samedi | dimanche =>

null; -- instruction null dans le cas d'un choix où rien n'est à faire end case;

Il est possible d'avoir comme expression discrète un appel de fonction qui retourne une valeur discrète :with Ada.Text_io, Ada.Integer_Text_io; -- la clause with permet l'import d'autres unités

procedure essai isi : Integer;function carre (i : Integer) return Integer isbegin

return i*i;end;

begin Ada.Text_io.put("entrer un entier : "); Ada. Integer_Text_io.get(i);ada;text_io;put("le carré est : "); case carre(i) is when 0..1 => Ada.Integer_Text_io.put(i); when others => Ada.Integer_Text_io.put(carre(i)); end case;

end essai;

3. Instructions de boucleUne boucle simple se présente sous la forme : loop ... end loop;

L'instruction exit permet la sortie de boucles simples, imbriquées ou non. recherche: loop exit -- sortie de la boucle recherche ... loop exit -- sortie de la boucle interne ... exit recherche when trouve; -- sortie conditionnelle de la boucle recherche ... exit when temp > max; -- sortie conditionnelle de la boucle interne ... exit recherche; -- sortie inconditionnelle de la boucle recherche end loop; ... end loop recherche;

Véronique Gaildrat

Support de cours ADA 95

11

D'autres instructions de boucle permettent d'effectuer un traitement : - tant qu'une condition est vraie, - pour un nombre fini de fois.

Ces deux instructions sont les boucles while et for. while encoreAFaire loop -- faire le traitement ... ... -- faire évoluer la condition encoreAFaire ... end loop;

for i in Integer range 1 .. 10 loop -- le traitement sera effectué 10 fois -- pour des valeurs de i allant de 1 à 10 -- i constante dans la boucle ... end loop;

for i in reverse Integer range 1 .. 10 loop -- le traitement sera effectué 10 fois -- pour des valeurs de i allant de 10 à 1 ... end loop;

for suivant in table'range loop -- suivant est constant dans la boucle if table (suivant) = ' ' then ... end if; ... end loop;

1.2.9Sous-programmes

Les sous-programmes (procédures et fonctions) sont constitués d'une en-tête, suivie de déclarations, puis d'instructions.

procedure identificateur [(p1 : in TypeP1; p2 : out TypeP2; p3 : in out TypeP3)] is déclarationsbegin instructions -- chaque instruction se termine par un ";"[exception traite-exceptions]end identificateur;

function identificateur[(p1 : in TypeP1; p2 : [in] T ypeP2)]return TypeRetourné is déclarationsbegin instructions -- chaque instruction se termine par un ";"[exception traite-exceptions]end identificateur;

Le mode de passage de paramètres fait référence à la manière dont on y accède, et non à la méthode physique de passage: les paramètres peuvent ainsi être déclarés in (lecture seulement), out (écriture seulement), ou in out (lecture et écriture).

Véronique Gaildrat

Support de cours ADA 95

12

1.2.10Instruction return

L'instruction return termine l'exécution d'une procédure ou d'une fonction.Toute fonction doit comprendre au moins un return et peut en contenir plusieurs.Dans le cas d'une procédure, l'instruction prend la forme suivante :

return; -- et stoppe l'exécution de la procédure

Pour une fonction, l'utilisation du return permet en plus de retourner une valeur du type de la fonction.Exemple : function impaire (val : Integer) return Boolean is begin if (val mod 2 ) = 0 then return false; else return true; end if; end impaire;Si l'expression retournée n'est pas du sous-type demandé, une erreur CONSTRAINT_ERROR sera levée.Si une fonction se termine sans avoir atteint de return ==> PROGRAM_ERROR.La fonction impaire pourra être écrite de la façon suivante : function impaire (val : Integer) return Boolean is begin return ((val mod 2) /= 0); end impaire;

Il est préférable de n'avoir qu'un return par fonction. Mais il est parfois nécessaire, pour des conditions de lisibilité d'en avoir plusieurs.

1.2.11Paquetages

Un Type de Données Abstrait est mis en œuvre en Ada grâce à un paquetage (package) comprenant les deux parties suivantes :

• la spécification du package qui contient les types, données et signatures des opérations (sous-programmes) visibles depuis l'extérieur,

• le corps du package (body) qui contient le code des opérations (sous-programmes) déclarées dans la partie spécification du package et le code des opérations (sous-programmes) non visibles depuis l'extérieur.

Un paquetage, une fois compilé, pourra être référencé et utilisé par un programme appelé : client.Exemple :

package Point2D_p is-- le package exporte le type Point2D-- ATTENTION, déclaré ainsi le type Point2D n'est pas protégétype Point2D is array (1..2) of Float; -- tableau de deux flottants-- Le Point2D est créé en (0,0)procedure creerPoint (p : out Point2D);-- Le Point2D est translaté de (x,y)procedure translater (p : in out Point2D; x, y : Float);-- retourne une chaîne ce caractères représentative du Point2Dfunction toString(p : Point2D) return String;

end Point2D_p;

package body Point2D_p is-- Le Point2D est créé en (0,0)procedure creerPoint (p : out Point2D) isbegin p(p'first) := 0.0; -- utilisation d'attributs p(p'last) := 0.0;end creerPoint;-- Le Point2D est translaté de (x,y)procedure translater (p : in out Point2D; x, y : Float) is

Véronique Gaildrat

Support de cours ADA 95

13

begin p(p'first) := p(p'first) + x; p(p'last) := p(p'last) + y;end translater;-- retourne une chaîne ce caractères représentative du Point2Dfunction toString(p : Point2D) return String isbegin return Float'image(p(p'first)) & " , " & Float'image(p(p'last)); end toString;

end Point2D_p;Client :

with Ada.Text_io, Point2D_p; -- la clause with permet l'import d'autres unitésprocedure essai is

p1, p2 : Point2D_p.Point2D;begin

Point2D_p.creerPoint(p1); Point2D_p.creerPoint(p2); Point2D_p.translater(p1, 2.0, 3.0); Ada.Text_io.put_line("p1 = " & Point2D_p.toString(p1));

end essai;Résultat de l'exécution :

p1 = 2.00000E+00 , -3.00000E+00

1.2.12Variable et Type d'une variable

Chaque variable est constituée d'un quadruplet :• son nom,• son Type, -- obligatoire et inchangé tout au long de l'exécution• sa valeur,• l'espace mémoire où est stockée sa valeur.

Le typage est strict, ainsi la valeur affectée à une variable doit être strictement du type de la variable. Tout manquement déclenchera une erreur (appelée exception).

Déclaration d'une variable d'un type simple : nomVariable : nomType [:= valeurInitiale];Exemple :

année : Integer := 2001; -- année est une variable entière de valeur 2001.

Le type peut être un type prédéfini ou construit par l'utilisateur. Parmi les types simples prédéfinis on trouvera :

• Integer,• Float,• Character,• String,• Boolean.

1.2.13Caractères et chaînes littérales

1.2.13.1Caractères littérauxIl y a deux jeux de caractères en Ada 95:

• Character, basé sur la norme isO 8 bits 8859-1communément appelée Latin-1 • Wide_Character, basé sur la norme isO 10646 et dont les 256 premières positions sont

celles de Character.Ces deux types sont définis dans le paquetage Standard de Ada.Exemple de caractères : 'A' la lettre A ' ' espace

1.2.13.2Chaînes littéralesDe la même manière que deux jeux de caractères sont définis, on trouve deux types de chaînes :

Véronique Gaildrat

Support de cours ADA 95

14

String (tableau de Character) et Wide_String (tableau de Wide_Character) Exemple de chaînes de caractères

"ceci est un message" "première partie d'une chaîne" & "qui continue à la ligne suivante" "" chaîne vide

1.2.14Paquetages de manipulation des caractères et des chaînes

Ada.Characters qui est en fait vide mais est la racine de l'arborescenceAda.Characters.Handling méthodes de conversion de caractères et de chaînes

function To_Lower (Item : in Character) return Character; function To_Upper (Item : in Character) return Character;

function To_Lower (Item : in String) return String; function To_Upper (Item : in String) return String;

Ada.Characters.Latin_1 consiste en la déclaration de constantes donnant les noms de la plupart des caractères.

Exemple : Ada.Characters.Latin_1.Exclamation

De nombreux paquetages fournissent des fonctionnalités pour la manipulation de chaînes. Les deux principaux sont :

Ada.Strings.Fixed pour des String de taille fixe.Ada.Strings.Unbounded pour des String non bornées (en fait seulement par

integer'last) exporte le type Unbounded_String

1.2.15Exemple d'un programme simple en ADA

with Ada.Text_io, Ada.Float_Text_io, Ada.numerics; -- import de paquetages préexistants dans Adaprocedure essai is x, y : Float; i, j, k : Integer;begin

Ada.Text_io.put (Item => "Hello ");Ada.Text_io.put (Item => "We hope you enjoy studying Ada!");Ada.Text_io.new_Line;

-- affectation de valeurs flottantes x := 3.14; x := Ada.numerics.pi; Ada.Float_Text_io.put(x); y := 2.0 * x; -- affectation de valeurs entières i := 3; j := 4; k := (i+j) * 3;end essai;

2. Les types

2.1Notion de type

2.1.1Définition• A un type T correspond :

- un nom, - un ensemble fini de valeurs, - un ensemble d'opérations primitives agissant sur les valeurs.

• Les opérations peuvent être : - prédéfinies (affectation, égalité ...)

Véronique Gaildrat

Support de cours ADA 95

15

- attributs,- sous-programmes (ayant un paramètre ou un type de retour égal au type T) à condition que T soit déclaré dans un paquetage et que les sous-programmes soient déclarés dans la spécification du même paquetage.

• A chaque instruction, le compilateur vérifie si l'opération, effectuée sur une variable d'un type donné, est permise. Dans le cas contraire, il n'y aura pas compilation du programme.

• Les types prédéfinis (tels que Integer, Float, Character, String) sont déclarés dans le paquetage Standard dont l'importation est automatique (pas de clause with).

2.1.2Règles• Chaque entité doit être déclarée : une entité est, soit une variable, soit une constante.• La déclaration de chaque entité spécifie son type.• Toute opération sur une entité doit en préserver le type => le type d'une entité est invariant durant

l'exécution. C'est le type spécifié à la déclaration.

2.1.3Classification de types ADA

Les types sont classés hiérarchiquement selon leurs propriétés :

les types access => pointeurs scalaires => un élément <--> une valeur composite => un élément <--> une ou plusieurs valeurs

Tous Types | +---------------+-------------+ | | Types Elémentaires Types Composites | | +-------+--+ +------+-------+---------+ | | | | | | access scalaires array record protected task | +------+----------------+ | | | +====================================+ discrets | real | | | | | +---------------+ +---+----------+ | | | | | | | Numeric enumeration | integer float fixed | Types | | | | | +--+----+ +----+--+ | | | | | | | |signed modular decimal ordinary | +====================================+

Hiérarchie des types Ada 2.1.4Les modèles de types

Il y a plusieurs manières de construire un type : - à partir d'une spécification qui définit un modèle de type, - à partir d'un type déjà existant au moyen d'une opération dite de dérivation. - On peut également construire un sous-type d'un type déjà existant.

2.1.5Déclaration de types

Structure :

Véronique Gaildrat

Support de cours ADA 95

16

type NomType is {spécification d'un modèle};

Exemple : type Couleur is (rouge, vert, bleu);

Exemple de type énumératif de nom Couleur ayant comme ensemble de valeurs les littéraux : rouge, vert et bleu.

type MyInt is range -100 .. 100;range -100 .. 100 définit l'ensemble des entiers pour le type MyInt. Les opérations applicables sont identiques à celles du type Integer.

Le mot clé range définit par défaut les entiers. digit définit les réels en virgule flottante. delta définit les réels en virgule fixe. array définit les tableaux. ...2.1.6Contraintes

Une contrainte restreint l'ensemble des valeurs d'un type, mais ne change pas l'ensemble des opérations applicables.

Contraintes d'intervalles : type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche);

début : Jour range lundi .. jeudi; -- range définit également les contraintes d'intervalle fin : Jour range jeudi .. vendredi; code : Character range 'a' .. 'f';

==> range borneInférieure .. borneSupérieure

Début est de type jour mais ne peut prendre toutes les valeurs définies dans l'ensemble des valeurs de jour. Début est considéré comme étant d'un sous-type non explicité du type jour.

2.2Sous-types et types dérivés

ATTENTION : Il y a compatibilité entre un sous-type et le type origine : on peut affecter une variable du sous-type avec une variable du type origine et réciproquement (à condition de respecter l'ensemble de valeurs). Par contre, il y a incompatibilité entre un type dérivé et le type origine !!!Aussi, créer des types uniquement quand c'est sémantiquement justifié.

2.2.1Sous-types

Un sous-type caractérise un ensemble de valeurs provenant d'un type donné. Le sous-ensemble est défini à partir d'une contrainte qui prennent des formes différentes selon la nature du type.Le sous-type possède toutes les opérations primitives du type dont il est issu.En Ada 95 un type est considéré comme un sous-type sans contrainte. Il n'y a donc que des sous-types !

Exemple :subtype JourDeMois is Integer range 1..31;

Une variable déclarée :jdm : JourDeMois;

pourra prendre des valeurs entre 1 et 31; La déclaration et l'affectation suivante est autorisée à condition que la valeur contenue dans i soit bien dans la contrainte de JourDeMois. i : Integer; … jdm := i; -- peut lever une exception de type CONSTRAINT_ERROR

Autre exemple : type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche);Il n'est pas toujours nécessaire d'introduire un sous-type pour imposer une contrainte : j : Jour range lundi .. vendredi; -- est possible

Véronique Gaildrat

Support de cours ADA 95

17

k :Jour range lundi .. vendredi; -- mais si cette contrainte doit être l :Jour range lundi .. vendredi; -- utilisée plusieurs fois :Il vaut mieux faire : subtype JourOuvrable is Jour range lundi .. vendredi; j, k, l : JourOuvrable;

Un sous-type n'est pas un nouveau type : maintenant : Jour; terme : JourOuvrable; -- maintenant et terme sont compatibles maintenant := terme; -- est une assignation légale.

Exemples construits à partir de caractères :subtype Minuscules is Character range 'a'..'z'; -- sous type

2.2.2Types dérivés

But : A partir d'un type explicitement défini, l'utilisateur peut définir un nouveau type par dérivation.

Exemple : type B is new A; | | | type parent type dérivéLe type dérivé B a : . les mêmes opérations primitives, . le même ensemble de valeurs, . les mêmes notations, . les mêmes attributs (au sens Ada)que A. Mais A et B sont de type différent.

Exemple :procedure essai is TAUX_ECHANGE : constant Integer := 7; TAUX_ECHANGE_WE : constant Integer := 8; -- Ici, les types dérivés se montrent utiles. -- En effet, on ne peut mettre des francs dans des dollars sans une opération de change ! type Franc is new Integer range 0 .. Integer'last; -- types dérivés type Dollar is new Integer range 0 .. Integer'last; type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); subtype JourOuvrable is Jour range lundi .. vendredi; subtype WeekEnd is Jour range samedi .. dimanche; -- sous-types f1, f2, f3 : Franc := 0; d1, d2, d3 : Dollar := 0; jr : Jour; congé : WeekEnd; i : Integer; begin f2 := 100; f1 := f2 + f3; jr := samedi; if jr in lundi .. vendredi then d1 := Dollar (f1) * Dollar(TAUX_ECHANGE); else congé := WeekEnd (jr); d1 := Dollar (f1) * Dollar(TAUX_ECHANGE_WE); end if; end essai;

Véronique Gaildrat

Support de cours ADA 95

18

Attention : Les types dérivés ne sont pas compatibles avec le type origine : l'instruction i := f2; est illégale. Les types dérivés montreront toute leur utilité quand on voudra introduire des notions d'encapsulation et d'abstraction de données avec les types privés et les packages.

Exemple : type B is (rouge, vert, bleu, cyan, magenta, jaune); type B is new A; x : A; y : B; function f (x : A) return A is ... end f; function f (x : B) return B is ... end f; begin x := A'first; -- x := rouge; -- x := f(x); -- aucun problème car résolution des homonymes x := f(rouge); -- x donne le type de la fonction y := f(rouge); -- -- par contre : x := y; -- illégal car x et y de type différent x := A(y); y := B(x); -- => conversion explicite x := A(f(y)); end;

Des ambiguïtés peuvent se produire et obligent à qualifier : if f(rouge) = rouge then -- type A ou B ?=> if f(rouge) = B'(rouge) ou if f(B'(rouge)) = rougeou if B'(f(rouge)) = rouge

ATTENTION : A(f(...)) convertit le résultat dans le type A A'(f(...)) le résultat est déjà de type A

Autre exemple : On dispose d'un type numérique quelconque : type Scalaire is ...; D'où sont dérivés d'autres types : type Longueur is new Scalaire; type Surface is new Scalaire; x, y : Longueur; s : Surface; On peut écrire : x := 1.5; y := x + 10.0; Mais : s := x; est illégal (on ne peut affecter des longueurs à des surfaces) s := s + x; idem

On écrit les fonctions suivantes : function sommeLongueurs (x, y : Longueur) return Longueur; function sommeSurfaces (x, y : Surface) return Surface;

On peut définir : function produitLongueurs (x, y : Longueur) return Longueur; qui est peu utile bien que syntaxiquement correct.

Il vaut mieux définir : function produitLongueurs (x, y : Longueur) return Surface is

Véronique Gaildrat

Support de cours ADA 95

19

begin return (Surface (Scalaire(x) * Scalaire (y))); end "*"; Ainsi on peut écrire : s := produitLongueurs(x, y);

On peut également obtenir des sous-types dérivés : type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); type JourOuvrable is new Jour range lundi .. vendredi;Le nouveau type hérite des propriétés de jour mais toutes les valeurs sont contraintes dans l'intervalle

lundi .. vendredi.

Exemple : j : Jour; k : JourOuvrable;alors j := mercredi; k := mercredi; -- correct if j < samedi then ... if k < samedi then...mais k := j; -- illégal k := dimanche; -- erreur d'intervalle k := JourOuvrable (j); -- peut lever CONSTRAINT_ERROR mais est autorisé

2.3Types scalaires

2.3.1Attributs communs à tous les types scalaires

Les attributs suivants sont définis pour tous les types scalaires :• S'first dénote la borne inférieure de l'ensemble de valeurs du type S. La valeur retournée est du

type de S (c'est toujours le cas sauf si un autre type de retour est précisé).• S'last dénote la borne supérieure de l'ensemble de valeurs du type S.• S'range est équivalent à S'first .. S'last. • S'base dénote le type non contraint (de base) parent du type S. Utile pour des calculs qui peuvent

momentanément dépasser la contrainte du type S.• S'min (a, b : S) return S, dénote une fonction qui retourne le minimum de a et b.• S'max (a, b : S) return S, dénote une fonction qui retourne le maximum de a et de b.• S'succ (a : S) return S renvoie la valeur qui succède à a dans l'ensemble des valeurs.

CONSTRAINT_ERROR peut arriver si on essaye de faire S'succ(S'last). Pour un type entier, S'succ(a) retourne a+1. Pour une type à virgule fixe retourne a+ delta. Pour un type à virgule flottante, retourne le nombre machine représentable suivant immédiatement a.

• S'pred (a : S) return S retourne le prédécesseur immédiat à a en suivant les mêmes règles que S'succ.

• S'image (a : S) return String, retourne la représentation sous forme de chaîne de caractère de a. Si a est un nombre, la chaîne commencera par le signe.

• S'width retourne la longueur maximale que peut prendre la chaîne de caractères retournée par S'image.

• S'wide_image (a : S) return Wide_string, idem que S'image mais retournant un Wide_string• S'wide_width retourne la longueur maximale que peut prendre la chaîne de caractères retournée

par S'wide_image.• S'value (im : String) return S renvoie une valeur dans le type S dont l'image est im.

S'value(S'image(a)) = a.• S'wide_value (s : Wide_String) return S, idem que S'value mais avec en entrée un Wide_string.

S'wide_value(S'wide_image(a)) = a

2.3.2Les types discrets

Définition :

Véronique Gaildrat

Support de cours ADA 95

20

Un type discret est un type scalaire. Donc, à chaque élément correspond une seule valeur.Un type discret est caractérisé par le fait que chaque valeur de l'ensemble a une position. On pourra donc repérer la position d'une valeur, la valeur d'une position, la valeur précédente, suivante...

Exemple :type MyInt is range -100 .. 100;

L'ensemble des valeurs est :-100, -99, -98, -97, ..........., 97, 98, 99, 100

Les positions respectives des valeurs sont :0 1 2 3 197 198 199 200

La position de la valeur 0 est 100, et la valeur en position 4 est -96.

2.3.2.1Attributs spécifiques aux types discrets• S'pos (a : S) return Integer, retourne la position de la valeur dans l'ensemble de valeurs de S.• S'val (I : Integer) return S, retourne la valeur de l'ensemble de valeurs de S qui se trouve à la position

donnée par i.

Les types discrets comprennent deux sous-ensembles : les types énumérés et les types entiers, partagés entre • entiers signés Signed qui sont les Integer standards et • entiers non signés appelés Modular.

2.3.2.2Les types entiers

Spécification : range début .. findébut et fin peuvent être des expressions statiques évaluées à l'exécution.

L'ensemble des valeurs autorisées est :type Integer is range Integer'first .. Integer'last;

'first et 'last sont des attibuts associés au type. Leur valeur dépend de l'implémentation. Sur une machine 16 bits : Integer'first = -32768 Integer'last = +32767Ces valeurs seront différentes avec une autre implémentation : utiliser les attributs et les contraintes pour assurer la portabilité !

Ensemble des opérations (sans opérations "mixtes") : l'affectation := op relationnelles = /= < <= > >= op binaires + - * / mod (signe de la 2eme op) rem (signe de la 1ere op) ** op unaires + - abs

Il ne faut pas confondre rem et mod : rem : remainder est le reste de la division entière a rem b a le signe de a et satisfait : abs (a rem b) < abs (b) mod : modulo a mod b a le signe de b et satisfait : abs (a mod b) < abs (b)

Pour effectuer une addition entre Float et Integer il faudra convertir explicitement l'une des deux opérandes.La conversion de Float vers Integer arrondit :

• 1.4 1• 1.6 2

Dans le paquetage standard, on trouve des sous-types prédéfinis tels :subtype Natural is Integer range 0 .. Integer'last;subtype Positive is Integer range 1 .. Integer'last;Natural'first = 0;Positive'first = 1;

Exemple d'utilisation :with Ada.Text_io, Ada.Integer_Text_io;procedure essai is type Int1 is range -1000000 .. 1000000;

Véronique Gaildrat

Support de cours ADA 95

21

i1, j1, k1 : Int1; i, j, k : Integer := 3;begin i1 := 2_000; j1 := 10; k1 := i1 + j1; if k1 in 10 .. Int1'last then k1 := 0; end if; if k1 not in 0 .. Int1'last then k1 := -1; end if; k := Integer(k1); k1 := int1(k); k1 := i1*2 - j1**3; k1 := k1 mod 2; if i1 = k1 then -- /= < <= >= > k1 := j1; end if; Ada.Text_io.put("entrer une valeur : "); Ada.Integer_Text_io.get (j); Ada.Text_io.new_line; Ada.Text_io.put("entrer une valeur : "); Ada.Integer_Text_io.get (Integer(j1)); Ada.Text_io.new_line; Ada.Text_io.put_line("Integer'first : "); i := Integer'first; Ada.Integer_Text_io.put (i); Ada.Text_io.new_line; Ada.Text_io.put_line("Integer'last : "); j := Integer'last; Ada.Integer_Text_io.put (j); Ada.Text_io.new_line; Ada.Integer_Text_io.put (Integer(i1));end essai;

2.3.2.3Les types Modular (dits types modulo)Correspondent à des types entiers non signés avec une arithmétique cyclique et donc jamais de dépassement de capacité.Ils sont utilisés pour gérer des données naturellement cycliques : heure, longitude.

type OctetNonSigné is mod 256; -- ensemble de valeurs 0..255 x : OctetNonSigné := 128;

… x := x + x; -- x vaut 0

Ada.Integer_Text_io.put(OctetNonSigne'modulus);Ensemble des opérations :

• opérations arithmétiques, identiques aux entiers signés mais modulo le type'modulus• opérations logiques binaires (and, or, xor) agissent comme sur une suite de bits.• opération logique unaire (not) soustrait la valeur maximale et se comporte vraiment comme

le not bit à bit pour les puissances de deux• opérations provenant du paquetage Interfaces appliquées à des types tels que Unsigned_16 :

o function Shift_Left (Value : Unsigned_16; Amount : Natural) return Unsigned_16;o function Shift_Right (Value : Unsigned_16; Amount : Natural) return Unsigned_16;o function Shift_Right_Arithmetic (Value : Unsigned_16; Amount : Natural) return

Unsigned_16;o function Rotate_Left (Value : Unsigned_16; Amount : Natural) return

Unsigned_16;o function Rotate_Right (Value : Unsigned_16; Amount : Natural) return

Unsigned_16;

Véronique Gaildrat

Support de cours ADA 95

22

2.3.2.4Les types énumérés

2.3.2.4.1Caractéristique des types énumérés

Les types énumérés sont des ensembles de valeurs ordonnées. Chaque valeur est appelée un littéral énumératif et doit être donnée par l'utilisateur.Déclaration de type énuméré : type NomType is <ensemble de littéraux>;Exemples : type Direction is (nord, est, sud, ouest); type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche);L'ensemble des valeurs est : lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche

L'ensemble des opérations applicables à ces valeurs est : affectation := test d'(in)égalité = /= test d'ordre > >= < <= lundi < jeudi if demain >= mardi then ...Déclaration d'entités : but : Direction; -- variable demain : Jour := lundi; -- variable initialisée milieu : constant Jour := jeudi; -- constante hier, avantHier : Jour := unAutreJour; -- déclaration multiple -- équivaut à hier : Jour := unAutreJour; avantHier : Jour := unAutreJour;Affectations correctes : but := ouest; demain := mardi; demain := milieu;Affectations incorrectes : but := dimanche; but := 8; but := demain; but := but + sud; milieu := mer;

2.3.2.4.2Types énumératifs prédéfinis

type Boolean is (false, true);Ensemble de valeurs : false true

Ensemble d'opérations : := = /= > >= < <= not and or xorexemples :

function "and" (left, right : Boolean) return Boolean;

type Character is <ensemble de tous les caractères Latin_1>;Ensemble de valeurs : Tous les caractères ASCIIEnsemble d'opérations : Identiques aux types énumératifs

type Wide_Character is <ensemble de caractères étendus>;

2.3.2.4.3Exemple d'utilisation de type énumératifs

with Ada.Text_io, Ada.Integer_Text_io;

Véronique Gaildrat

Support de cours ADA 95

23

procedure essai is type Couleur is (rouge, vert, bleu, cyan, magenta, jaune); type Feu is (rouge, orange, jaune);

c1, c2 : Couleur; c3 : Couleur := bleu; fr1, fr2, fr3 : Feu;

begin if c1 in cyan .. jaune then c1 := rouge; end if; fr1 := rouge; for coul in Couleur'first .. Couleur'last loop Ada.Integer_Text_io.put (couleur'pos(coul)); end loop;end essai;

2.3.2.4.4Exemple d'utilisation de booléens

Boolean_io n'est pas standard mais est à définir par instanciation de Ada.Text_io.Enumeration_io.with Ada.Text_io, Boolean_io, Ada.Integer_Text_io;procedure essai is b1, b2, b3 : Boolean;begin b1 := true; b2 := false; if b1 = b2 then -- /= < > <= >= boolean_io.put(b1); else boolean_io.put(b2); -- affiche FALSE end if; if b1 and b2 then b1 := true; else b2 := false; end if; if b1 and then 1>2 then b1 := false; end if; if b1 or else b2 then b2 := false; end if;end essai;

2.3.3Les types réels

Spécification : 1_ Virgule fixe : delta d [range début .. fin] d : intervalle minimum entre deux valeurs successives de l'ensemble des valeurs. Exemple : type Del is delta 0.05 range 0.0 .. 10.0; L'ensemble des valeurs est : 0.0, 0.05, 0.10, 0.15, ...

2_ Virgule flottante : digits n [range début .. fin] n : nombre de chiffres décimaux significatifs.

Exemple : type Reel is digits 7;Le type prédéfini Float est :

type Float is digits Float'digits range Float'first .. Float'last;L'ensemble des valeurs autorisées est :

de Float'first à Float'last

Véronique Gaildrat

Support de cours ADA 95

24

Tous les réels ne sont pas représentables en machine. Un ensemble de nombre modèles est défini à l'élaboration d'un type réel. Ces nombre sont des multiples entiers de puissance de deux qui peuvent être représentés excatement. Les valeurs stockées pour les réels sont définies à partir de ces nombres modèles.Le nombre de digits assure la précision minimale dont on peut être sûr.Les littéraux réels sont convertis en nombres modèles avec une précision qui dépend de l'implémentation.Les types à virgule fixe permettent une déclaration avec une erreur absolue donnée par la valeur en delta, ce qui spécifie exactement les nombres modèles.Ada permet de contrôler l'erreur admissible dans un calcul numérique mais ceci ne sera pas abordé dans ce cours. Pour plus de renseignements, se référer au manuel de référence. Notamment, en ce qui concerne les attributs relatifs aux types réels.

L'ensemble des opérations comprend : l'affectation := op relationnelles = /= < <= > >= op binaires + - * / mod rem ** op unaires + - abs

Exemple d'utilisation de types réels en virgule fixe et virgule flottante :with Ada.Text_io, Ada.Float_Text_io, Ada.Integer_Text_io;procedure essai is subtype Reel is Float digits 5 range -10000.0 .. 10000.0; type VirgFixe is delta 0.1 range -10.0 .. 10.0; r1, r2, r3 : Reel; v1, v2 : VirgFixe; x: Float := 123.45E-1;begin r1 := 0.1E2; r2 := -1.2E2; r3 := (r1*r2)/3.0 + 2.2; if r3 in 1.0 .. 10000.2 then Ada.Text_io.put_line (" in "); end if; r3 := r1; x := Float (r1); if r1 = r2 then r3 := r1; end if; r1 := r1**2; x := Float (r1); v1 := 1.0; v2 := 2.2; v1 := v1 + v2; v1 := VirgFixe(v1*v2); Ada.Float_Text_io.put (Float'first); Ada.Float_Text_io.put (Float'last); Ada.Integer_Text_io.put (Float'digits); Ada.Float_Text_io.put (Float(r1)); Ada.Float_Text_io.put (x);end essai;

Attention : La conversion de Float vers Integer arrondit au lieu de tronquer !1.4 devient 11.6 devient 21.5 devient 2-1.5 devient -2 -- la valeur à mi-chemin est arrondie en s'éloignant de 0

2.3.4Les types décimaux

Véronique Gaildrat

Support de cours ADA 95

25

Un type décimal est une forme de réel à virgule fixe. La déclaration fournit une valeur pour delta mais qui doit obligatoirement être une puissance de 10 et donne le nombre de chiffres décimaux significatifs.

Exemple : type Euro is delta 0.01 digits 14; Ce qui permet 12 chiffres pour les Euros et 2 pour les centimes d'Euros. Soit au maximum : 999 999 999 999.990 Euros

Les opération des types à virgule fixe s'appliquent aux types décimaux (sauf quelques règles particulières d'arrondi (destinées aux comptables ! Voir manuel de référence)

2.4Types composés principaux (tableaux et articles)

Une variable de type composé contient un certain nombre de composants.Ces composants sont de même type pour les tableaux (arrays) et peuvent être de type différent pour les articles (records)

2.4.1Types tableaux

Une entité de type tableau est un ensemble indicé d'éléments de même type.La spécification d'un type tableau permet d'indiquer : - le nombre d'indices, - le type des indices qui doit être Discret

- le type des éléments.-

2.4.1.1Variables tableaux

Déclaration basique : a : array (Integer range 1..10) of Integera est une variable ayant 10 composants de type Integer. L'accès à un composant se fait par : a(3) := 1000; ou encore

j := a(i); avec 1<= i <= 10 et j : IntegerPour un tableau à 2 dimensions on donnera un intervalle discret pour les indices de chaque dimension : a : array (Jour range lundi .. vendredi, Integer range -10..5) of Float;accès aux composants par : a(lundi,1) := 0.0 r := a(j, -10); avec j : Jour et lundi <= j <= vendrediLe format général de la spécification est le suivant :

array (TypeDiscret1 range début .. fin [, TypeDiscret2 range début .. fin]) of TypeComposant;début et fin pouvant être des littéraux, des constantes ou des variables qui seront évaluées au moment de la déclaration.

Sauf cas particulier (usage unique et pas de passage de paramètres) il est pratiquement toujours préférable de déclarer des types tableaux et des variables de ces types là.

2.4.1.2Types tableaux non contraints

Les tableaux non contraints permettent une certaine généralisation des types tableaux en permettant de fixer les contraintes d'indices uniquement à la déclaration des variables de type tableau.Exemple de type de tableau prédéfini non contraint : type String is array (Positive range <>) of Character; -- <> se dit "boite"

Exemples de déclarations de type tableau non contraint: type Vecteur is array (Integer range <>) of Float;

Ensemble de valeurs du type Vecteur: Chaque Vecteur : - a des composants de type Float, - est indexé par des valeurs de type Integer. Mais tous les Vecteurs n'auront pas les mêmes bornes.

On peut introduire un sous-type intermédiaire :

Véronique Gaildrat

Support de cours ADA 95

26

subtype Vecteur5 is Vecteur(1..5); v : Vecteur5;

Tableau bi-dimensionnel : type Matrice is array ( Integer range <>, Integer range <>) of Float; subtype Matrice3_3 is Matrice(1..3, 1..3); m3 : Matrice3_3;

2.4.1.3Contraintes d'indices

Les types d'indices sont toujours de type Discret.Une contrainte d'indice sert à spécifier les bornes des indices d'une entité tableau non contraint.

Exemple : v : Vecteur(2 .. 15); w : Vecteur (1 .. 1000);v et w sont de même type et peuvent s'échanger des valeurs, à condition de respecter les contraintes d'indices. m : Matrice(1 .. 1000, 15 .. 100); mn : Matrice (1..n, 1..n); -- n sera évalué dynamiquement à la déclaration de mn

2.4.1.4Agrégats

Un agrégat est la forme littérale d'une valeur de type tableau (un élément de l'ensemble de valeurs du type tableau).Il existe deux formes d'agrégats qui ne doivent pas être mélangées :

• l'agrégat positionnel où les valeurs sont donnés dans l'ordre où elles seront stockées dans la variable de type tableau. Les valeurs sont séparées par des virgules.v : Vecteur5 := (1.0, 2.0, 3.0, 4.0, 5.0);

• l'agrégat par nom où chaque valeur est précédée de la valeur de l'indice et du symbole =>, ainsi l'ordre des indices n'a plus à être respecté.v : Vecteur5 := (1 => 1.0, 2 =>2.0, 4 => 4.0, 5 => 5.0, 3 => 3.0);

Les règles de formation des agrégats ressemblent à celles utilisées pour l'instruction case. Chaque valeur peut être donnée pour un seul indice ou un intervalle ou plusieurs indices donnés explicitement.La clause others permet de donner une valeur à tous les éléments non spécifiés mais doit toujours être placée à la fin de l'agrégat :

w : Vecteur(1..1000) := (1 => 1.0, 2 | 4 =>2.0, 3 => 3.0, 7..15 => 5.0, others => 0.5); m3 : Matrice3_3 := ( 1=> (1=>1.0, others=>0.0),

2=> (2=>1.0, others=>0.0), 3=> (3=>1.0, others=>0.0));

v : Vecteur(1 .. 15) := (1.0, 2.0, 3.0, 4.0, 5.0, others => 0.0);

La déclaration multiple suivie d'une affectation provoque l'initialisation des deux tableaux : v51, v52 : Vecteur5 := (others => 1.0);initialise toutes les valeurs de v51 et de v52 à 1.

2.4.1.5Attributs de tableaux

Les bornes des indices sont dénotées par des attributs de tableaux : v : Vecteur (2 .. 15); for n in v'first.. v'last loop

... end loop; v'first -- 2 v'last -- 15 v'range -- 2 .. 15 v'length -- v'last – v'first + 1 = 15 – 2 + 1 = 14

Véronique Gaildrat

Support de cours ADA 95

27

De même, pour un tableau multi-dimensionnel : m : Matrice (1 .. 10, 1 .. 100);

m'last(1) -- 1000 m'last(2) -- 100 m'range(1) -- 1..1000 m'length(1) -- 1000

Les attributs first, last, length et range peuvent s'appliquer aux variables de type tableau ainsi qu'aux type et aux sous-types tableaux eux mêmes à condition qu'ils soient contraints. Vecteur'first -- illégal Vecteur5'first -- 1

Les bornes des indices peuvent provenir de la valeur initiale : message : String := "combien de caractères ? "; -- message'first = 1, message'last = 24 v1 : Vecteur := (1.0, 2.5, 3.0); Attention aux bornes de v1 : v1'first = Integer'first et v1'last = Integer'first + 3 – 1 !Attention : others ne doit pas être utilisé dans un agrégat qui est utilisé pour initialiser une variable de type tableau non contraint !

On peut déclarer un tableau comme étant constant, auquel cas il doit être initialisé à la déclaration : messageContant : constant String := "message constant"; type Semaine is array (Jour) of Boolean; jourOuvrés : constant Semaine := (true, true, true, true, true, false, false); ou alors jourOuvrés : constant Semaine := (lundi .. vendredi =>true, others => false);

2.4.1.6Tableaux non contraints et Passage de paramètres

Un paramètre formel de type tableau non contraint est contraint par les bornes du paramètre effectif correspondant : procedure essai (s : String) is -- String non contraint begin for x in s'range loop -- s a les contraintes du paramètre effectif ... end loop; end essai;

chaîne : String (1 .. 20); begin ... Ada.Text_io.get(chaîne); -- il faudra saisir exactement 20 caractères ! essai (chaîne); -- s'range 1..20 Ada.Text_io.get_line(chaîne, l); -- la longueur réellement saisie donnée par l essai (chaîne(1..l)); -- s'range 1..l ... essai("Dupont"); -- s'range 1..6 end;

2.4.1.7Types tableaux contraints

Une contrainte d'indice peut être donnée dans la définition de type tableau : type VecteurFixe is array (1..4) of Float; v1, v2 : VecteurFixe; -- ont tous les mêmes indices et des valeurs de type Float

type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); type Durée is Integer range 0..8; type Horaire is array (Jour range lundi .. dimanche) of Durée;Equivaut à : type HoraireStandard is array (Jour range <>) of Durée;suivi de : subtype Horaire is HoraireStandard (lundi .. dimanche);

Véronique Gaildrat

Support de cours ADA 95

28

on peut écrire aussi : type Horaire is array (lundi .. dimanche) of Durée;ou encore : type Horaire is array (Jour) of Durée;

En fait, sauf dans le cas où les indices sont bien établis et toujours identiques, il est préférable de déclarer des types tableaux non contraints.L'utilisation des attributs dans les sous-programmes ayant des types tableaux en paramètres formels rend l'usage des types tableaux non contraints très performant.

2.4.1.8Sous-types de tableaux

Un sous-type d'un type tableau (contraint ou non contraint) doit spécifier explicitement toutes les bornes.subtype Rangée is Vecteur (0 .. 100);subtype Ligne is String (1 .. 80);

Il est possible aussi de ne pas modifier les bornes, ce qui permet de donner un autre nom tout en restant compatible avec le type d'origine. subtype semaine is jour.

2.4.1.9Opérations sur un type tableau

• Affectation := : pour pouvoir affecter un tableau à un autre il est nécessaire qu'ils soient de même type, et de même taille pour chaque dimension, sans avoir forcément les mêmes indices. Quand les premiers indices ne sont pas identiques, on parle de glissement d'un tableau dans l'autre :v : Vecteur (1..5);w : Vecteur (0..4);…v := w;Un non respect du nombre d'éléments à affecter provoquera un CONSTRAINT_ERROR.

• Egalité, inégalité = /= : il est possible de comparer des tableaux de même type et de même longueur pour chaque dimension, sans avoir forcément les mêmes indices. Les tests d'égalité suivent les mêmes règles de glissement que l'affectation sauf que si les tableaux n'ont pas la même longueur, la valeur retournée sera false. Les deux tableaux seront égaux s'ils possèdent le même nombre d'éléments et que ceux-ci soient égaux (en respectant l'ordre des éléments).if v = w then … end if;

• Opérateurs de comparaison <, <=, >, >= : s'appliquent uniquement à des tableaux à une dimension et contenant des éléments de type Discret. La comparaison suit la règle suivante, la position relative de chaque couple d'éléments dans le type permet de les ordonner.type VecteurInt is array (Integer range 1..5) of Integer ; -- pas de type anonyme!!vi1, vi2 : VecteurInt := (others => 0);vi1 (1) := 1;if vi1 > vi2 then … end if; -- sinon la comparaison ne compile pass1 : String := "chat";s2 : String := "chien";if s1 < s2 then … end if;

• Tranches : une tranche de tableau à une dimension s'écrit en donnant le nom de la variable (ou constante) suivi de l'intervalle entre parenthèses.s : String(1..10) := "1234567890";t constant String := s(3..8); -- t'first = 3, t'last = 8, t = "345678"s(1..4) := "bara";s(4..7) := := s(1..4); -- s(1..7) = "barbara" car s(1..4) est évalué avant l'affectation

• Concaténation & : la concaténation s'effectue sur deux tableaux de même type. Le tableau résultat doit avoir autant d'éléments que la somme des élément des deux tableaux à concaténer.

w : Vecteur (-10..10) := (others => 0.0); x : Vecteur (1..10) := (1.5, 2.5, 3.5, others => 4.0); v1 : Vecteur(1..10) = w (1..5) & x (1..5); -- utilise les tranches de tableau

Véronique Gaildrat

Support de cours ADA 95

29

v2 : Vecteur := w & x; -- v aura la borne inférieure de w et 31 éléments

• Les opérations and, or, xor et not s'appliquent aux tableaux à une dimension de booléens :with Ada.Text_io, Boolean_io;procedure essai is type Primaire is (r, v, b); type Couleur is array (Primaire) of Boolean; -- les 8 valeurs possibles peuvent être représentées -- pour plus de facilité par les constantes suivantes : blanc : constant couleur := (true, true, true); rouge : constant couleur := (true, false, false); vert : constant couleur := (false, true, false); bleu : constant couleur := (false, false, true); cyan : constant couleur := (false, true, true); magenta : constant couleur := (true, false, true); jaune : constant couleur := (true, true, false); noir : constant couleur := (false, false, false); c1 : Couleur; -- c peut prendre 2x2x2 valeurs différentes. -- c est un tableau de trois composantes dont les indices sont : r, v, b. -- ces trois composantes sont c(r), c(v), c(b)

-- prenant chacune la valeur true ou false.begin -- ce qui permet d'écrire l'expression suivante : c1 := rouge or vert; -- qui équivaut au jaune, Boolean_io.put(c1(r)); Boolean_io.put(c1(v)); Boolean_io.put(c1(b));

Ada.Text_io.new_line;c1 := not noir; -- qui est blanc.Boolean_io.put(c1(r)); Boolean_io.put(c1(v)); Boolean_io.put(c1(b));Ada.Text_io.new_line;

end essai;

2.4.1.10Caractères et Chaînes de Caractères

Les types Character et Wide_Character sont des types énumérés prédéfinis. A partir de Character on peut créer d'autres types énumérés :

type ChiffreRomain is ('I', 'V', 'X', 'L', 'C', 'D', 'M'); -- tel que :chiffre : ChiffreRomain := 'D';…ChiffreRomain'first = 'I'ChiffreRomain'succ('C') = chiffreChiffreRomain'pos('M') = 6chiffre > 'L' = true

Noter que le test 'X' < 'L' est illégal car ambigu : on ne sait pas si on compare des Character, des Wide_Character ou même des ChiffreRomain. Pour lever l'ambiguïté il faut qualifier au moins un des deux littéraux : ChiffreRomain'('X') < 'L' = true Character'('X') < 'L' = false !Attention :

• ChiffreRomain('X') ou ChiffreRomain(c) est une conversion de type.• ChiffreRomain'('X') permet de qualifier une donnée littérale dans un type donné.

Pour utiliser les caractères non imprimables du type Character il faut donner leur chemin d'accès : with Ada.Character.Latin_1; Ada.Character.Latin_1.nul -- pour la caractère nul par exemple

Les types String et Wide_String sont eux aussi prédéfinis comme des tableaux non contraints, respectivement de Character et de Wide_Character.Ils obéissent donc à toutes les règles concernant les tableaux non contraints, avec quelques notations supplémentaires :

Véronique Gaildrat

Support de cours ADA 95

30

oie : constant String := ('o', 'i', 'e'); -- peut aussi s'écrireoie : constant String := "oie";

Pour placer le caractère '"' dans un chaîne : ('A', '"', 'B') = "A""B" -- avec doublement du guillemetUne chaîne vide sera : ""

On peut définir le type NombreRomain comme étant un tableau de ChiffreRomain contenant au moins un ChiffreRomain (ils ne connaissaient pas le 0 !) : type NombreRomain is array (Positive range <>) of ChiffreRomain; -- puis DeuxMilleUn : constant NombreRomain := "MMI"; -- constant pour ne pas être changé! function addition (nb1, nb2 : NombreRomain) return NombreRomain is nb : NombreRomain(1..Integer'max(nb1'length, nb2'length)*2); -- ? fin : Positive; begin -- convertir nb1 et nb2 en Positive, les additionner et -- reconvertir en NombreRomain dans la variable nb return nb(1..fin); end addition, nbr : NombreRomain := addition("X", DeuxMilleUn); -- avec la fonction addition nbr : nombreRomain(1..5) := "CCL" & "IV"; -- opération effectuée sur des NombreRomain s : String (1..5) := "CCL" & "IV"; -- opération sur des String, pas d'ambiguïté mais b : Boolean := "CCL" < "IV"; -- illégal car ambigu (String ou NombreRomain ?)

Contrairement au C, pour faire un tableau de chaînes de caractères, il ne faut surtout pas faire un tableau en 2 dimensions de caractères, car sinon, toutes les sous-chaînes doivent absolument la même longueur et de plus, on perd la possibilité de manipuler des String.En faisant un tableau de String, on garde l'obligation de chaînes de taille identique mais on manipule vraiment des String :

type TableauDeChaines is array (Integer range <>) of String(1..5);ferme : TableauDeChaines := ("poule", "chien", "vache");…Ada.Text_io.put(ferme(2));

Une solution pour créer un tableau de chaînes de longueurs quelconques sera d'utiliser les pointeurs (type accès). Une autre solution est d'utiliser le type Ada.Strings.map.Ubounded_String :

with Ada.Text_io, Ada.Strings.Unbounded;use Ada.Strings.Unbounded;procedure essaiChaines is type TableauDeChaines is array (Integer range <>) of Unbounded_String; ferme : TableauDeChaines (1..5) := (to_unbounded_string("poule",

to_unbounded_string("cheval"), to_unbounded_string("chat"),to_unbounded_string("chien"), to_unbounded_string("brebis"));

begin for i in ferme'range loop Ada.Text_io.put_line(to_string(ferme(i))); end loop;end essaiChaines;

2.4.1.11Conversions explicites entre types tableaux

with Ada.Text_io, Ada.Integer_Text_io;procedure essaiConversion is type VecteurI is array (Integer range <>) of Integer;

type Table is new VecteurI;-- type Table is array (Integer range <>) of Integer;

-- méthode retournant une chaîne de caractères représentative-- de l'état de la table (version non récursive) function toString2 (t : Table) return String is i : Integer:= 1;

Véronique Gaildrat

Support de cours ADA 95

31

s : String(1 .. t'length*(Integer'image(i)'length)); begin for j in t'range loop declare im : String := Integer'image(t(j)); begin s(i..i+im'length-1) := im; i := i + im'length; end; end loop; return s(1..i-1); end toString2;

-- méthode retournant une chaîne de caractères représentative-- de l'état de la table (version récursive) function toString (t : Table) return String is begin if t'length >0 then declare im : String := Integer'image(t(t'first)); begin return im & toString(t(t'first+1..t'last)); end; else return ""; end if; end toString; -- tri deux boucles simpleprocedure trier (v : in out VecteurI) is tmp : Integer;begin for i in v'first..v'last-1 loop for j in i+1..v'last loop if v(i) > v(j) then tmp := v(i); v(i) := v(j); v(j) := tmp; end if; end loop; end loop; end trier;

t : Table(1..10) := (2,8,6,4,3,5,1,7,9,0); v : VecteurI := (2,8,6,4,3,5,1,7,9,0);

begin Ada.Text_io.put("t avant le tri : "); Ada.Text_io.put_line(toString2(t)); trier(VecteurI(t)); Ada.Text_io.put("t après le tri : "); Ada.Text_io.put_line(toString2(t)); Ada.Text_io.put("v avant le tri : "); Ada.Text_io.put_line(toString(Table(v))); trier(v); Ada.Text_io.put("v après le tri : "); Ada.Text_io.put_line(toString(Table(v)));

end essaiConversion;

2.4.2Types articles

Une entité de type article est un ensemble de composants pouvant être de type différent.La spécification d'un type article doit d'indiquer son nom, la liste des composants (et leur type). Contrairement aux tableaux, il ne peut y avoir de type article anonyme (une variable ne peut être déclarée si le type n'existe pas). Le format général de la spécification est le suivant : type Article is record déclaration_entité 1; déclaration_entité 2; ...

Véronique Gaildrat

Support de cours ADA 95

32

déclaration_entité n; end record;

2.4.2.1Exemple

type NomDeMois is (janvier, février, mars, avril, mai, juin, juillet, août, septembre, octobre, novembre, décembre);

type Date is record -- déclaration d'un type d'article jour : Integer range 1 .. 31; -- 1° composant d'article mois : NomDeMois; -- 2° " " an : Integer range 1 .. 3000; -- 3° " " end record;L'ensemble de valeurs est : tout triplet ordonné contenant : (un jour, un mois, un an).

2.4.2.2Ensemble des opérations

:= = /= qui teste et affecte réellement le contenu de l'article (!= C)

2.4.2.3Sélection d'un composant : notation pointée

d : date; begin d.jour := 2; d.mois := mai; d.an := 1990; d.an := d.an + 1; end;

2.4.2.4Agrégats

Un agrégat assemble des valeurs de composants pour former la valeur d'un article.• De façon positionnelle :

(14, juillet, 1789)• Ou en nommant les composants :

(jour => 14, mois => juillet, an => 1789) (mois => juillet, jour => 14, an => 1789)

2.4.2.5Valeur par défaut d'un composant

type DataIso is record an : Integer range 1 .. 3000 := 1980; mois : NomDeMois := janvier; jour : Integer range 1 .. 31 := 1; end record; début : DataIso ; -- initialisée par défaut : début = (1980, janvier, 1) hier : date; -- non initialisée demain : date := (1, janvier, 1980); -- initialisation explicite

2.4.2.6Articles à partie discriminante

Le nom d'un type d'article peut être accompagné d'un paramètre dont la valeur sera définie au moment de la déclaration d'une entité.Exemple : subtype TailleLigne is Positive range 1..200;Attention : Chaîne doit être de taille définie sinon CONSTRAINT_ERROR a la déclaration de buf2 type Chaîne is array (TailleLigne range <>) of Character; type Ligne (taille : TailleLigne := 80) is record n : Natural := 0; texte : Chaîne (1 .. taille);

Véronique Gaildrat

Support de cours ADA 95

33

end record; buf1 : ligne(132); -- listings buf2 : ligne; -- écran 80 caractères par défaut

2.4.2.7Articles à variantes

Dans certaines entités composées: - un composant dépend d'un autre composant, - la structure dépend de la valeur d'un composant.Par exemple : - une personne de genre masculin peut être barbue ou non, - une personne de genre féminin peut avoir des enfants.

Certains composants n'existent que pour certaines valeurs d'un composant appelé discriminant.type MasculinFeminin is (m, f);type Personne (genre : MasculinFeminin := f) is record -- le record dépend du genre naissance : Date; -- pour tous case genre is -- PARTIE VARIABLE when m => -- seul les hommes sont barbus barbu : Boolean; -- UNE VARIANTE when f => -- seules les femmes font des enfants enfants : Natural; -- AUTRE VARIANTE end case;end record;

L'ensemble de valeurs se présente comme des triplets de la forme : genre = m naissance barbu ==> (m, (25, mars, 1940), false)

genre = f naissance enfants ==> (f, (5, janvier, 1701), 3)

L'ensemble des opérations est contraint : - On ne peut changer les discriminants qu'en changeant tout l'article. p : Personne; -- p.genre = f p := (m, uneDate, true); -- p.genre = m p.genre := ... -- ILLEGAL !!! -L'accès à un composant d'une variante dépend de la valeur du discriminant : p.barbu -- accessible si p.genre = m p.enfants -- lève CONSTRAINT_ERROR si p.genre = m - Contrainte de discriminant à la déclaration : jules : Personne (genre => m); julie : Personne := (f, (3, mars, 1928), 0);

2.4.2.8Sous-types d'articles

On peut, par exemple, déclarer un sous-type de l'article personne : subtype Homme is Personne (genre => m); subtype Femme is Personne (genre => f);

subtype Roi is Personne ([genre =>]m);louis : array (1..18) of Roi;john : Roi := (genre => m, naissance => (24, décembre, 1167), décès => (18, octobre, 1216), -- le champ décès : Date a été rajouté "…", -- le champ adresse : String(20) aussi

…);Tranche : louis (11..18)

Véronique Gaildrat

Support de cours ADA 95

34

Combinaison des formes précédentes : louis (11).naissance -- Date louis (11).naissance.an -- Integer range 1..3000 john.adresse (12) -- Character louis (14).adresse (1) -- Character successeur (john).naissance.jour -- Integer range 1..31

2.4.2.9Défauts et contraintes

lui : Homme; jules : Personne (genre => m); julie : Femme; autre : Personne;

begin autre := julie; -- vérifié autre := lui; -- à la lui := jules; -- compilation lui := autre; -- vérifie à l'exécution que autre ait un discriminant égal à m-- jules := julie; -- toujours incorrect;-- jules.sexe := f; -- ILLEGAL !! que deviendrait la barbe !end;

Entité contrainte : jules : Personne (genre => m); -- valeur imposée de discriminantEntité non contrainte : autre : Personne; -- pas de discriminant imposéc'est pourquoi autre peut être affecté avec jules ou julie …

Mais tout discriminant doit être initialisé, car la structure de l'entité en dépend.Donc : - valeur par défaut => les entités non contraintes sont autorisées, type Personne (genre : MasculinFeminin := f) is ... Ex : secrétaire : Personne; -- genre = f par défaut - pas de valeur par défaut => toute entité doit obligatoirement être contrainte à la déclaration. type Humain (genre : MasculinFeminin) is ... Ex : sœur : Humain (genre => f); -- la contrainte fixe le discriminant - On peut écrire : secrétaire := (m, uneDate, barbu => true); et puis : secrétaire := (f, plusTard, enfants => 2); mais on ne pourrait pas écrire : sœur := (m, hier, barbu => true); -- les humains ne changent pas de sexe !!

Sur le même modèle : subtype Indice is Integer range 0..5; type Polynôme (degré : Indice := 0) is record -- valeur par défaut a : VecteurI (0..degré); end record; p, q : Polynôme; -- ne sont pas contraints (degré initial = 0 mais peut être changé par affectation) … p := (3, (5, 2, 1, 3));

L'attribut p'constrained permet de savoir si une variable d'un type à discriminant est contrainte ou non.if not p'constrained then

p := (3, (5, 2, 1, 3)); end if;

Résumé :• Pas de valeur par défaut pour le discriminant : tout article est obligatoirement contraint à la

déclaration (pas de changement de valeur de discriminant au cour de l'exécution).• Une valeur par défaut pour chaque paramètre du discriminant.

Véronique Gaildrat

Support de cours ADA 95

35

o Si aucune valeur n'est donnée à la déclaration : valeur par défaut et article non contraint (peut changer de valeur de discriminant au cours de l'exécution.

o Si une valeur est précisée à la déclaration (= ou /= de la valeur par défaut), l'article est contraint

• L'attribut 'constrained permet de savoir à l'exécution si un article est contraint avant d'essayer de changer sa valeur de discriminant.

• Pour changer un discriminant, il faut affecter obligatoirement la totalité des champs de l'article par affectation d'un agrégat ou par affectation d'un autre article.

2.4.2.10Types dérivés d'articles à discriminant

On peut déclarer un nouveau type Homme dérivé de Personne mais qui ne sera plus compatible comme un sous-type. Les opérations primitives pourront lui être appliquées à condition d'effectuer une conversion en Personne.

type Homme is new Personne(genre=>m);

Il est possible de déclarer un type avec plusieurs discriminants :On désire manipuler des matrices dont la borne inférieure est obligatoirement 1, et qui ne sont pas contraintes à être carrées.

type Rectangle (ligne, colonne : Positive) is record mat : Matrice (1..ligne, 1..colonne);end record;r : Rectangle (ligne => 2, colonne => 3); -- ou bienr : Rectangle (2, 3);

On peut dans ce cas dériver le type Ligne3 qui sera un Rectangle avec obligatoirement 3 lignes :-- on ne peut pas écrire : subtype Ligne3 is Rectangle (ligne => 3); On écrira :type Ligne3 (colonne : Positive) is record mat : Matrice (1..3, 1..colonne);end record;ou bien :type Ligne3(colonne : Positive) is new Rectangle (ligne => 3, colonne => colonne);

2.4.3Composition de types

Etant donné le type tableau : type Pavage is array ( Positive range <>, Positive range <>) of Couleur;Nous pouvons construire : type Carrelage (n : Positive) is record carre : Pavage (1 .. n, 1 .. n); end record;

Le nom d'un discriminant peut servir dans une contrainte d'indice.Exemple : a, b : carrelage(8); -- 8 par 8La taille du pavage d'un carrelage est invariable,On ne peut créer de carrelage ayant un pavage rectangulaire.

En composant des carrelages : type Rubik (n : Positive) is record dessus, dessous, avant, arrière, gauche, droit : carrelage(n); end record;Un nom de discriminant peut figurer dans une contrainte de discriminant.

Véronique Gaildrat

Support de cours ADA 95

36

Exemple : cubeSimple : Rubik (2);

2.5Les types access

Les types access Ada correspondent aux types pointeurs des langages tels que C ou C++.Deux formes de types access :

• Accès à des entités (variables, articles, tableaux …).o Type access spécifiques à un pool de mémoire (terme Ada pour un "tas", zone mémoire

allouée dynamiquement).o Type access généralisés qui peuvent référer des entités déclarées statiquement.

• Accès à des sous-programmes.

Accessibilité des types accessLes règles d'accessibilité doivent maintenir une complète sécurité tout en gardant suffisamment de flexibilité. Pour plus d'efficacité, le maximum de contrôles est effectué à la compilation.Règle de base : La durée de vie (existence dynamique) d'une entité accédée doit être au moins aussi longue que celle du type access.

2.5.1Type access spécifiques à un pool

Pour les types déjà présentés, un nom d'entité est associé à l'entité elle-même. Sa durée de vie commence à la déclaration et se termine quand le contrôle de l'exécution quitte l'unité contenant la déclaration. declare x : Chose; -- x est créé begin x := ...; -- x est utilisé end; -- x est détruit

Pour les types access :• Une variable de type access est une référence qui désigne une entité et peut désigner

successivement plusieurs entités.type PtrTypeEntité is access TypeEntité;

• Ensemble de valeurs : les valeurs de type PtrTypeEntité pointent des entités de type TypeEntité et peuvent être considérées comme des références qui ne peuvent pointer que sur un TypeEntité,

ptr : PtrTypeEntité;

• les valeurs sont obtenues par l'exécution d'un allocateur new : ptr := new TypeEntité; crée une entité de type TypeEntité et affecte à ptr la référence (pointeur) de l'entité allouée,new TypeEntité; -- crée une nouvelle entité et retourne sa référencenew TypeEntité'(agrégat donnant la valeur initiale) -- l'entité est allouée et initialisée

• Accès à l'entité : unNom.all -- déférencement : à partir de la référence on obtient l'entité pointée

-- si l'entité est composée (pointeur sur un article par exemple) unNom.partie -- désigne un composant de l'entité UnNom.partie = UnNom.all.partie -- considéré comme une abréviation

• les entités allouées cessent d'exister quand elles sont récupérées par un "ramasse miettes" (garbage collector) ou explicitement libérées,

• la valeur particulière null ne désigne aucune entité, c'est la valeur initiale par défaut pour chaque PtrTypeEntité.

Ensemble d'opérations :Egalité : = /= comparaison de deux références (désignent-elles la même entité ?)

Véronique Gaildrat

Support de cours ADA 95

37

if x = y then -- vrai si x et y désignent la même entité if x.all = y.all then -- déférencement : vrai si l'entité désignée par x -- a la même valeur que l'entité désignée par yAffectations : := affectation (des références et pas des entités) x := y; -- copie la référence x.all := y.all; -- copie l'entité référencée x.partie := y.partie; -- copie une composant de l'entité référencée

Attention : -- deux références distinctes mais dont les entités référencées sont identiques : idem := PtrTypeEntité := new TypeEntité'(ptr.all); -- deux références pour la même entité alterEgo : PtrEntité := ptr;

Référence constante : c : constant PtrTypeEntité := new TypeEntité'(agrégat donnant la valeur constante);Comme constante, c doit référencer toujours la même entité mais la valeur de l'entité peut elle changer :c.all := ptr.all; -- autoriséc := ptr; -- illégal

Exemple : type PtrTypeEntité is access TypeEntité; x, y : PtrTypeEntité; -- initialement NULL begin x := new TypeEntité; -- x pointe sur l'entité 1 y := new TypeEntité; -- y pointe sur l'entité 2 x := y; -- deux noms pour l'entité 2, entité 1 est inaccessible y := new TypeOentité; -- y pointe sur l'entité 3, entité 2 est inaccessible end; -- x et y disparaissent -- entité 1, 2 et 3 sont récupérées par le garbage collector

2.5.2Quand utiliser les types access

. quand des entités apparaissent de façon inattendue : type NomDePersonne is access Personne; bébé : NomDePersonne; bébé := new Personne (genre =>f); -- félicitations . quand plusieurs personnes se réfèrent à la même entité : adaAugusta, ladyLovelace : nomDePersonne; . quand les entités sont liées les unes aux autres : adaAugusta.pere = lordByron . quand les relations changent au cours du temps : jackyKennedy.conjoint := aristoteOnnassis

2.5.3Comment créer une structure arborescente

type Personne; -- déclaration préliminaire incomplète type NomDePersonne is access Personne; type Personne is record

patronyme : String(1..4); père, mère, conjoint : NomDePersonne; end record;

2.5.4Utilisation "historique" de l'arborescence

Attention : le patronyme doit être exactement de 4 caractères si on souhaite effectuer l'initialisation à la déclaration. adam : NomDePersonne := new Personne'("adam", null, null, null);

Véronique Gaildrat

Support de cours ADA 95

38

eve : NomDePersonne := new Personne'("eve ", null, null, adam); adam.conjoint := eve;-- delay 9.0*mois; cain : NomDePersonne := new personne'("cain", adam, eve, null);-- remarque cain.pere := cain.mere.conjoint;

2.5.5Contraintes sur les types access

Des contraintes peuvent s'appliques au type accédé et au type access lui même.• Contraintes sur le type accédé :

type refPos is access Integer range 1.. Integer'last; rp : refPos := new Integer'(0); -- attention : CONSTRAINT_ERROR-- il vaudra mieux écrire :type refPos is access Positive; rp : refPos := new Positive'(0); -- attention : CONSTRAINT_ERROR

• type access sur un type tableau non contraint :type refMatrice is access Matrice;rm : RefMatrice;rm := new Matrice(1..5, 1..3); -- matrice contrainte à l'allocationrm := new Matrice'(1..5 => (1..4 => 0.0)); -- matrice contrainte et initialisée à l'allocation 'rm := new Matrice; --illégalAccès aux éléments de la Matrice :rm(1,1) qui peut être considéré comme une abréviation de rm.all(1,1)

• Contrainte sur le type accesso introduction d'un sous-type :

subtype RefMatrice_3x4 is RefMatrice(1..3, 1..4);rm_3x4 : RefMatrice_3x4;

o -- ne peut également référencer que des matrices de 3lignes, 4 colonnes rm_3x4 : RefMatrice(1..3, 1..4);

o L'allocation se fera par une contrainte explicite :rm_3x4 := new Matrice(1..3, 1..4);ou par une déclaration de sous-type :subtype Matrice_3x4 is Matrice(1..3, 1..4);rm_3x4 := new Matrice_3x4;

o Comme Matrice_3x4 contient les bornes du tableau, on peut initialiser une nouvelle entité par:rm_3x4 := new Matrice_3x4 (others => (others => 0.0));

• Chaînes à longueur variable :type Chaîne is access String;s : Chaîne := new String'("une chaîne"); -- ne pas oublier la ' : allocation et initialisationtype Chaînes is array (Positive range <>) of Chaîne;ferme : constant Chaînes := (new String'("cheval"),

new String'("oie"), new String'("vache"));for i in ferme'range loop

Ada.Text_io.put_line(ferme(i).all);end loop;Le patronyme du type Personne pourrait lui aussi être de type access : type TypePatronyme is access String; type Personne is record

patronyme : TypePatronyme; père, mère, conjoint : NomDePersonne; end record;

adam : NomDePersonne := new Personne'(new String'("adam"),null, null, null);

• On peut contraindre une variable d'accès à ne désigner que des entités ayant une certaine valeur de discriminant :

Véronique Gaildrat

Support de cours ADA 95

39

Une personne mieux définie sera : type MasculinFeminin is (m, f); type Personne (genre: MasculinFeminin);

type NomDePersonne is access Personne; type Personne (genre : MasculinFeminin) is record patronyme : String(1..4); père : NomDePersonne (m); -- contraint à être de genre m mère : NomDePersonne (f); -- contraint à être de genre f conjoint : NomDePersonne; -- non contraint (PACS autorisé !) end record;

Moins libéral ! : type Personne (genre : MasculinFeminin) is record patronyme : String(1..4); père : NomDePersonne (m); -- contraint à être de genre m mère : NomDePersonne (f); -- contraint à être de genre f case genre is when f => époux : NomDePersonne (m); when m => épouse : NomDePersonne (f); end case; end record;

Exercice :Ecrire une procédure de tri utilisant la structure d'arbre binaire. type Vecteur is array (Integer range <>) of Integer; type Nœud; type Arbre is access Nœud; type Nœud is record val : Integer; gauche, droit : Arbre; end record; procedure tri (a : in out Vecteur) is i : Integer; base : Arbre := null; procedure insérer (t : in out Arbre; v : Integer) is begin if t = null then t := new Nœud'(v, null, null); else if v < t.val then insérer (t.gauche, v); else insérer (t.droit, v); end if; end if; end insérer; procedure extraire (t : Arbre) is begin if t /= null then extraire (t.gauche); a(i) := t.val; i := i + 1; extraire (t.droit); end if; end extraire; begin for j in a'range loop insérer (base, a(j)); end loop; i := a'first; extraire (base); end tri;

Véronique Gaildrat

Support de cours ADA 95

40

2.5.6Types access généralisés

Les types access généralisés permettent un accès indirect à des entités déclarées qui le permettent ainsi qu'à des entités allouées.

-- l'emploi de all précise qu'on a à faire avec un access généralisé (/= new qui précise un accès à une entité allouée dynamiquement)type RefInt is access all Integer; ri : RefInt;-- aliased : marquage d'une variable pouvant être référencée par un access généralisé i : aliased Integer; …-- référence obtenue par l'emploi de l'attribut access et non de l'allocateur new ri := i'access; ri.all := 25; -- déférencement pour accéder à l'entier référencé

On peut restreindre l'accès en lecture seule en remplaçant dans la définition all par constant : type RefConsInt is access constant Integer;rci : RefConsInt;c : aliased constant Integer := 2001;…rci := i'access; -- variable aliasedrci := c'access; -- constante aliased

Les types access généralisés peuvent aussi référencer un type composé :type RefString is access all String;rs : RefString;hello : aliased String := "bonjour" & Ada.Characters.Latin_1.cr

& Ada.Characters.Latin_1.lf; ch : aliased String(1..9) := "bienvenue"; … rs := hello'access; rs := ch'access; -- illégal car rs non contraint et ch contrainte rs.all(1..5) := "salut"; -- respect des bornes !

rs.all := "salut " & Ada.Characters.Latin_1.cr & Ada.Characters.Latin_1.lf;

La conversion est possible de type access vers type access généralisé à condition que les contraintes sur le type access et sur le type accédé soient respectées. type RefInt is access Integer; type RefGInt is access all Integer; ri : RefInt := new Integer'(3); i : aliased Integer := 5; rgi : RefGInt := i'access; … rgi := RefGInt(ri);

2.5.7Paramètres access de sous-programmes

Exemple :procedure p (ptr : access String) is

type RefString is access all String;rs : RefString;

begin rs := RefString(ptr);

Ada.Text_io.put_line(ptr.all);end p;

Caractéristiques :

Véronique Gaildrat

Support de cours ADA 95

41

• ptr est de type anonyme donc on ne peut pas déclarer d'autres variables à l'intérieur du sous-programme du même type que p et on ne dispose donc pas de l'égalité et de l'affectation. Il faut déclarer un type équivalent et faire une conversion de type. Quand la conversion se fait vers un type interne au sous-programme on évite les problèmes d'accessibilité. Dans le cas contraire, la vérification est effectuée à l'exécution. Si elle échoue l'erreur PROGRAM_ERROR est levée.

• ptr ne doit pas être null lors de l'appel (vérifié à l'exécution) sinon CONSTRAINT_ERROR.

2.5.8Accès à un sous-programme

Un type access peut faire référence à un sous-programme et le sous-programme peut être appelé directement par déférençage. Exemple :

with Ada.Text_io, Ada.Float_Text_io, Ada.numerics, Ada.Numerics.Elementary_Functions;

procedure essai is type fonctionMath is access function (f : Float) return float; t : fonctionMath; x, theta : Float;

function intégrer (f : fonctionMath; a, b : Float) return Float isbegin

return (f(a) + f.all(b))/2.0; -- .all n'est obligatoire que lorsqu'il n'y a pas de paramètres

end intégrer;begin

Ada.Float_Text_io.put(intégrer(Ada.Numerics.Elementary_Functions.Sin'access,0.0, Ada.numerics.pi/2.0));

end essai;Tous les sous-programmes ne peuvent pas être référencés :

Des opérations prédéfinies du paquetage Standard : o + - … = et /= , o les attributs tels que 'pred, 'succ …o les sous-programmes déclarés implicitement par dérivation !!!!!!!!!!!!!!!!!!!o les sous-programmes donnés uniquement dans des corps et donc non accessibles.

Par contre, l'accès à de sous-programmes sera très utilisé pour l'interfaçage avec d'autres langages. Dans ce cas on utilise des directives au compilateur (pragmas) pour préciser les conventions d'appel.

Véronique Gaildrat

Support de cours ADA 95

42

3. Unités de BibliothèqueUne Unité de Bibliothèque est une unité de programme qui peut être compilée séparément. Les U de B sont généralement hiérarchisées pour permettre la conception et la mise en œuvre en équipe de grosses applications.

3.1Sous-programmes

Un sous-programme est une boite noire qui reçoit des valeurs d'entrée et, après calcul, retourne des valeurs de sortie.Les sous-programmes Ada peuvent être des fonctions ou des procédures.La spécification d'un sous-programme se fait en donnant : - le nom du sous-programme, - le nom, le type et pour une procédure le mode de passage des paramètres formels, - pour une fonction, le type de la valeur retournée. procedure identificateur1 [(partie_formelle)] is partie_déclarative begin ... end identificateur1;

function identificateur2 [(partie_formelle)] return nomDe(sous)Type is partie_déclarative begin ... end identificateur2;

En cas de récursivité mutuelle, la spécification d'un des deux sous-programmes sera donnée avant le corps des deux sous-programmes : function identificateur2 [(partie_formelle)] return nomDe(sous)Type; -- spécification procedure identificateur1 [(partie_formelle)] is partie_déclarative begin xx := identificateur2(…); … end identificateur1;

function identificateur2 [(partie_formelle)] return nomDe(sous)Type is partie_déclarative begin identificateur1(…); ... end identificateur2;

Attention : Les surcharges d'opérateurs ne peuvent pas être des U de B en soi, mais doivent toujours être contenues dans une autre U de B (en général un paquetage).

3.1.1Modes de passage de paramètres

Trois modes de paramètres sont possibles pour les procédures :in : Le paramètre formel est une constante (qui peut être obtenue par évaluation d'une expression) et ne permet que la lecture de la valeur du paramètre effectif correspondant.in out : Le paramètre formel est une variable et permet la lecture et la modification de la valeur du paramètre effectif correspondant.out : Le paramètre formel est une variable qui permet la lecture et la modification de la valeur du paramètre effectif correspondant. Contrairement au mode de passage en in out, le paramètre effectif n'a pas besoin d'être initialisé avant l'appel.

Le mode in est le mode par défaut et le seul possible pour les fonctions.

Selon le type de paramètre, le passage se fait :

Véronique Gaildrat

Support de cours ADA 95

43

- Par copie pour les types scalaires pour les types accès

Copie à l'appel pour in et copie au retour pour out, deux copies dans le cas in out. - Par copie ou par référence pour les types tableaux pour les types articles

Pour garder la portabilité, il ne faut pas présupposer un mode particulier de passage.On considère le passage de paramètres sous l'aspect logique du transfert au moment de l'appel et du retour. Chaque compilateur Ada est libre de choisir la technique d'implantation.

A noter : pour le passage de paramètres de tableaux, il peut y avoir glissement entre les indices du tableau réel et du tableau formel. seul le nombre des éléments compte (et le type bien sur).

3.1.2Paramètres nommés et paramètres par défauts

procedure essai (pf1 : in Type1; pf2, pf3 : in out Type2; pf4 : out Type4) is …end essai;

Ordre des paramètres réels :• donnés dans l'ordre de déclaration des paramètres formels.

essai (pr1, pr2, pr3, pr4);• comme pour les agrégats, il est possible de nommer le paramètre formel associé à un paramètre réel.

Ainsi l'ordre n'est plus à conserver.essai (pf4 => pr4, pf1 => pr1, pf3 => pr3, pf2 => pr2);

• Les deux formes peuvent être utilisées conjointement : d'abord les paramètres positionnels dans l'ordre puis les paramètres nommés dans un ordre quelconque.essai(pr1, pr2, pf4 => pr4, pf3 => pr3);

Paramètres par défaut :Une spécification de sous-programme peut définir une valeur par défaut pour des paramètres de mode IN. Ces paramètres doivent être placés en fin de liste

Exemple : procedure racineCarrée (x : in Float; y : out Float; précision : in Float);L'appel est effectué ainsi : racineCarrée (3.0, v, 0.0001);

En donnant la valeur de précision par défaut : procedure racineCarrée (x : in Float; y : out Float; précision : in Float:= 0.0001);L'appel peut être : racineCarrée (v, w, 0.01); -- précision explicite racineCarrée (v, w); -- précision par défaut

Autre exemple : type Alcool is (gin, vodka, whisky); type Style is (onTheRocks, sec, tonic); type Decor is (olive, citron);La boisson standard est : procedure drink (base : Alcool := gin; servi : Style := onTheRocks; plus : Decor := olive);Les commandes au bar se font par : drink (servi => tonic); -- gin, tonic, olive drink (vodka, plus => citron); -- vodka, onTheRocks, citron drink (whisky, sec); -- whisky, sec, olivePour un habitué : function alcoolFavori (today : Jour) return Alcool is begin case today is when lundi .. vendredi => return gin; when samedi | dimanche => return vodka; end case; end arsouille;

Véronique Gaildrat

Support de cours ADA 95

44

procedure drink (base : Alcool := alcoolFavori (j); servi : Style := onTheRocks; plus : Decor := olive) is

... end drink;

3.1.3Sous-programmes de type procédure

Exemple : procedure équation (a, b, c : in Float; racine1, racine2 : out Float; ok : out Boolean) is d : constant Float := b ** 2 - 4 * a * c; begin ok := d >= 0.0; if not ok then racine1 := 0.0; -- toujours donner une valeur aux paramètres en out racine2 := 0.0;

else racine1 := (-b + sqrt(d))/(2.0 * a); racine2 := (-b - sqrt(d))/(2.0 * a); end if; end équation;

Les paramètres formels sont : a,b et c en entrée, racine1, racine2 et ok en sortie.Les paramètres réels devront donner des valeurs pour a,b et c et récupérer le résultat dans racine1, racine2 et ok.

3.1.4Sous-programmes de type fonction

Les paramètres d'une fonction sont toujours de mode IN.

Exemple de sous-programme de type fonction : type Vecteur is array (Integer range <>) of Float; function produitScalaire (x, y : Vecteur) return Float is total : Float := 0.0; j : Integer range y'first..y'last+1 := y'first; begin if x’length /= y’length then -- déclenchement d'une erreur raise CONSTRAINT_ERROR; end if; for i in x'range loop total := total + x(i) * y(j); j := j + 1; end loop; return total; end produitScalaire;

function rev (x : Vecteur) return Vecteur is r : Vecteur (x’range); begin for i in x’range loop r(i) := x(x’first+ x’last- i); end loop; return r; end rev;

Avec les déclarations suivantes : a : Vecteur (1 .. n);

Véronique Gaildrat

Support de cours ADA 95

45

b : Vecteur (2..n+1) r : Float;

On peut appeler les fonctions ainsi : r := produitScalaire (a, b); a := rev(b); -- glissement des indices de b vers ceux de a

De la même manière que pour les appels de procédures, les appels de fonctions peuvent être effectués en donnant les paramètres effectifs de façon positionnelle ou nommée.

Les fonctions comme les procédures peuvent être déclarées sans paramètres.

Exemple : function accesAutorise return Boolean is begin ... end accesAutorise;

Sera appelée ainsi : if accesAutorise then ... end if;

Dans ce cas, on remarque qu'il n'est pas possible de faire la distinction entre un nom de variable et l'appel d'une fonction sans paramètres. 3.1.5Notion de surcharge

Des opérateurs peuvent être homonymes : - pour des entiers -- 2 * 4 - pour des flottants -- 2.0 * 4.0 function "*" (x, y : Integer) return Integer; function "*" (x, y : Float) return Float;

Des procédures peuvent être homonymes : procedure put (s : String); procedure put (i : Integer); procedure put (b : Boolean); appelées : put ("ceci est "); put (condition);

On dit que ces sous-programmes homonymes sont surchargés.

Autres types de surcharges : type Diptère is (mouche, fourmi, cousin, moustique); type Parent is (père, mère, frère, sœur, cousin, cousine);On a deux homonymes pour "cousin". insecte : Diptère := cousin; -- le diptère proche : Parent := cousin; -- fils de l'oncleL'identification utilise le contexte. On doit qualifier si le contexte ne suffit pas : procedure traiter (d : Diptère); procedure traiter (p : Parent); traiter (insecte); -- aucun problème traiter (proche); -- avec les variables traiter (fourmi); -- aucun problème traiter (sœur); -- avec les littéraux non surchargés traiter (cousin); -- quel cousin ?La solution consiste à qualifier : traiter (Parent'(cousin))

Véronique Gaildrat

Support de cours ADA 95

46

La fonction qui réalise le produit scalaire de deux vecteurs aurait pu être déclarée de la façon suivante : function "*" (x, y : Vecteur) return Float is

… -- même corps end "*";Et donc être appelée ainsi : r := "*"(a, b); -- ou mieux : r := a * b; -- produit scalaire des deux vecteurs a et b

− Toute méthode peut être surchargée => utiliser le même nom de méthode mais avec des types de paramètres différents. C'est par les paramètres que le compilateur sait quelle méthode exécuter.

− Tout opérateur peut être surchargé SAUF := qui ne peut jamais être surchargé !!− L'opérateur "=" peut être surchargé avec tout type déclaré : function "=" (a, b : TypeQuelconque) return Boolean;

Dans ce cas : function "/=" (a, b : TypeQuelconque) return Boolean;

est automatiquement définie.− La surcharge de la fonction "=" peut retourner un autre type que Boolean, mais dans ce cas "/=" n'est pas

définie. 3.1.6Applications des sous-programmes ADA

Les sous-programmes sont des unités de programme exécutables qui encapsulent des algorithmes.Ils ont trois applications principales : - unités de programmes principales, - définition de contrôle fonctionnel, - définition d'opérations sur des types de données abstraits.

3.1.6.1Programmes principaux

Le programme principal en ADA n'a pas de structure particulière, mais est seulement une procédure sans paramètres. Généralement, le programme principal doit être totalement affranchi de tout détail d'implantation.

3.1.6.2Contrôle fonctionnel

Avec une conception fonctionnelle descendante traditionnelle (par raffinages successifs) le problème est résolu en partant du plus haut niveau, puis décomposé en fonctions ou procédures de complexité toujours moindre.ADA offre la possibilité de cacher des fonctions de bas niveau.

Exemple : procedure analyse is données : TypeDonnées; procedure acquérir (données : out TypeDonnées) is separate; procedure vérifier (données : in TypeDonnées) is separate; procedure imprimer (données : in TypeDonnées) is separate; begin acquérir (données); vérifier (données); imprimer (données); end analyse;

Grâce à la clause separate, les spécifications des sous-programmes de bas niveaux sont visibles, mais leurs corps sont compilés séparément (pouvant même être contenus dans d'autres fichiers) et donc cachés logiquement et physiquement.

Le corps du sous-programme imprimer dans un fichier imprimer.adb pourra être : with Ada.Text_io, X; separate (analyse); procedure imprimer (données : in TypeDonnées) is

Véronique Gaildrat

Support de cours ADA 95

47

begin ... -- est client de X et de Ada.Text_io end imprimer;celui de vérifier dans un fichier verifier.adb : with Y; separate (analyse); procedure vérifier (données : in TypeDonnées) is begin ... -- est client de Y end vérifier;

L'avantage est que la procédure analyse n'a pas à faire référence aux unités utilisées par les sous-programmes quelle contient. Ainsi, en cas de recompilation de X, seule imprimer aura à être recompilée.

3.1.6.3Opération primitives sur des Types de Données Abstraits

Avec une conception par Types de Données Abstraits (cf paquetages), les sous-programmes sont considérés comme des opérations primitives sur un type de données abstraits, constituant un tout logique.On utilise les packages pour encapsuler et renforcer l'abstraction des données pour le client du package.

Véronique Gaildrat

Support de cours ADA 95

48

3.2Paquetages (Package Ada)

3.2.1Définition et généralités

3.2.1.1Définition

Définition : Un package Ada est une U de B (peut être compilé séparément) qui va être utilisée pour mettre en œuvre :1. l'encapsulation de données :

Un package contient et exporte la déclaration d'un type (TDA) et un ensemble de sous-programmes dits primitifs, manipulant les entités de ce type et réalisant un tout logique.Exemple : Le package Pile_p contient au minimum : - le type Pile, - les cinq opérations primitives : sommet,

dépiler, empiler, pile vide, pile pleine.

2. l'abstraction de données :Ada permet de masquer au client d'un package la structure interne d'un élément du type exporté par le package.

Pour l'exemple de la Pile, il faut que le client ne puisse la manipuler qu'au travers de l'utilisation des opérations primitives et ne puisse donc accéder qu'à l'élément situé au sommet de la Pile.

Une U de B qui fait référence à un package déjà compilé doit observer deux règles : -- au début du programme, référence des packages utilisés (dont il est lui-même client) : with pac1, pac2, ..., pac_n; procedure proc is ...

-- chaque référence à une entité d'un package fournisseur fait référence au nom de ce package : pac1.p1 (...); -- procédure p1 du package pac1 ... pac3.p1 (...); -- procédure p1 du package pac3 ... end proc;

3.2.1.2Spécification et corps de package

Un package est constitué de deux parties bien distinctes : - La spécification constitue l'interface et est visible à un client. - Le corps de package constitue l'implantation et est protégé.

La structure générale des packages ADA est la suivante :Exemple de séparation entre spécification et corps de package :-- Interface : package Exemple_p is -- fichier Exemple_p.ads -- partie publique : spécification type TypeExporté is ...; variableExportée : ...; procedure procExportée is ...; -- partie privée : implémentation private … end Exemple_p;-- Corps : package body Exemple_p is -- fichier Exemple_p.adb

Véronique Gaildrat

Support de cours ADA 95

49

-- partie cachée : corps, implémentation procedure procLocale is ... end procLocale; procedure procExportée is ... end proc_Exportée; begin -- Initialisations à l'élaboration du paquetage end Exemple_p;

Les sous-programmes exportés sont donc déclarés dans la spécification, et développés dans le corps du package.Les packages ont pour but de : - Faciliter la maintenance : . L'information est localisée. . On peut modifier le corps sans répercussion à l'extérieur du module (tant que l'interface reste inchangée, les clients n'ont pas à être recompilés). - Permettre une conception distribuée par : . Division de l'application en unités (modules). . Définition des interfaces de chaque module.

. Distribution des corps (qui sont par définition indépendants) dont l'écriture peut être faite par différentes équipes.

- Ouvrir la voie à la réutilisation de composants logiciels.

3.2.2Types classiques de packages

Plusieurs types de packages existent : - Collection de déclarations communes, - bibliothèque de sous-programmes, - définition de type de données abstraits, - définition d'entités isolés (machines à états abstraits).

3.2.2.1Collection de déclarations communes

with Ada.Text_io, Ada.Integer_Text_io;

package Calendrier is type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); type Mois is (janvier, février, mars, avril, mai, juin, juillet, août,

septembre, octobre, novembre, décembre); type Date is record j : Jour; num : Positive range 1 .. 31; m : Mois; a : Positive; end record;

jourParMois : constant array (Mois) of Integer := (février=> 28, avril|juin|septembre|novembre => 30, others => 31); -- ou (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); paques1990 : constant Date := (dimanche, 15, avril, 1990); end Calendrier;

3.2.2.2Exemple de bibliothèque de sous-programmes

package Trigo is function cos (angle : in Float) return Float; function sin (angle : in Float) return Float; function tan (angle : in Float) return Float;

Véronique Gaildrat

Support de cours ADA 95

50

end Trigo;

package body Trigo is -- Entités internes masquées à l'utilisateur longueur : constant := 5; function factorielle (n : Positive) return Positive is begin …; end; -- Entités exportées function cos (angle : in Float) return Float is begin …; end; function sin (angle : in Float) return Float is begin …; end; function tan (angle : in Float) return Float is begin …; end; end Trigo;

3.2.2.3Exemple de définition de package exportant un nouveau type ( !! /= TDA)

package FileEntiers_p is -- La définition du type est faite ici (pas d'abstraction de données) -- cette définition est accessible à l'utilisateur. type Tab is array (Positive range <>) of Integer; type FileEntiers (taille : Positive) is record data : Tab(1..taille); dernier : Natural := 0; end record; -- opérations primitives exportées procedure vider (f : in out FileEntiers); procedure insérer (val : Integer; f : in out FileEntiers); procedure enlever (f : in out FileEntiers); function premier (f : FileEntiers) return Integer; function estVide (f : FileEntiers) return Boolean; end FileEntiers_p;

Attention : Ce package ne protège pas ses données. Il y a encapsulation, mais pas abstraction des données. Rien n'empêche le client de faire f.data(2) := …; ce qui contredit la spécification de la File qui exige que les éléments soient insérés en queue et enlevés en tète.

3.2.2.4Exemple de définition de machine abstraite

package Tortue is type EtatPlume is (baissée, levée); type Direction is (nord, est, sud, ouest); procedure avancer (nbPoints : Positive); procedure tourner; -- dans le sens des aiguilles d'une montre procedure poserPlume; procedure leverPlume; function positionEnX return Positive; function positionEnY return Positive; function versOù return Direction; end Tortue;

Véronique Gaildrat

Support de cours ADA 95

51

package body Tortue is -- Le type n'est pas privé, mais simplement invisible à l'intérieur du body type LaTortue is record x, y : Positive; plume : EtatPlume; vers : Direction; end record; t : LaTortue; procedure avancer (nbPoints : Positive) is begin

case t.vers iswhen nord => t.y := t.y + nbPoints;when sud => t.y := t.y - nbPoints;when est => t.x := t.x + nbPoints;when ouest => t.x := t.x - nbPoints;

end case; end avancer; procedure tourner is begin t.vers := Direction'succ(t.vers); exception when CONSTRAINT_ERROR => t.vers := nord; end tourner; procedure poserPlume is begin t.plume := baissée; end poserPlume; procedure leverPlume is begin t.plume := levée; end leverPlume; function positionEnX return Positive is begin return t.x; end positionEnX; function positionEnY return Positive is begin return t.y; end positionEnY; function versOù return Direction is begin return t.vers; end versOù; begin t := (10, 30, levée, nord); end Tortue;

3.2.3Clients de packages

Pour utiliser les opérations, variables ou types exportés définis dans un package, il faut :- soit utiliser la notation pointée :

nomPackage.entitéExportée

Exemple : x := r * Trigo.cos (théta); Tortue.avancer (10); if Tortue.position_x < 3 then ... end if;

- soit utiliser la clause use qui permet d'éviter la notation pointée. La facilité d'écriture qui en découle entraîne une perte de précision sur la provenance des entités utilisées.

declare use Exemple_p; begin ... entitéExportée ... end;

Véronique Gaildrat

Support de cours ADA 95

52

Exemple : declare use Calendrier; noelProchain : Date; begin noelProchain := (lundi, 25, décembre, 2001); end; Au lieu de : noelProchain := (calendrier.lundi, 25, calendrier.décembre, 1990);

Exemple :with Ada.Text_io, Ada.Integer_Text_io, Tortue;use Ada.Text_io, Ada.Integer_Text_io, Tortue;procedure essai isbegin

put(" x : "); put(positionEnX, 3); -- il faut savoir faire la distinction ! put(" y : "); put(positionEnY, 3); put(" dir : "); put(Direction'pos(versOù), 1); new_line; tourner; avancer(10); put(" x : "); put(positionEnX, 3); put(" y : "); put(positionEnY, 3); put(" dir : "); put(Direction'pos(versOù), 1); new_line;

end essai;

Dilemme : • ne pas utiliser use, garder l'origine des entités utilisées, mais devoir écrire :

a, b, c : NombreComplexes.Complexe; -- origine explicite…c := NombreComplexes."+"(a, b); -- notation pointée pour les opérateurs !

• utiliser le use, bénéficier de la notation infixée, mais perdre l'origine du type :use NombreComplexes;…a, b, c : Complexe; -- ignorance de l'origine du type Complexe !…c := a + b; -- notation infixée pour les opérateurs

• mieux : utiliser la clause use de type :use type NombreComplexes.Complexe;a, b, c : NombreComplexes.Complexe; -- origine explicite…c := a + b; -- notation infixée pour les opérateurs

Exercice :La séquence définie par : xn+1 = xn.55 MOD 213 fournit des "pseudo" chiffres aléatoires.La valeur initiale x0 doit être un entier impair compris entre 0 et 213 .Ecrire un package Random contenant une procédure init pour initialiser la séquence et une fonction next qui fournit la valeur suivante.

package Random is bad : exception; modulus : constant := 2**13; subtype Small is Integer range 0..modulus; procedure init (germe : Small); -- ne doit pas être un multiple de 2 function next return Small;

Véronique Gaildrat

Support de cours ADA 95

53

end Random; package body Random is multiplier : constant:= 5**5; x : Small; procedure init (germe : Small) is begin if germe mod 2 = 0 then raise bad; end if; x := germe; end init; function next return Small is begin x := x * multiplier mod modulus; return x; end next; end Random;

3.2.4Résumé

1. Un package peut être défini dans n'importe quelle région déclarative :

package local à un autre package, à un sous-programme, à un bloc.

2. Un package peut être une unité de compilation :

spécification et corps compilés séparément et accessibles par des clients.

3. La partie spécification et la partie corps d'un package sont indépendantes.

la compilation séparée des deux parties est possible, la partie spécification peut être livrée à une équipe de développeurs avant la réalisation

du corps.

4. Un package peut exporter :

des types, des variables ou des constantes, des sous-programmes, des paquetages, des exceptions (vues plus loin).

3.2.5Types privés

Les types privés permettent de cacher les détails de structure afin de respecter le principe d'abstraction des données en : - masquant la représentation interne des données, - exportant les opérations de manipulation de ce type, - empêchant un usage non contrôlé.

- types privés : - déclaration d'entités de ce type - affectation - test d'égalité (= et /=)

- utilisation des sous-programmes définis pour ce type (opérations primitives)

Exemple :package Point2D_p is

-- le package exporte le type Point2D –protégé-type Point2D is private;-- Le Point2D est créé en (0,0)

Véronique Gaildrat

Support de cours ADA 95

54

procedure creerPoint (p : out Point2D);-- Le Point2D est translaté de (x,y)procedure translater (p : in out Point2D; x, y : Float);-- retourne une chaîne ce caractères représentative du Point2Dfunction toString(p : Point2D) return String;

privatetype Point2D is array (1..2) of Float; -- tableau de deux flottants

end Point2D_p;

Le corps est ici totalement identique à ce qu'il était auparavant.

3.2.6Types limités

Un type limité est un type pour lequel l'affectation et l'égalité prédéfinies ne sont pas disponibles.L'égalité peut être redéfinie ("/=" le sera aussi si l'égalité retourne un Boolean).Un type composé contenant un composant limité doit obligatoirement être déclaré limité.Exemple :

type Rationnel is limited recordnumérateur : Positive := 1;dénominateur : Positive := 1;

end record;Ainsi l'égalité pourra être redéfinie afin de pouvoir établir que : 1/3 = 2/6 !

function pgcd (a, b : Positive) return Positive isbegin

if a = b then return a; elsif a > b then return pgcd(a - b, b); else return pgcd (a, b-a); end if;

end pgcd;

procedure normaliser (r1 : Rationnel; r2 : out Rationnel) is p : Positive;

begin p := pgcd(abs(r1.numérateur), r1.dénominateur); r2.numérateur := r1.numérateur / p; r2.dénominateur := r1.dénominateur / p;

end normaliser;

function "=" (r1, r2 : Rationnel) return Boolean is -- "=" non prédéfinie pour le type Rationnel

n1, n2 : Rationnel;begin normaliser(r1, n1); normaliser(r2, n2);

if (n1.numérateur /= n2.numérateur) or (n1.dénominateur /= n2.dénominateur) then return false; else return true; end if;end "=";type Essai is array (1..3) of Rationnel;function "=" (t1, t2 : Essai) return Boolean is -- "=" non prédéfinie pour le type Essaibegin

for i in t1'range loop if (t1(i) /= t2(i)) then return false; end if; end loop; return true;

Véronique Gaildrat

Support de cours ADA 95

55

end "=";

t1 : Essai ;t2 : Essai ;

L'affectation n'est jamais permise, donc : pas de fonction qui retourne un type limité.procedure clone (vIn : TypeLimité; vOut : out TypeLimité); -- fournit une copie de vIn

3.2.7Types limites privés

Les types limités privés sont utilisés pour restreindre les opérations possibles sur un TDA à celles déclarées dans le package. Ceci s'applique surtout à des TDA dynamiques dont le type accessible est en fait un pointeur qui donne l'accès à des données allouées dynamiquement.Dans ce cas, le test d'égalité et l'affectation n'ont pas de sens car ils s'appliquent aux pointeurs et pas aux contenus.Exemple :

package Pile_p is type Pile is limited private;

procedure creerPile(p : out Pile);procedure empiler (p : in out Pile; x : in Integer);procedure depiler (p : in out Pile);function sommet (p : Pile) return Integer;function estVide (p : Pile) return Boolean;function estPleine (p : Pile) return Boolean;function hauteur (p : Pile) return Natural;function "=" (p, q : Pile) return Boolean;function toString(p : Pile) return String;PILE_PLEINE, PILE_VIDE : exception;

private type Cellule; type Pile is access Cellule type Cellule is record valeur : Integer; suiv : Pile; end record; end Pile_p;

L'affectation est interdite car le type Pile est limité privé.L'égalité a été redéfinie.

Il y a une alternative aux types limités privés qui restent assez lourds dans leur emploi car un type limité dans une conception hiérarchique entraîne souvent une cascade de types limités parfois sans qu'on le souhaite : type Pile is private; function clone (p : Pile) return Pile; -- permet de duppliquer p (faire une copie) function "="(p1, p2 : Pile) return Pile; -- permet de tester l'égalité des contenus si redéfinie

Attention à bien spécifier que l'affectation ":=" existe mais ne fait que copier les références (pointeurs).On se rapprocha ainsi des méthodes prédéfinies de Java pour les types qui ne sont pas des types de base :

• la fonction equals qui teste les contenus à redéfinir• la fonction "=" qui teste l'égalité de références si non redéfinie• l'affectation ":=" qui affecte les références (crée un alias)• la fonction clone qui retourne une copie à redéfinir

3.3Surnommage

Les clauses use peuvent être interdites (critères de qualité du logiciel). Dans ce cas, le nom complet U_de_B.nomEntité devra être donné. Parfois, ce nom complet peut s'avérer vraiment trop long (rappel : il est souhaitable d'avoir des noms explicites !).

Véronique Gaildrat

Support de cours ADA 95

56

Pour conserver un bon niveau de lisibilité sans que les noms soient perçus comme insupportablement longs, on peut utiliser le surnommage qui n'aura qu'un effet purement local.

− Surnommage d'entité de package :v : Float renames DonnéesAéroplane.vitesseDeCroisière;

-- permet d'utiliser v dans les calculs locaux

− Surnommage partiel d'objets composés :type Personne is record naissance : Date; …end record;type Population is array (Integer range <>) of Personne;

Plutôt que d'écrire :gens : Population (1..MAX);…for i in … loop gens(i).naissance.jour := …; gens(i).naissance.mois := …;end loop;

On pourra écrire :for i in … loop

declare d : Date renames gens(i).naissance;

begin d.jour := …; d.mois := …;

end;end loop;

Il existe d'autres manières d'utiliser le surnommage :

− Surnommage de sous-programmes, utilisé pour donner un sens particulier chez un client :function prodScalaire (x, y : Table) return Float renames "*";

Les paramètres et le type retourné sont identiques à ceux du sous-programme surnommé.Dans le cas de surnommage d'opérateurs, quand on n'utilise pas la clause use, on évite l'écriture :

ps := "*"(t1, t2);

− Surnommage comme moyen de donner un corps de sous-programme dans un corps de package :package Toto_p is … procedure toto(…) ;end Toto_p;

with Titi_p;package body Toto_p is … procedure toto(…) renames Titi_p.titi; -- mêmes paramètresend Toto_p;

− Surnommage de sous-unités de bibliothèque :Permet par exemple de garder la compatibilité avec d'anciennes versions. Exemple :

with Ada.text_io; -- Ada 95package Text_io renames Ada.Text_io;with Ada.text_io;package Integer_io renames Ada.Integer_Text_io;

− Surnommage des littéraux d'énumération par une fonction sans paramètres :

Véronique Gaildrat

Support de cours ADA 95

57

function dix return ChiffreRomain renames 'X';

Résumé :Le surnommage s'applique aux entités (variables, constantes), aux composants d'objets (champs de records, tranche de tableau), aux sous-programmes, aux packages.

En revanche, il ne s'applique pas aux types. Pour les types on utilisera les sous-types.subtype Clef is Integer range 1..MAX;

3.4Compilation séparée

Une grosse application est obtenue à partir du développement simultané d'unité de bibliothèque.Afin de gagner en efficacité il est préférable de pouvoir compiler séparément le plus grand nombres d'unités. Il est également préférable que les unités dépendent les unes des autres le moins possible.

A partir des fichiers contenant le source des programmes, le compilateur génère des U de B.

Ces unités sont : - des spécifications de sous-programmes -- non nécessaires - des corps de sous-programmes, - des spécifications de package - des corps de packages - des instanciations d'unités génériques (vu plus tard)

3.4.1Dépendances

Toute U de B cliente d'un autre U doit le préciser explicitement par la clause With.Une autre dépendance automatique est entre le corps d'un package qui dépend toujours de la spéficication de ce package.

Une U de B doit toujours être compilée avant les unités qui dépendent d'elle :• La spécif avant le corps, • les U de B dont il dépend avant le client.

Il y a deux moyens de réduire les dépendances afin de limiter les recompilations : • Les sous-unités• Les unités enfants de bibliothèque (bibliothèque hiérarchique)

3.4.2Sous-unités

Une sous-unité peut être : - un corps de sous-programme, - un corps de package,mais ne peut être une surcharge d'opérateur.Une sous-unité peut être définie séparément de la spécification.

Exemple :

procedure proc is package pac_p is procedure p1; end pac_p; package body pac_p is separate; procedure p2 (x, y : Positive) is separate;begin...end proc;

with X; with Y;

Véronique Gaildrat

Support de cours ADA 95

58

separate (proc) separate (proc)package body pac_p is procedure p2 (x, y : Positive) is procedure p1 is separate; begin ...end pac_p; end p2;

with Z;separate (proc.pac_p)procedure p1 isbegin … end p1;

L'intérêt de procéder ainsi est de limiter les dépendances qui ne sont utiles qu'aux sous-unités.Une recompilation de Z entraînera la recompilation de p1 mais pas de pac_p.

Attention : • la clause With ne se réfère qu'aux U de B et non aux sous-unités.• une U de B doit être compilée avant les sous-unités.

3.4.3Bibliothèque hiérarchique

Ada permet une conception hiérarchique des U de B dont l'intérêt est de fournir une forme de programmation par extension.

Un package Parent_p peut avoir des sous-packages Parent_p.Enfant1_p et Parent_p.Enfant2_p

Le package Ada possède lui-même de nombreux packages enfants dont les principaux sont Numerics, Characters, Strings, Text_io, Integer_Text_io, …

Toute U de B est enfant du package Standard par défaut.

3.4.3.1Règles de visibilité

Les U enfants peuvent être publiques (un client peut les importer)ou être privées (vues seulement dans le sous-arbre dont son U parente est la racine).

Sans utiliser la clause With ou Use :• La partie publique d'une U enfant publique n'a accès qu'à la partie publique de l'U parente (pour

conserver l'abstraction de données).• La partie privée ainsi que le corps d'une U enfant publique accède à la partie publique et privée de l'U

parente.• Le client dans tous les cas n'a accès qu'à la partie publique des U enfants publiques.• Une U enfant privée a accès à la totalité de la définition de son parent (publique et privée) car elle est

inaccessible aux clients.• La visibilité entre U sœurs publiques est identique à celle de toutes les U de B (clause with et visibilité

de la partie publique).• Une U privée n'est visible que pour les corps de ses U sœurs.

3.4.3.2Exemple

package complexe_p is type Complexe is private; -- 4 opérations : function "+" (x, y : Complexe) return Complexe; function toString (c : Complexe) return String;private type Complexe is record

Véronique Gaildrat

Support de cours ADA 95

59

a, b : Float; end record;end complexe_p;

package body complexe_p is function "+" (x, y : Complexe) return Complexe is begin return (x.a+y.a, x.b+y.b); end "+"; function toString (c : Complexe) return String is begin return "(" & Float'image(c.a) & " , " & Float'image(c.b) & ")"; end toString;end complexe_p;

package complexe_p.cartesien is -- accès à la partie publique function créer (r, i : Float) return Complexe; function partieReelle (c : Complexe) return Float; function partieImaginaire (c : Complexe) return Float;end complexe_p.cartesien;

package body complexe_p.cartesien is -- accès aussi à la partie private function créer (a, b : Float) return Complexe is begin return (a, b); end créer; function partieReelle (c : Complexe) return Float is begin return c.a; end partieReelle; function partieImaginaire (c : Complexe) return Float is begin return c.b; end partieImaginaire;end complexe_p.cartesien;

package Complexe_p.Polaire is function créer (r, theta : Float) return Complexe; function module (c : Complexe) return Float; function argument (c : Complexe) return Float;end Complexe_p.Polaire;

with Ada.Elementary_Functions;package body Complexe_p.Polaire is function créer(r, theta : float) return Complexe is begin return (r * cos(theta), r * sin(theta)); end créer; function module(c : Complexe) return Float is begin return sqrt(c.a*c.a + c.b*c.b); end "abs"; function argument (c : Complexe) return Float is begin return arcos(c.a / sqrt(c.a*c.a + c.b*c.b)); end arg;end Complexe_p.Polaire;

--with complexe_p; -- inutilewith complexe_p.cartesien, Ada.Text_io;

Véronique Gaildrat

Support de cours ADA 95

60

use type complexe_p.Complexe;

procedure complexeEssai is c : complexe_p.Complexe;begin c := complexe_p.cartesien.créer(3.0, 4.0); Ada.Text_io.put_line("c = " & complexe_p.toString(c)); c := c + c; Ada.Text_io.put_line("c = " & complexe_p.toString(c));end complexeEssai;

Les deux unités enfants utilisent le type privé défini dans le package parent.Elles ont accès à toutes les opérations qui y sont définies.

complexeEssai n'aura besoin d'être recompilée qu'en cas de modification de la spécification de complexe_p.cartesien et complexe_p.En cas de modification du corps de complexe_p.cartesien, on aura à le recompiler puis seulement à refaire l'édition de liens pour régénérer l'exécutable.

Schéma de dépendances :

3.4.4Impact méthodologique

Programmation ascendante : (with) On obtient une bibliothèque de packages :

On spécifie d'abord la spécification.Le corps peut être défini ensuite, puis modifié sans que cela affecte les unités clientes de l'interface.

Programmation descendante : (separate) On écrit l'U de B qui contient les sous-unités. On développe les sous-unités qui peuvent elles-mêmes contenir des sous-unités.

Modularité physique : les fichiers sont séparés.

Quand un programme s'exécute, il faut obligatoirement que toutes les U de B dont il dépend aient été élaborées. Cet ordre d'élaboration doit être cohérent avec les dépendances entre unités.

Les pragma Elaborate peut être utilisé pour forcer le compilateur à élaborer une unité avant une autre : with P; pragma Elaborate(P); -- ainsi le corps de P est élaboré avant la spécification de Q package Q is … end Q;

Le pragma Elaborate_All indique que toutes les unités dont l'U de B à compiler dépend doivent être élaborées avant elle (transitivement).

Véronique Gaildrat

Support de cours ADA 95

61

Le pragma Elaborate_Body indique que le corps d'une U doit être élaboré juste après la spécification. package xx is

Une U statique (où aucune expression n'a besoin d'être évaluée dynamiquement) telle que les packages collection de déclarations, peut être élaborée avant même le début de l'exécution : Ceci est obtenu par le pragma Preelaborate.Une U pré-élaborée ne peut dépendre que d'autres U pré-élaborées.

Une U dite "pure" est non seulement pré-élaborée mais en plus elle n'a pas d'état (elle ne contient aucune donnée susceptible de changer d'état au cours de l'exécution.Une U pure ne peut dépendre que d'autres U pures. Exemple : package Ada is pragma Pure(Ada); end Ada;

Dans la bibliothèque prédéfinie de Ada il existe de nombreuses U Preelaborate ou Pure.

Véronique Gaildrat

Support de cours ADA 95

62

4. Exceptions

4.1Déclaration d'une exception

nomException : Exception;

4.1.1Exceptions prédéfinies

Constraint_Error : . Variable hors des bornes de son type . Indice hors de son intervalle . Discriminant non respecté . Division par zéro -- Deprecated : NUMERIC_ERROR : Program_ Error: . Non respect de la structure de contrôle . Appel d'un sous-programme dont le corps n'a pas encore été élaboré . Sortie de fonction sans "return"Storage_ Error: . Plus de mémoire disponible pour faire un "new" . Appel d'un sous-programme récursif qui boucle

Le package IO_Exceptions définit les exceptions nécessaires au packages d'entrée-sortie.

package Ada.IO_Exceptions is pragma Pure(IO_Exceptions); Status_Error : exception; Mode_Error : exception; Name_Error : exception; Use_Error : exception; Device_Error : exception; End_Error : exception; Data_Error : exception; Layout_Error : exception;end Ada.IO_Exceptions;

Deux erreurs survenant lors des entrées-sorties :Device_Error :

. Opération d'entrée-sortie effectuée sur un système déficientData_Error :

. Survient quand le contenu du buffer est inutilisable pour l'opération de lecture (types non compatibles lors d'un get ou valeur hors des bornes du type (et non du sous-type !!) attendu)

Les autres survenant lors de la manipulation de fichiers :Status_error :

. Tenter d'atteindre un fichier non ouvert ou d'ouvrir un fichier déjà ouvert.Mode_error :

. Essai de lecture (resp. d'écriture) dans un fichier ouvert dans un mode non compatibleName_error :

. Tenter d'ouvrir (resp. créer un fichier) dont le nom ne correspondant pas à un fichier existant (resp. déjà existant)

Use_Error :. Tenter d'exécuter sur un fichier une opération non compatible avec les droits d'accès

End_Error : . Tentative de lecture au delà de la fin du fichierLayout_Error :

. propagée par Col, Line ou Page si la valeur retournée excède Count'last

4.2Récupération et traitement d'une exception

Véronique Gaildrat

Support de cours ADA 95

63

Une exception peut être récupéré et traitée dans une partie traite-exception qui peut se trouver en fin de bloc, que ce soit un corps de sous-programme, un corps de package ou un bloc d'instruction. begin … -- instructions exception when Constraint_Error => … -- traitement de l'exception when Ada.IO_Exceptions.Data_Error => … -- traitement de l'exception when others => .. -- récupération de toutes les exceptions non citée précédemment end [nom de l'unité];

• Après l'exécution de la partie traite-exception, le contrôle ne peut être rendu au bloc où l'exception a été levée.

• Quand une exception est levée dans une fonction, il faut placer une instruction return si on veut fournir un résultat même dégradé.

• Un traite-exception en fin de corps de package ne peut s'appliquer que lors de l'initialisation du package et non lors de l'exécution des sous-programmes.

• Si une exception est levée dans un traite-exception, la nouvelle est passée à l'U appelante (le traite-exception ne boucle pas).

• Si une exception est levée lors de l'élaboration, il est possible que l'exécution ne démarre même pas.• Les exceptions ne peuvent pas être surchargées => en cas de conflit, utiliser la notation pointée.

L'exécution du bloc courant se termine à l'endroit où l'exception (le cas échéant) a été levée. Si ce bloc n'a pas de traite-exception, celle-ci est propagée dynamiquement à l'U appelante. Et ainsi de suite jusqu'à ce qu'un traite-exception qui traite ce type d'exception soit rencontré, ou que l'exécution se termine avec affichage d'un message à l'écran.

4.3Instruction raise

L'instruction raise permet le traitement des erreurs survenant en cours d'exécution, ainsi que la gestion des cas exceptionnels.

Il y a deux principales manières de gérer les erreurs d'exécution. Deux exemples vont permettre de situer ces deux cas.

Considérons les procédures de gestion de pile : empiler et dépiler. Ces procédures ont été déclarées dans un package (voir §3.2.6). Lors de leur utilisation, des erreurs peuvent advenir (par exemple, vouloir empiler lorsque la pile est pleine). Ces erreurs peuvent être récupérées et être traitées afin de permettre la poursuite du déroulement du programme.

Premier cas : declare p : Pile_p.Pile; begin ... Pile_p.empiler (p, valeur); ... Pile_p.depiler(p); ... exception when Constraint_Error => -- manipulation de pile -- incorrecte ou autre erreur ? ...; end;

Dans ce cas, lorsqu'une erreur survient de type Constraint_Error, une procédure de récupération d'erreur est engagée, mais on ignore quel est l'erreur qui est survenue.

Deuxième cas :

Véronique Gaildrat

Support de cours ADA 95

64

Le package pile peut être réécrit de façon à permettre une meilleure gestion des erreurs.

package pile_p is -- spécification du package Pile_Pleine, Pile_Vide : exception; type Pile is private; procedure empiler (p : in out Pile; x : Integer); procedure depiler(p : in out Pile); private max : constant := 100; type Tab is array (Integer range <>) of Integer; type Pile is record s : Tab (1..max); top : Integer range 0 .. max := 0; end record; end pile_p;

package body pile_p is -- corps du package procedure empiler (p : in out Pile; x : Integer) is begin if p.top = max then raise Pile_Pleine; end if; p.top := p.top + 1; p.s (p.top) := x; end empiler; procedure depiler(p : in out Pile) is begin if p.top = 0 then raise pile_Vide; end if; p.top := p.top - 1; end depiler; end pile_p;

Une exception est déclarée de la même manière qu'une variable et déclenchée par une instruction raise.

Nous pouvons maintenant écrire : declare p : Pile_p.Pile; begin ... Pile_p.empiler (p, valeur); ... Pile_p.depiler(p); ... exception when Pile_Pleine => … -- manipulation de pile incorrecte when Pile_Vide => … when others => -- autre type d'erreur ...; end;

Autres exemples d'utilisation :

1. Lecture protégée d'un entier :

with Ada.Text_io, Ada.Integer_Text_io, Ada.IO_Exceptions;use Ada.Text_io, Ada.Integer_Text_io;

Véronique Gaildrat

Support de cours ADA 95

65

with Ada.Text_io, Ada.Integer_Text_io, Ada.IO_Exceptions;use Ada.Text_io, Ada.Integer_Text_io;

procedure testeExceptions is type Jour is new Integer range 1..31; j : Jour; package Jour_io is new Integer_io(Jour); subtype Mois is Integer range 1..12; m : Mois;begin loop begin put("j : "); Jour_io.get(j); put("mois : "); get(m); exit; -- quand tout est ok exception when Ada.IO_Exceptions.data_error => put_line("data_error"); skip_line; when Constraint_Error => put_line("Constraint_error"); flush(Ada.Text_io.current_output); when others => put_line("others !!"); end; end loop; put_line("saisie ok : "); Jour_io.put(j); put(m);end testeExceptions;

2. Propagation de la même exception :Un traite-exception qui souhaite propager la même exception que celle analysée après la clause when n'a qu'à exécuter l'instruction raise sans nom d'exception. when others => … -- un certain traitement raise; -- on ne connaît pas l'exception mais on la propage end;

4.4Occurrences d'exceptions

4.4.1Récupération

Le package Ada.Exceptions permet d'analyser plus finement la cause de l'exception.• type Exception_Occurrence • exception_name retourne le nom de l'exception (notation pointée complète et en majuscule)• exception_message retourne un message d'une seule ligne• exception_information retourne une information plus complète permettant de faire la trace de

l'erreur

Pour obtenir l'accès à ces informations, il faut paramétrer le choix dans le traite-exception : when event : others => -- event n'existe que dans le traite-exception put("l'exception inconnue est : "); put_line(exception_name(event)); put(exception_message(event)); reraise_occurrence(event); -- propage la même exception (= raise;) end;

4.4.2Levée d'une occurrence d'exception

Véronique Gaildrat

Support de cours ADA 95

66

L'appel de la méthode raise_exception permet de lever une Exception tout en lui associant des informations. L'attribut 'identity qui est utilisé ici permet d'obtenir un Exception_Id à partir d'un Exception_Occurrence : declare problème : Exception; begin … raise_exception (problème'identity, "première cause du pb"); … raise_exception (problème'identity, "deuxième cause du pb"); … exception when event : problème => if exception_message(event) = "…" then … end if; end;

4.5Package Ada.Exceptions

package Ada.Exceptions is type Exception_Id is private; Null_Id : constant Exception_Id; function Exception_Name(Id : Exception_Id) return String; type Exception_Occurrence is limited private; type Exception_Occurrence_Access is access all Exception_Occurrence; Null_Occurrence : constant Exception_Occurrence; procedure Raise_Exception(E : in Exception_Id; Message : in String := ""); function Exception_Message(X : Exception_Occurrence) return String; procedure Reraise_Occurrence(X : in Exception_Occurrence); function Exception_Identity(X : Exception_Occurrence) return Exception_Id; function Exception_Name(X : Exception_Occurrence) return String; -- Same as Exception_Name(Exception_Identity(X)). function Exception_Information(X : Exception_Occurrence) return String; procedure Save_Occurrence(Target : out Exception_Occurrence; Source : in Exception_Occurrence); function Save_Occurrence(Source : Exception_Occurrence) return Exception_Occurrence_Access; private ... -- not specified by the language end Ada.Exceptions;

Véronique Gaildrat

Support de cours ADA 95

67

5. La généricitéLe concept de généricité permet de généraliser : - les packages, - les sous-programmes.

Exemple de sous-programme générique : procedure echange1 (a, b :in out Integer) is temp : Integer; begin temp := a; a := b; b := temp; end echange1;

procedure echange2 (a, b :in out Mot) is temp : Mot; begin temp := a; a := b; b := temp; end echange2;

procedure echange3 (a, b :in out Matrice) is temp : Matrice; begin temp := a; a := b; b := temp; end echange3;

procedure echange4 (a, b :in out Float) is temp : Float; begin temp := a; a := b; b := temp; end echange4;

Il vaut mieux écrire une procédure d'échange générique. Ici, le paramètre de généricité est le type des données à échanger :

generic type T is private; procedure echange_gen (a, b : in out T) is temp : T; begin temp := a; a := b; b := temp; end echange;

Instanciation avec le type désiré :

procedure echange is new echange_gen (T => Integer);ou procedure echange is new echange_gen (Mot);ou procedure echange is new echange_gen (Matrice);ou procedure echange is new echange_gen (Float);

Exemple de package générique :

generic max : Positive; type Element is private; package pile_gen_p is type Pile is private; Pile_Pleine, Pile_Vide : Exception; procedure empiler (p : in out Pile; x : Element); procedure depiler(p : in out Pile);

Véronique Gaildrat

Support de cours ADA 95

68

function sommet(p : Pile) return Element; private type Tableau is array (1..max) of Element; type Pile is record s : Tableau; top : Natural range 0..max := 0; end record; end pile_gen_p;

with Ada.Text_io, Ada.Integer_Text_io, pile_gen_p, Ada.Exceptions;use Ada.Text_io, Ada.Integer_Text_io, Ada.Exceptions;

procedure usePileGen is m : Positive;begin -- instanciation de pile_gen_p put_line("-----------------> instanciation :"); loop begin put("m = "); get(m); exit; exception when Data_Error => put_line("erreur de saisie"); skip_line; when Constraint_Error => put_line("erreur de saisie"); end; end loop; declare package pileChar is new pile_gen_p(m, Character); c : Character; p : pileChar.Pile; begin put("entrer un mot termine par un '.' : "); get(c); while c /= '.' loop pileChar.empiler(p, c); get(c); end loop; put("miroir : "); loop put(pileChar.sommet(p)); pileChar.depiler(p); end loop; exception when pileChar.Pile_Vide => new_line(2); put_line("arret de l'execution"); when pileChar.Pile_pleine => new_line; put_line("pile pleine"); when event : others => new_line(2); put("l'exception inconnue est : "); put_line(exception_name(event)); end;end usePileGen;

5.1Syntaxe

-- Définition generic -- Liste des paramètres de généricité variable_générique : ...;

Véronique Gaildrat

Support de cours ADA 95

69

type type_générique is ...; with ss_prog_générique ( ...);

-- Définition de l'unité générique package p is -- unité = package ... end p;ou procedure p (...) is -- unité = procédure ... end p;ou function f (...) is -- unité = fonction ... end f;

-- Instanciation <unité> nom_instance is new nom_générique (paramètres);

5.2Paramètre générique "variable"

La notation in out est identique à celle utilisée pour les sous-programmes mais la signification n'est pas la même : pas de passage de valeur par copie et pas de mode out.

L'utilisation des paramètres à l'intérieur de l'unité générique est assez libre sauf aux endroits où`est nécessaire une évaluation statique (case sans clause others).

5.2.1Passage noté : in

Exemple : generic max : [in] Positive; -- in est le mode par défaut type Element is private; package pile_gen_p is

Le paramètre max se comporte comme une constante. Sa valeur provient du paramètre réel donné lors de l'instanciation.Une valeur par défaut est possible : max : Positive := 10;Dans ce cas la valeur par défaut est prise si aucun paramètre réel n'est donné à l'instanciation.

package petitePileChar is new pile_gen_p(5, Character); package PileChar is new pile_gen_p(Element => Character); package grandePileChar is new pile_gen_p(50, Character);

Attention : Le type de d'un paramètre générique constant ne peut être d'un type limité car son initialisation utilise l'affectation.

5.2.2passage noté : in out

Dans ce cas, le paramètre réel doit oblogatoirement être une variable mais ne peut pas être un composant d'un article non contraint à discriminant si l'existance de ce composant dépend du discriminant.

5.3Paramètre générique "TYPE"

La manière dont est déclaré le paramètre générique "type" implique des contraintes sur l'utilisation de ce type dans l'U générique : type T is private;suppose que l'affectation et l'égalité sont définies pour T (se rapproche du comportement d'un type privé pour un client), et qu'il ne peut s'agir d'un type non contraint comme String.

Exemples :

Véronique Gaildrat

Support de cours ADA 95

70

1. Echange cyclique de trois valeurs, utilisant (instanciant) la procédure générique echange_gen. generic type Thing is private; procedure cab (a, b, c : in out Thing); procedure cab (a, b, c : in out Thing) is procedure swap is new echange_gen (x => Thing); begin swap (a, b); swap (a, c); end cab;

2. Obtention du suivant Modulo. generic type T is (<>); -- type discret function next (x : T) return T; function next (x : T ) return T is begin if x = T'last t hen return T'first; ELSE return T'succ (x); end if; end next;

On peut écrire : type Jour is (lun, mar, mer, jeu, ven, sam, dim); function demain is new next (Jour);

subtype Chiffres is Integer range 0 .. 9; function chiffre_suivant is new next (Chiffre);

3. Dictionnaire d'entités génériques. generic type Entité is private; type Valeur is private; package dictionnaire is procedure val_par_défaut (val : in Valeur); function valeur_de (obj : Entité) return Valeur; procedure change_valeur (obj : in Entité; val : in Valeur); procedure faire_l_état; end dictionnaire;

with dictionnaire; with mots; package dico is new dictionnaire (mots.Mot, Positive);

with dictionnaire; with mots; package larousse is new dictionnaire (mots.Mot, String(300));

5.4Quelques types formels génériques

type Discret is (<>); -- type énuméré ou entier type Entier is range <>; -- type entier quelconque type ModuloT is mod <>; -- type modulo quelconque type Angle is delta <>; -- type point fixe quelconque type Masse is digits <>; -- type flottant quelconque type Monnaie is delat <> digits <>; -- type décimal quelconque type Item is private; -- type quelconque (contraint) type Item(<>) is private; -- type quelconque (à discriminant inconnu) comme String

Véronique Gaildrat

Support de cours ADA 95

71

type Item(x : Tu; y : Ty; …) is private; -- type quelconque (à discriminant connu) type Item is limited private; -- type totalement quelconque

Un paramètre de généricité peut dépendre de paramètres précédents : type Table is array (Discret) of Item; -- tableau d'éléments quelconques et de taille quelconque type Lien is access Item; -- pointeur sur un type quelconque type Lien is access constant Item; -- accès généralisé en lecture seule à un type type Lien is access all Item; -- accès généralisé à un type type Lien is access procedure x(paramètres); -- accès à une procédure de profil identique type Lien is access function x(param) return Item; -- accès à une fonction de profil identique

Exemples : generic type Indice is (<>); type Reel is digits <>; type Tab is array (Indice range <>) of Reel; function somme (a : Tab) return reel; --spécification function somme (a : Tab) return reel is -- corps … end somme;

type Vecteur is array (Integer range <>) of Float; function sommeVecteur is new somme(Integer, Float, Vecteur);

5.5Exemple : Ada.Text_io

package Ada.Text_io is -- gestion de fichier (open, close, new_line, etc ...); -- entrée-sortie de caractères et de chaînes (put, get, put_line, get_line)

generic type Enum is (<>);package Enumeration_IO is

-- toutes les méthodes d'entrées sorties sur des types énumérésend Enumeration_IO;

end Ada.Text_io;

Lorsqu'on désire effectuer des entrées-sorties sur des chaînes de caractères, il suffit de faire : with Ada.Text_io;pour accéder aux fonctions d'entrée-sortie sur les caractères et chaînes.

Pour les entiers, Ada.Text_io.Integer_io est délà instancié : with Ada.Text_io; package Ada.Integer_Text_io is new Ada.Text_io.Integer_io (Integer);puis utiliser ce nouveau package : with Ada.Integer_Text_io; ... Ada.Integer_Text_io.put (32); ...De la même manière : -- pour les flottants with Ada.Text_io; package Ada.Float_Text_io is new Ada.Text_io.float_io (Float);Par contre, pour les booléens et tous les type énumérés, il faut instancier le package générique correspondant : with Ada.Text_io; package boolean_io is new Ada.Text_io.Enumeration_io (Boolean); with Ada.Text_io; package jour_io is new Ada.Text_io.Enumeration_io (Jour);

Véronique Gaildrat

Support de cours ADA 95

72

5.6Paramètres génériques "sous-programmes"

-- Spécificationgeneric with function f (x : Float); function intégrale_de (a, b : Float; n : Natural) return Float;

-- Corpsfunction intégrale_de (a, b : Float; n : Natural) return Float is ... ...begin ... for i in 1 .. n loop somme := somme + f(a + (b-a) * i/n) * (b-a)/n; end loop; ...end intégrale_de;

-- Instanciationfunction somme_sin is new intégrale_de (sin);

-- Utilisationx := somme_sin (0.0, 2 * pi, 100);

5.7Paramètre générique "paquetage"

Un paquetage peut être passé comme paramètre de généricité, ce qui permet de créer une hiérarchie cohérente de paquetages.Il existe deux formes possibles : with package p_p is new q_p(<>);qui indique que p_p doit être obtenu par instanciation de q_p, q_p étant lui-même générique.

Les paramètres effectifs peuvent être indiqués explicitement : with package p_p is new q_p(p1, p2, …);

Exemple : generic type Indice is (<>); with package pile_p is new pile_gen_p(<>); package vecteur_piles_p is use pile_p; type Vecteur is private; … private type Vecteur is array(Indice range <>) of Pile; end vecteur_pile_p;

5.8Une application des unités génériques

L'exemple suivant effectue le tri de tableaux de n'importe quel type d'indice contraint et de n'importe quel type de composants.

generic type elem is private; type index is (<>); type elements is array (index) of elem; with function "<" (x,y : in elem) return Boolean;

Véronique Gaildrat

Support de cours ADA 95

73

procedure trier (les_elements : in out elements);

procedure trier (les_elements : in out elements) is temp : elem; echange : Boolean;

begin for ind in index'succ(les_elements'first) .. les_elements'last loop echange := false; for i in reverse ind .. les_elements'last loop if les_elements (i) < les_elements (index'pred(i)) then echange := true; temp := les_elements (index'pred(i)); les_elements (index'pred(i)) := les_elements (i); les_elements (i) := temp; end if; end loop; exit when not echange; end loop;end trier;

Le type elem étant un type privé, les opérations d'affectation et le test d' (in)égalité sont disponibles.L'utilisation des attributs permet de référencer des bornes d'un tableau dont le type réel est inconnu.Le sous-programme générique formel "<" étant importé par le générique, est directement visible dans le corps.

Instanciation :procedure tri is subtype indice is Integer range 1 .. 20; subtype sous_chaine is String (1 .. 20);

procedure trier_chaines is new trier ( elem => Character, index => indice, elements => sous_chaine, "<" => "<"); chaîne : String (1..12) := ("il fait beau");begin trier_chaine (chaîne); Ada.Text_io.put (chaîne);end tri;

Application des paquetage en paramètre de généricité : Regroupement de paramètres de généricité generic type Indice is (<>); type Element is private; type Vecteur is (Indice range <>) of Element; package vecteurGeneral_p is end; -- rien entre is et end !!

generic with package p_p is vecteurGeneral(<>);

with function "<" ( a, b : Element) return Boolean; procedure tri (v : in out p_p.Vecteur);

procedure tri (v : in out p_p.Vecteur) is; -- algorithme de tri choisi end tri;

instanciation : package vectFloat is new vecteurGeneral_p(Integer, Float, Vecteur); procedure triVecteur is new tri(vectFloat, "<"); -- "<" prédéfini

Véronique Gaildrat

Support de cours ADA 95

74

5.9Unités de Bibliothèque hiérarchiques et génériques

Si une U parente est générique alors toutes les U enfants le sont.L'inverse n'est pas obligatoire. Un parent générique peut avoir des U enfants génériques ou non.

Exemple : generic type T is private; package parent_p is … end parent_p;

generic package parent_p.enfant_p is … end parent_p.enfant_p;

L'instanciation peut s'effectuer de différentes manières :A l'intérieur d'une autre U de B les deux instances sont indépendantes : with parent_p.enfant_p; package test_p is package newParent_p is new parent_p(T => TypeT); package newEnfant_p is new newParent_p.enfant_p; … end test_p;

Au niveau bibliothèque les deux instances sont indépendantes : with parent_p; package newParent_p is new parent_p(T => TypeT);

with parent_p.enfant_p; with newParent; package newEnfant_p is new newParent_p.enfant_p; Au niveau bibliothèque en gardant la dépendance : with parent_p; package newParent_p is new parent_p(T => TypeT);

with parent_p.enfant_p; package newParent_p.newEnfant_p is new newParent_p.enfant_p;

On constate donc qu'au niveau de la hiérarchie, il faut instancier U par U.Les packages enfants peuvent avoir leurs propres paramètres de généricité qui seront donnés à leur tour.

Véronique Gaildrat

Support de cours ADA 95

75

6. Annexe : Bibliographie • Manuel de référence du langage de programmation ADA 95 : International Standards

Organization. Reference Manual for the Ada Programming Language. isO/8652-1995.

• Programmer en ADA 95, J.BARNES, 2eme édition, Editions Vuibert, ISBN: 2-7117-8651-X, Version revue, augmentée (fournit un CD-Rom comprenant le compilateur ObjectAda d'Aonix, ainsi que le Reference Manual Ada en hypertext.

• Programmation séquentielle avec Ada 95, Pierre Breguet et Luigi Zaffalon, Editeur: Presses polytechniques et universitaires romandes, ISBN : 2-88074-404-0

• Ada 95 Rationale : http://www.informatik.uni-stuttgart.de/ifi/ps/ada-doc/rat95/

• Lovelace Ada 95 Tutorial : http://www.adahome.com/Tutorials/Lovelace/lovelace.htm

• Ada 95 Reference Manual : http://www.adahome.com/rm95/

• Pour tout trouver sur Ada : http://www.adahome.com/

• Compilateur GNAT : http://www.gnat.com/ ftp://ftp.lip6.fr/pub/gnat/3.13p/gnat-3.13p-i686-pc-linux-gnu-bin.tar.gzPour linuxftp://ftp.lip6.fr/pub/gnat/3.13p/winnt/ Pour windows

Ce compilateur est identique à celui utilisé en TP. Il existe sur de multiples plates-formes.• Compilateur Object Ada d'Aonix :

http://www.aonix.com/content/index.html/ Très pratique à utiliser car il fournit un environnement de programmation avec petit éditeur et interface très conviviale. A priori se comporte comme le GNAT.

Véronique Gaildrat

Support de cours ADA 95

76

7. Annexe : L'environnement de programmation

7.1Programme source

Ada est un langage de programmation algorithmique qui possède une certaine similarité avec Pascal.Le code écrit par un programmeur est constitué d’unités de bibliothèque (U de B) qui doivent être placés dans des fichiers sources. Ces fichiers ont un suffixe obligatoire qui est :Pour la partie spécification --> nomFichier.adsPour la partie corps (body) --> nomFichier.adb

Le point d’entrés d’un programme Ada est la procédure principale par laquelle doit commencer l’exécution. Cette procédure peut porter un nom quelconque (contrairement au C où elle doit s’appeler main) mais ne doit pas avoir d’arguments (paramètres).

Exemple :with Ada.Text_io; -- la clause with permet d'utiliser toute autre U de C

procedure hello isbegin Ada.Text_io.put_line ("hello word");end hello;

Ce programme sera stocké dans un fichier hello.adb pour être ensuite compilé.

7.2Compilation et édition de liens

Nous utiliserons le compilateur GNAT Ada 95 Compiler.

7.2.1Unité de Bibliothèque et programme objet

Une Unité de Bibliothèque peut être :• un sous-programme (au sens Pascal),• la spécification d’un paquetage (équivalant d’un module C : nomFichier.h) : contenant la déclaration

d’un type et de la signature des sous programmes exportés.• le corps d’un paquetage (équivalant en C du : nomFichier.c) : contenant le corps des sous programmes

exportés et des sous programmes internes.

Le résultat d'une compilation est un fichier appelé programme objet : nomFichier.oet un fichier texte contenant les informations de dépendance : nomFichier.ali(ali : Ada Library Information)

Compilation : > gcc –c hello.adb

L'option de compilation "-c" indique au compilateur d'exécuter uniquement la compilation. Cette option est obligatoire pour compiler un fichier car gcc ne sait pas enchaîner directement les étapes en Ada.La compilation en généré un fichier hello.o qui contient la traduction binaire du fichier hello.adb ainsi qu'un fichier hello.ali qui contient des informations supplémentaires vérifiant la validité du programme compilé.Pour pouvoir obtenir un fichier exécutable, l'étape suivante sera d'exécuter :

> gnatbind hello[.ali]puis

> gnatlink hello[.ali]

Mais il existe une manière plus simple et directe d'obtenir le même résultat :> gnatmake hello.adb

Le résultat est un programme exécutable appelé "hello" qui peut être exécuté par :> hello (ou ./hello si le path ne contient pas le '.')

L'exécution provoque l'affichage de la chaîne de caractères : "hello word" à l'écran !

Véronique Gaildrat

Support de cours ADA 95

77

7.2.2Compilation dans le cas où le programme est composé de plusieurs unités

Considérons un programme plus complexe constitué d'un fichier contenant la procédure principale (que nous appellerons programme principal) et de deux autres fichiers contenant la spécification et le corps d'un paquetage.

fichier : greetings_p.adspackage greetings_p is procedure hello; procedure goodbye;end greetings_p;

fichier : greetings_p.adbwith Ada.Text_io;package body greetings_p is procedure hello is begin Ada.Text_io.put_line("hello word"); end hello; procedure goodbye is begin Ada.Text_io.put_line("goodbye word"); end greetings_p;

end greetings_p;

fichier : souhaits.adb with greetings_p; procedure souhaits is begin greetings_p.hello; greetings_p.goodbye; end souhaits;

Pour construire un fichier exécutable à partir de ce programme :> gcc –c souhaits.adb> gcc –c greetings_p.adb> gnatbind souhaits> gnatlink souhaits

Remarque : 1. Il n'y a pas d'ordre particulier requis pour la compilation des U de C2. Il n'est pas nécessaire de compiler la spécification dans le cas où il existe un fichier séparé contenant

le corps du paquetage. Seul le .adb a besoin d'être compilé.3. Pour tester la sémantique d'un fichier de spécif il est possible de lancer :

> gcc –c greetings_p.ads –gnatc

En pratique il sera plus direct de compiler l'ensemble des modules par :> gnatmake souhaits.adb

7.3Organisation des fichiers

La commande > gnatmake toto.adb, bien qu'ayant l'avantage d'effectuer une compilation complète peut s'avérer insuffisante quand on veut organiser ses fichiers dans différents répertoires.En effet, après utilisation du gnatmake, tous les fichiers, spécification, bodies, binaires, exécutables se retrouveront dans le même répertoire. Ceci peut être source d'un certain fouillis!

Deux solutions sont possibles :1. utiliser un fichier Makefile dans lequel on donnera les différents commandes à effectuer pour que

l'organisation des répertoires soit reconnue et maintenue.2. utiliser les variables d'environnement.

Véronique Gaildrat

Support de cours ADA 95

78

7.3.1Répertoires

Une manière classique d'organiser ses fichiers est de créer les répertoires suivants :

src : tous les .adb (bodies)inc : tous les .ads (spécifications) bin : tous les .o et les .ali (binaires générés à la compilation)exe : tous les exécutables

7.3.2 Utilisation d'un Makefile

Un fichier Makefile (ici simplifié) va regrouper les commandes que l'on souhaite enchaîner à la compilation pour maintenir l'organisation des fichiers. Il doit se trouver dans le répertoire src et porter le nom de Makefile (avec le 'M' majuscule !) :

MAKEFILE bdir=../binedir=../exerdir=../incddir=../doc

# '#' est un commentaire# TARGET = nom du fichier a compiler (essai est un exemple)TARGET= testerRomains

$(TARGET) : rm -f $(bdir)/$(TARGET).ali gnatmake -aI../inc -aO../bin -aL../bin $(TARGET) mv *.o $(bdir) mv *.ali $(bdir) mv $(TARGET) $(edir)

clean: rm -f $(edir)/* rm -f $(bdir)/*

Le Makefile va lancer la commande de compilation : gnatmake avec en paramètre le fichier dont le nom a été donné comme cible (TARGET). Le gnatmake suivra toutes les dépendances et ira chercher les fichiers dont il a besoin (spécifications et binaires dans les répertoires donnés par –aI –aO et –aL).Une fois la compilation terminée, tous les fichiers générés se trouvent dans src. Il faut donc effectuer les mv pour les mettre là où il doivent être.Pour changer de fichier à compiler, il suffit de changer le nom de fichier associé à TARGET dans le Makefile.Pour exécuter le Makefile, taper en ligne de commande : > make <rc>

Véronique Gaildrat

Support de cours ADA 95

79

En cas de dépendances qui ne s'effectuent pas correctement (gnatmake n'arrive pas à recompiler les corps de packages dont l'application est cliente par exemple), il peut être nécessaire de recompiler tous les fichiers. Pour cela il faut d'abord détruire les binaires générés afin que la recompilation soit lancée. Pour détruire les binaires taper en ligne de commande : > make clean <rc>Puis recompiler avec > make <rc>

7.3.3Utilisation des variables d'environnement

gnatmake utilise des variables d'environnement pour savoir où aller chercher les fichiers binaires et les fichiers de spécification.Pour qu'il prenne en compte les répertoires bin et inc, il faut placer ces deux lignes dans le fichier .cshrc et ensuite exécuter la commande : > source .cshrcpour que la modification soit prise en compte.

setenv ADA_INCLUDE_PATH ${ADA_INCLUDE_PATH}:/marine4/linfg/linfgxx/Ada95/incsetenv ADA_OBJECTS_PATH ${ADA_OBJECTS_PATH}:/marine4/linfg/gaildrat/Ada95/bin

Une fois les variables d'environnement positionnées il suffit de faire : > gnatmake essai.adbpour compiler essai et pile_p.Mais là, tous les fichiers générés resteront dans le répertoire src.Il faudra donc ensuite faire les mv à la main !

Véronique Gaildrat

Support de cours ADA 95

80

Véronique Gaildrat

Support de cours ADA 95