ADA : Cours

Transcription

ADA : Cours
Véronique Gaildrat
_____________________________________________________________
____________________________________________
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]
Support de cours ADA 95
1
Véronique Gaildrat
Table des matières
1.
INTRODUCTION................................................................................................................................ 5
1.1
ORIGINES DE ADA.............................................................................................................................. 5
1.1.1
Historique................................................................................................................................... 5
1.1.2
Objectifs du langage.................................................................................................................. 5
1.2
ELÉMENTS DE BASE D'UN PROGRAMME ADA 95................................................................................ 5
1.2.1
Eléments lexicaux, séparateurs et délimiteurs........................................................................... 5
1.2.2
Littéraux numériques.................................................................................................................. 6
1.2.2.1 Littéraux entiers...................................................................................................................... 6
1.2.2.2 Littéraux réels......................................................................................................................... 6
1.2.3
Mots réservés.............................................................................................................................. 6
1.2.4
Identificateurs............................................................................................................................. 6
1.2.5
Commentaires et Pragmas......................................................................................................... 7
1.2.6
Opérateurs et expressions.......................................................................................................... 7
1.2.7
Evaluation des expressions......................................................................................................... 7
1.2.8
Instructions (sauf return et raise).............................................................................................. 8
1.2.8.1 Instruction d'affectation.......................................................................................................... 8
1.2.8.2 Instruction nulle..................................................................................................................... 8
1.2.8.3 Instruction bloc....................................................................................................................... 8
1.2.8.4 Instructions composées.......................................................................................................... 8
1.2.9
Sous-programmes..................................................................................................................... 10
1.2.10
Instruction return..................................................................................................................... 11
1.2.11
Paquetages................................................................................................................................ 11
1.2.12
Variable et Type d'une variable................................................................................................ 12
1.2.13
Caractères et chaînes littérales................................................................................................ 13
1.2.13.1
Caractères littéraux........................................................................................................... 13
1.2.13.2
Chaînes littérales.............................................................................................................. 13
1.2.14
Paquetages de manipulation des caractères et des chaînes..................................................... 13
1.2.15
Exemple d'un programme simple en ADA................................................................................ 13
2.
LES TYPES ........................................................................................................................................ 14
2.1
NOTION DE TYPE ............................................................................................................................... 14
2.1.1
Définition..................................................................................................................................14
2.1.2
Règles....................................................................................................................................... 14
2.1.3
Classification de types ADA..................................................................................................... 14
2.1.4
Les modèles de types................................................................................................................ 15
2.1.5
Déclaration de types................................................................................................................. 15
2.1.6
Contraintes............................................................................................................................... 15
2.2
SOUS-TYPES ET TYPES DÉRIVÉS......................................................................................................... 16
2.2.1
Sous-types................................................................................................................................. 16
2.2.2
Types dérivés............................................................................................................................ 16
2.3
TYPES SCALAIRES..............................................................................................................................19
2.3.1
Attributs communs à tous les types scalaires........................................................................... 19
2.3.2
Les types discrets...................................................................................................................... 19
2.3.2.1 Attributs spécifiques aux types discrets............................................................................... 19
2.3.2.2 Les types entiers................................................................................................................... 20
2.3.2.3 Les types Modular (dits types modulo)................................................................................ 21
Support de cours ADA 95
2
Véronique Gaildrat
2.3.2.4 Les types énumérés............................................................................................................... 21
2.3.3
Les types réels........................................................................................................................... 23
2.3.4
Les types décimaux................................................................................................................... 24
2.4
TYPES COMPOSÉS PRINCIPAUX (TABLEAUX ET ARTICLES)................................................................. 25
2.4.1
Types tableaux......................................................................................................................... 25
2.4.1.1 Variables tableaux................................................................................................................ 25
2.4.1.2 Types tableaux non contraints.............................................................................................. 25
2.4.1.3 Contraintes d'indices............................................................................................................ 26
2.4.1.4 Agrégats................................................................................................................................ 26
2.4.1.5 Attributs de tableaux............................................................................................................ 26
2.4.1.6 Tableaux non contraints et Passage de paramètres............................................................... 27
2.4.1.7 Types tableaux contraints..................................................................................................... 27
2.4.1.8 Sous-types de tableaux......................................................................................................... 28
2.4.1.9 Opérations sur un type tableau............................................................................................. 28
2.4.1.10
Caractères et Chaînes de Caractères................................................................................. 29
2.4.1.11
Conversions explicites entre types tableaux.................................................................... 30
2.4.2
Types articles............................................................................................................................ 31
2.4.2.1 Exemple................................................................................................................................ 32
2.4.2.2 Ensemble des opérations...................................................................................................... 32
2.4.2.3 Sélection d'un composant : notation pointée........................................................................ 32
2.4.2.4 Agrégats................................................................................................................................ 32
2.4.2.5 Valeur par défaut d'un composant........................................................................................ 32
2.4.2.6 Articles à partie discriminante..............................................................................................32
2.4.2.7 Articles à variantes............................................................................................................... 33
2.4.2.8 Sous-types d'articles............................................................................................................. 33
2.4.2.9 Défauts et contraintes........................................................................................................... 34
2.4.2.10
Types dérivés d'articles à discriminant............................................................................ 35
2.4.3
Composition de types................................................................................................................ 35
2.5
LES TYPES ACCESS............................................................................................................................ 36
2.5.1
Type access spécifiques à un pool..........................................................................................36
2.5.2
Quand utiliser les types access.............................................................................................. 37
2.5.3
Comment créer une structure arborescente............................................................................. 38
2.5.4
Utilisation "historique" de l'arborescence............................................................................... 38
2.5.5
Contraintes sur les types access............................................................................................ 38
2.5.6
Types access généralisés...................................................................................................... 40
2.5.7
Paramètres access de sous-programmes................................................................................ 41
2.5.8
Accès à un sous-programme.................................................................................................... 41
3.
UNITÉS DE BIBLIOTHÈQUE........................................................................................................ 43
3.1
SOUS-PROGRAMMES..........................................................................................................................43
3.1.1
Modes de passage de paramètres............................................................................................. 43
3.1.2
Paramètres nommés et paramètres par défauts....................................................................... 44
3.1.3
Sous-programmes de type procédure....................................................................................... 45
3.1.4
Sous-programmes de type fonction.......................................................................................... 45
3.1.5
Notion de surcharge................................................................................................................. 46
3.1.6
Applications des sous-programmes ADA................................................................................. 47
3.1.6.1 Programmes principaux....................................................................................................... 47
3.1.6.2 Contrôle fonctionnel............................................................................................................. 47
Support de cours ADA 95
3
Véronique Gaildrat
3.1.6.3 Opération primitives sur des Types de Données Abstraits................................................... 48
3.2
PAQUETAGES (PACKAGE ADA)......................................................................................................... 49
3.2.1
Définition et généralités........................................................................................................... 49
3.2.1.1 Définition............................................................................................................................. 49
3.2.1.2 Spécification et corps de package........................................................................................ 49
3.2.2
Types classiques de packages................................................................................................... 50
3.2.2.1 Collection de déclarations communes.................................................................................. 50
3.2.2.2 Exemple de bibliothèque de sous-programmes.................................................................... 50
3.2.2.3 Exemple de définition de package exportant un nouveau type ( !! /= TDA)....................... 51
3.2.2.4 Exemple de définition de machine abstraite........................................................................ 51
3.2.3
Clients de packages.................................................................................................................. 52
3.2.4
Résumé......................................................................................................................................54
3.2.5
Types privés.............................................................................................................................. 54
3.2.6
Types limités............................................................................................................................. 55
3.2.7
Types limites privés................................................................................................................... 56
3.3
SURNOMMAGE.................................................................................................................................. 57
3.4
COMPILATION SÉPARÉE ..................................................................................................................... 58
3.4.1
Dépendances............................................................................................................................ 58
3.4.2
Sous-unités................................................................................................................................ 59
3.4.3
Bibliothèque hiérarchique........................................................................................................ 59
3.4.3.1 Règles de visibilité............................................................................................................... 59
3.4.3.2 Exemple................................................................................................................................ 60
3.4.4
Impact méthodologique............................................................................................................ 62
4.
EXCEPTIONS.................................................................................................................................... 63
4.1
DÉCLARATION D'UNE EXCEPTION ......................................................................................................63
4.1.1
Exceptions prédéfinies..............................................................................................................63
4.2
RÉCUPÉRATION ET TRAITEMENT D'UNE EXCEPTION .......................................................................... 64
4.3
INSTRUCTION RAISE .......................................................................................................................... 64
4.4
OCCURRENCES D'EXCEPTIONS........................................................................................................... 66
4.4.1
Récupération............................................................................................................................ 66
4.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 ........................................................................................................................................... 69
5.2
PARAMÈTRE GÉNÉRIQUE "VARIABLE"............................................................................................... 70
5.2.1
Passage noté : in...................................................................................................................... 70
5.2.2
passage noté : in out................................................................................................................ 70
5.3
PARAMÈTRE GÉNÉRIQUE "TYPE"..................................................................................................... 70
5.4
QUELQUES TYPES FORMELS GÉNÉRIQUES..........................................................................................71
5.5
EXEMPLE : ADA.TEXT_IO ................................................................................................................. 72
5.6
PARAMÈTRES GÉNÉRIQUES "SOUS-PROGRAMMES"........................................................................... 73
5.7
PARAMÈTRE GÉNÉRIQUE "PAQUETAGE"............................................................................................ 73
5.8
UNE APPLICATION DES UNITÉS GÉNÉRIQUES......................................................................................74
5.9
UNITÉS DE BIBLIOTHÈQUE HIÉRARCHIQUES ET GÉNÉRIQUES............................................................ 75
6.
ANNEXE : BIBLIOGRAPHIE........................................................................................................ 76
Support de cours ADA 95
4
Véronique Gaildrat
7.
ANNEXE : L'ENVIRONNEMENT DE PROGRAMMATION.................................................. 77
7.1
PROGRAMME SOURCE....................................................................................................................... 77
7.2
COMPILATION ET ÉDITION DE LIENS.................................................................................................. 77
7.2.1
Unité de Bibliothèque et programme objet.............................................................................. 77
7.2.2
Compilation dans le cas où le programme est composé de plusieurs unités........................... 78
7.3
ORGANISATION DES FICHIERS............................................................................................................ 78
7.3.1
Répertoires............................................................................................................................... 79
7.3.2
Utilisation d'un Makefile......................................................................................................... 79
7.3.3
Utilisation des variables d'environnement............................................................................... 80
Support de cours ADA 95
5
Véronique Gaildrat
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 (autové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
Caractères autres :
• #
"
_
• a..z
A..Z
• 0..9
(
) * + , - . / :
=> .. ** := /= >=
tabulation
fin de ligne
; < = > |
<= << >> <>
1American National Standard Institute
2International
Standard Organization
Support de cours ADA 95
6
Véronique Gaildrat
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és
Attention à 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
COMPTER_LA_MONNAIE
TABLE_128_A
Ada
UMO164g
=
Centimes
/=
cent_imes
Exemples d'identificateurs incorrects :
fich&chips
"&"
RATE-of-FLOW
"-"
TIME__LAG
"__"
Support de cours ADA 95
7
Véronique Gaildrat
77E2
X_
tax rate
with
"7"___
"_ "
" " => deux identificateurs
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 :
opérateurs de relation :
opérateurs additifs :
opérateurs unaires :
opérateurs multiplicatifs :
Au plus fort :
and
=
not in
+
+
*
**
or
/=
xor
<
/
not
&
mod
abs
>=
>
>=
in
rem
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'affectation
variable := expression;
-- où type de la variable = type de l'expression
1.2.8.2Instruction nulle
Support de cours ADA 95
8
Véronique Gaildrat
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
-- déclaration de variables ou de types locaux
-- au bloc
begin
-- instructions manipulant
-- les variables locales et
-- les variables globales accessibles
[exception
traite exception]
end;
échange:
declare
temp : Float;
begin
temp := v1;
v1 := v2;
v2 := temp;
end échange;
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
Support de cours ADA 95
9
Véronique Gaildrat
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 is
i : Integer;
function carre (i : Integer) return Integer is
begin
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 boucle
Une 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;
Support de cours ADA 95
10
Véronique Gaildrat
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
if table (suivant) = ' ' then
...
end if;
...
end loop;
-- suivant est constant dans la boucle
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éclarations
begin
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éclarations
begin
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).
Support de cours ADA 95
11
Véronique Gaildrat
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 (sousprogrammes) 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 Point2D
function 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) is
begin
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
Support de cours ADA 95
12
Véronique Gaildrat
begin
Client :
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 Point2D
function toString(p : Point2D) return String is
begin
return Float'image(p(p'first)) & " , " & Float'image(p(p'last));
end toString;
end Point2D_p;
with Ada.Text_io, Point2D_p; -- la clause with permet l'import d'autres unités
procedure 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éraux
Il 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érales
De la même manière que deux jeux de caractères sont définis, on trouve deux types de chaînes :
Support de cours ADA 95
13
Véronique Gaildrat
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'arborescence
Ada.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 Ada
procedure 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é ...)
Support de cours ADA 95
14
Véronique Gaildrat
•
•
- 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
scalaires
composite
=>
=>
=>
pointeurs
un élément <--> une valeur
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 :
Support de cours ADA 95
15
Véronique Gaildrat
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
digit
delta
array
...
définit par défaut les entiers.
définit les réels en virgule flottante.
définit les réels en virgule fixe.
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;
fin : Jour range jeudi .. vendredi;
code : Character range 'a' .. 'f';
==>
-- range définit également les contraintes d'intervalle
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
Support de cours ADA 95
16
Véronique Gaildrat
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 := terme;
-- maintenant et terme sont compatibles
-- 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
|
|
type dérivé
A;
|
type parent
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;
Support de cours ADA 95
17
Véronique Gaildrat
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)) = rouge
ou
if B'(f(rouge)) = rouge
ATTENTION :
A(f(...))
A'(f(...))
convertit le résultat dans le type A
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
Support de cours ADA 95
18
Véronique Gaildrat
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;
if j < samedi then ...
if k < samedi then...
mais
k := j;
k := dimanche;
k := JourOuvrable (j);
-- correct
-- illégal
-- erreur d'intervalle
-- 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 :
Support de cours ADA 95
19
Véronique Gaildrat
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,
Les positions respectives des valeurs sont :
0
1
2
3
197
198
La position de la valeur 0 est 100, et la valeur en position 4 est -96.
100
199
200
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 .. fin
dé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 = +32767
Ces 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;
Support de cours ADA 95
20
Véronique Gaildrat
begin
i1, j1, k1 : Int1;
i, j, k : Integer := 3;
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;
Support de cours ADA 95
21
Véronique Gaildrat
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
xor
exemples :
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 ASCII
Ensemble 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;
Support de cours ADA 95
22
Véronique Gaildrat
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
Support de cours ADA 95
23
Véronique Gaildrat
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
1
1.6
devient
2
1.5
devient
2
-1.5
devient
-2
-- la valeur à mi-chemin est arrondie en s'éloignant de 0
2.3.4Les types décimaux
Support de cours ADA 95
24
Véronique Gaildrat
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 Integer
a 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 : Integer
Pour 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 <= vendredi
Le 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 :
Support de cours ADA 95
25
Véronique Gaildrat
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
Support de cours ADA 95
26
Véronique Gaildrat
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);
essai (chaîne);
Ada.Text_io.get_line(chaîne, l);
essai (chaîne(1..l));
...
essai("Dupont");
end;
-- il faudra saisir exactement 20 caractères !
-- s'range
1..20
-- la longueur réellement saisie donnée par l
-- s'range
1..l
-- s'range
1..6
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);
Support de cours ADA 95
27
Véronique Gaildrat
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 pas
s1 : 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
Support de cours ADA 95
28
Véronique Gaildrat
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') = chiffre
ChiffreRomain'pos('M') = 6
chiffre > '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 :
Support de cours ADA 95
29
Véronique Gaildrat
oie : constant String := ('o', 'i', 'e');
-- peut aussi s'écrire
oie : constant String := "oie";
Pour placer le caractère '"' dans un chaîne :
('A', '"', 'B') = "A""B" -- avec doublement du guillemet
Une 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;
Support de cours ADA 95
30
Véronique Gaildrat
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 simple
procedure 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;
begin
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);
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;
...
Support de cours ADA 95
31
Véronique Gaildrat
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);
Support de cours ADA 95
32
Véronique Gaildrat
end record;
buf1 : ligne(132);
buf2 : ligne;
-- listings
-- é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)
Support de cours ADA 95
33
Véronique Gaildrat
Combinaison des formes précédentes :
louis (11).naissance
louis (11).naissance.an
john.adresse (12)
louis (14).adresse (1)
successeur (john).naissance.jour
-- Date
-- Integer range 1..3000
-- Character
-- Character
-- Integer range 1..31
2.4.2.9Défauts et contraintes
lui
jules
julie
autre
begin
--end;
: Homme;
: Personne (genre => m);
: Femme;
: Personne;
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 !
Entité contrainte :
jules : Personne (genre => m);
Entité non contrainte : autre : Personne;
c'est pourquoi autre peut être affecté avec jules ou julie …
-- valeur imposée de discriminant
-- pas de discriminant imposé
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.
Support de cours ADA 95
34
Véronique Gaildrat
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 bien
r : 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 8
La 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.
Support de cours ADA 95
35
Véronique Gaildrat
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 access
Les 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érence
new 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é ?)
Support de cours ADA 95
36
Véronique Gaildrat
if x = y then
if x.all = y.all then
-- vrai si x et y désignent la même entité
-- déférencement : vrai si l'entité désignée par x
-- a la même valeur que l'entité désignée par y
Affectations :
:=
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);
Support de cours ADA 95
37
Véronique Gaildrat
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'allocation
rm := new Matrice'(1..5 => (1..4 => 0.0)); -- matrice contrainte et initialisée à l'allocation  '
rm := new Matrice;
--illégal
Accè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 access
o 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 initialisation
type 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 :
Support de cours ADA 95
38
Véronique Gaildrat
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;
Support de cours ADA 95
39
Véronique Gaildrat
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 aliased
rci := 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 :
Support de cours ADA 95
40
Véronique Gaildrat
•
•
ptr est de type anonyme donc on ne peut pas déclarer d'autres variables à l'intérieur du sousprogramme 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 is
begin
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.
Support de cours ADA 95
41
Véronique Gaildrat
3. Unités de Bibliothèque
Une 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 :
Support de cours ADA 95
42
Véronique Gaildrat
- 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, olive
Pour 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;
Support de cours ADA 95
43
Véronique Gaildrat
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);
Support de cours ADA 95
44
Véronique Gaildrat
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'oncle
L'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))
Support de cours ADA 95
45
Véronique Gaildrat
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
Support de cours ADA 95
46
Véronique Gaildrat
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 sousprogrammes 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.
Support de cours ADA 95
47
Véronique Gaildrat
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
Support de cours ADA 95
48
Véronique Gaildrat
begin
-- partie cachée : corps, implémentation
procedure procLocale is
...
end procLocale;
procedure procExportée is
...
end proc_Exportée;
-- 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;
Support de cours ADA 95
49
Véronique Gaildrat
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;
Support de cours ADA 95
50
Véronique Gaildrat
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 is
when 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;
Support de cours ADA 95
51
Véronique Gaildrat
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 is
begin
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;
Support de cours ADA 95
52
Véronique Gaildrat
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)
Support de cours ADA 95
53
Véronique Gaildrat
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 Point2D
function toString(p : Point2D) return String;
private
type 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 record
numé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 is
begin
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
Essai
begin
for i in t1'range loop
if (t1(i) /= t2(i)) then
return false;
end if;
end loop;
return true;
Support de cours ADA 95
54
Véronique Gaildrat
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 !).
Support de cours ADA 95
55
Véronique Gaildrat
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;
end Toto_p;
-- mêmes paramètres
−
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 95
package 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 :
Support de cours ADA 95
56
Véronique Gaildrat
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
- 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)
-- non nécessaires
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;
Support de cours ADA 95
with Y;
57
Véronique Gaildrat
separate (proc)
package body pac_p is
procedure p1 is separate;
end pac_p;
begin
separate (proc)
procedure p2 (x, y : Positive) is
...
end p2;
with Z;
separate (proc.pac_p)
procedure p1 is
begin
…
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
peut avoir des sous-packages
et
Parent_p
Parent_p.Enfant1_p
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
Support de cours ADA 95
58
Véronique Gaildrat
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;
-- inutile
with complexe_p.cartesien, Ada.Text_io;
Support de cours ADA 95
59
Véronique Gaildrat
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).
Support de cours ADA 95
60
Véronique Gaildrat
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.
Support de cours ADA 95
61
Véronique Gaildrat
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éficient
Data_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 compatible
Name_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 fichier
Layout_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
Support de cours ADA 95
62
Véronique Gaildrat
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 traiteexception 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 =>
end;
...;
-- manipulation de pile
-- incorrecte ou autre erreur ?
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 :
Support de cours ADA 95
63
Véronique Gaildrat
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;
Support de cours ADA 95
64
Véronique Gaildrat
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
Support de cours ADA 95
65
Véronique Gaildrat
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;
Support de cours ADA 95
66
Véronique Gaildrat
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é :
ou
ou
ou
procedure echange is new echange_gen (T => Integer);
procedure echange is new echange_gen (Mot);
procedure echange is new echange_gen (Matrice);
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);
Support de cours ADA 95
67
Véronique Gaildrat
private
function sommet(p : Pile) return Element;
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 : ...;
Support de cours ADA 95
68
Véronique Gaildrat
type type_générique is ...;
with ss_prog_générique ( ...);
ou
ou
-- Définition de l'unité générique
package p is
...
end p;
procedure p (...) is
...
end p;
function f (...) is
...
end f;
-- unité = package
-- unité = procédure
-- unité = fonction
-- 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;
type Element is private;
package pile_gen_p is
-- in est le mode par défaut
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 :
Support de cours ADA 95
69
Véronique Gaildrat
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
type
type
type
type
type
type
type
Discret is (<>);
Entier is range <>;
ModuloT is mod <>;
Angle is delta <>;
Masse is digits <>;
Monnaie is delat <> digits <>;
Item is private;
Item(<>) is private;
Support de cours ADA 95
-- type énuméré ou entier
-- type entier quelconque
-- type modulo quelconque
-- type point fixe quelconque
-- type flottant quelconque
-- type décimal quelconque
-- type quelconque (contraint)
-- type quelconque (à discriminant inconnu) comme String
70
Véronique Gaildrat
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és
end 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);
Support de cours ADA 95
71
Véronique Gaildrat
5.6Paramètres génériques "sous-programmes"
-- Spécification
generic
with function f (x : Float);
function intégrale_de (a, b : Float; n : Natural) return Float;
-- Corps
function 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;
-- Instanciation
function somme_sin is new intégrale_de (sin);
-- Utilisation
x := 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;
Support de cours ADA 95
72
Véronique Gaildrat
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 (
begin
end tri;
elem => Character,
index => indice,
elements => sous_chaine,
"<" => "<");
chaîne : String (1..12) := ("il fait beau");
trier_chaine (chaîne);
Ada.Text_io.put (chaîne);
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
Support de cours ADA 95
73
Véronique Gaildrat
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.
Support de cours ADA 95
74
Véronique Gaildrat
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.gz
Pour linux
ftp://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.
Support de cours ADA 95
75
Véronique Gaildrat
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.ads
Pour 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 is
begin
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.o
et 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 !
Support de cours ADA 95
76
Véronique Gaildrat
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.ads
package greetings_p is
procedure hello;
procedure goodbye;
end greetings_p;
fichier : greetings_p.adb
with 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 C
2. 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.
Support de cours ADA 95
77
Véronique Gaildrat
7.3.1Répertoires
Une manière classique d'organiser ses fichiers est de créer les répertoires suivants :
src : tous les .adb
inc : tous les .ads
bin : tous les .o et les .ali
exe : tous les exécutables
(bodies)
(spécifications)
(binaires générés à la compilation)
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=../bin
edir=../exe
rdir=../inc
ddir=../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>
Support de cours ADA 95
78
Véronique Gaildrat
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 .cshrc
pour que la modification soit prise en compte.
setenv ADA_INCLUDE_PATH ${ADA_INCLUDE_PATH}:/marine4/linfg/linfgxx/Ada95/inc
setenv 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.adb
pour 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 !
Support de cours ADA 95
79
Véronique Gaildrat
Support de cours ADA 95
80