• Aucun résultat trouvé

Apprendre à programmer avec Ada étape par étape

N/A
N/A
Protected

Academic year: 2021

Partager "Apprendre à programmer avec Ada étape par étape"

Copied!
9
0
0

Texte intégral

(1)

Tour d'horizon

des

fonctionnalités clefs du langage ADA

Stéphane MARIEL Avant Propos

Le langage ADA est un langage à part parmi tous les dialectes informatiques. Défini comme la réponse à un appel d'offre du ministère américain à la défense (DoD), norme ISO, il présente une richesse fonctionnelle étonnante (ceci dès sa première version en

1983) tout en mettant en oeuvre des mécanismes stricts garantissant la conformité des programmes à l'exécution.

Aujourd'hui ce langage peu mis en avant est utilisé dans l'industrie aéronautique et de défense dans le monde entier.

Le nom du langage fut choisi en hommage à Lady Ada Byron, comtesse de Lovelace, assistante de Babbage et considérée comme le premier programmeur de l'histoire.

I. Structure d'un programme

La base syntaxique d'ADA est le langage Pascal. Rien d'original en la matière donc. Le langage distingue procédures et fonctions comme il se doit et toute procédure peut être choisie pour sous-programme principal (en C il s'agit de main).

-- mon premier programme ada -- hello world !

with text_io; use text_io; procedure exemple1 is begin

put("hello world!"); end;

Par défaut et contrairement aux langages fondés sur C, les arguments de la ligne de commande ne sont pas associés au sous-programme principal. Il convient pour en disposer de faire appel à une bibliothèque spécifique. Ce fonctionnement est compréhensible pour un langage dont la vocation est le plus souvent de réaliser des systèmes embarqués.

I.1. Sous-programmes

Concernant les sous-programme en général, ADA propose un mécanisme de passage des paramètres indépendant du compilateur. Traditionnellement c'est au contraire au développeur de choisir entre passage par copie ou par référence.

Le langage ADA offre une approche différente en proposant de choisir entre un mode en lecture seul, un mode en écriture seule, ou un mode mixte. La réalité du mode de passage choisi incombant au seul compilateur, probablement mieux à même de déterminer pour une architecture donnée la solution la plus efficace.

-- définition d'une procédure et d'une fonction

--with Ada.text_io; use Ada.text_io; -- ignorer pour l'instant procedure Exemple2 ; is

Package int_io is new Ada.Text_Io.integer_io(Integer); use Int_Io; -- idem

value : integer;

procedure Bar(a: in integer) is i: integer := 1;

begin

i := 4; end Bar;

function Foo(B: in Integer) return integer is I: integer := 3; begin return i; end Foo; begin bar(Value); Value := foo(4);

put("hello world!"); new_line; put(value); new_line;

end;

Il faut noter que les fonctions n'acceptent que des paramètres en lecture seule. Ceci afin de limiter les effets de bord. Enfin, en ADA, toutes les procédures et fonctions sont surchargeables, sous réserve d'avoir des signatures (prototypes) différentes, valeurs par défaut exclues.

with ada.text_io; procedure exemple3 is

package int_io is new ada.text_io.integer_io(integer); procedure foo(i : integer) is

begin

int_io.put(i); end foo;

procedure foo(i : integer; j: integer) is begin int_io.put(i); ada.text_io.put(","); int_io.put(j); ada.text_io.new_line; end; begin foo(3,4); end exemple3;

(2)

I.2. Un peu d'exotisme

Le langage ADA prend parfois à contrepied certaines bonnes vieilles habitudes, en matière de syntaxe comme de contraintes.

Utilisation d'attributs

Il s'agit d'un des éléments probablement parmi les plus perturbants à première vue. ADA définit un ensemble d'attributs, qui appliqués à des données ou des types, vont permettre d'obtenir des informations souvent élémentaires ou intimement liées à la donnée.

L'appel à ces attributs, on parle de qualification, se fait au moyen du caractère « ' » (apostrophe ou quote).

Ainsi sur un type de données on pourra obtenir la borne inférieure, la précision maximale. Pour les objets il sera possible d'obtenir la classe ou le type. Sur un tableau les différents indexes.

En réalité ses attributs sont tous simples, il suffit de les imaginer comme de simples méthodes appelées non pas avec la notation pointée habituelle mais avec la quote.

-- un exemple de type enuméré, avec utilisation de la qualification with Ada.text_io; use Ada.text_io;

procedure exemple4 is

type couleur is ( blanc, bleu, jaune, rouge, vert, orange); package couleur_io is new ada.Text_io.enumeration_io(couleur); use couleur_io; c : couleur := bleu; begin put(c); new_line; put(Couleur'pred(C)); New_line; end exemple4;

Les tableaux : intervalles et agrégats

Contrairement au langage C, un tableau peut être indexé par tout intervalle d'un type discret, cela inclut donc les types énumérés, les entiers, et les types dérivés.

Les bornes de l'intervalle sont librement fixables.

Enfin il est possible d'utiliser la notion d'agrégat (un équivalent à { ... } en C) pour initialiser un tableau en une seule fois. En ADA l'agrégat est donné entre parenthèses, on peut initialiser les valeurs par position, par nom (notamment pour les index de types énumérés), enfin la clause others permet de donner une valeur par défaut à tous les éléments non encore initialisés du tableau.

with ada.text_io; procedure exemple5 is

type couleur is ( bleu, rouge, vert, jaune , blanc ); package int_io is new ada.text_io.integer_io(integer);

t1 : array (rouge..blanc) of integer;

type mytab is array (couleur'range) of integer; t2 : mytab;

procedure put(a : mytab) is begin

ada.text_io.put ("("); for c in a'range loop int_io.put(a(c)); if c /= a'last then ada.text_io.put(", "); end if; end loop; ada.text_io.put(")"); end put; begin

t2 := ( bleu | vert => 5, others => 3 ); put(t2);

end exemple5;

II. Exceptions

ADA complète la syntaxe de base du langage par le support des exceptions. Un support qui pourra paraître simpliste au regard des mécanismes offerts par exemple dans Java. La réalité est qu'un tel niveau est probablement très suffisant.

Le langage propose un type de données : exception. Chacun peut naturellement déclarer de nouvelles exceptions (en plus de celles définies par défaut). L'exception en ADA s'assimile à un signal nommé, elle ne porte pas d'information additionnelle.

Lorsque qu'une exception est déclenchée (on dit levée) le signal se propage en remontant la pile des blocs d'instructions puis des sous-programmes, ceci tant qu'aucun bloc de traitement n'est rencontré. Au pire, si même le sous-programme principal ne comporte aucun bloc pour traiter l'exception, l'exécution du programme est stoppée.

Le bloc de traitement des exceptions peut être vu comme le switch du langage C. Chaque exception peut recevoir un traitement dédié, ou un traitement global peut être exécuté pour un ensemble d'exceptions.

L'instruction raise permet de lever une exception explicitement. Les exceptions définies dans le langage sont elles le plus souvent levées par le runtime.

A noter que les exceptions sont présentes dans le langage depuis 1983. with text_io; use text_io;

procedure Exemple6 is

Monexception : exception;

procedure Foo(I : in Integer := 0) is begin if I < 0 then raise Constraint_Error; elsif I > 10 then raise Monexception; sous

programme programmesous bloc bloc

Exé cutio

n no rmal

(3)

end if; end Foo; begin begin Foo(-4); exception when Constraint_Error =>

put("Ok, il n'y a pas mort d'homme."); new_line; raise Monexception;

end; exception

when others =>

Put("Rah! Mieux vaut en finir"); new_line; end Exemple6;

III. Modèle de composants

Au delà des améliorations apportées au langage Pascal, ADA introduit une approche modulaire du développement en permettant la compilation séparée de portions du code. Ces différents unités de compilation vont permettre de mettre en oeuvre des approches dites top-down et bottom-up.

Parmi les unités de compilation, ADA introduit la notion de Packages. Ces packages vont permettre la définition de bibliothèques (approche bottom-up).

III.1. Packages

Les packages ADA sont donc des librairies réutilisables. Chacun dispose d'une spécification et d'une ou plusieurs implantations.

Cette séparation va permettre de tirer profit des différents modes de développement (top-down ou bottom-up). En effet, spécification comme implantation sont des unités de compilation et sont compilables séparément. On pourra donc compiler un programme principal avant ses composants, tester des composants en priorité ou encore développer des implantations de test.

L'instruction package est utilisée seule pour définir les spécifications d'une librairie. Pour l'implantation on utilisera le couple : package body.

package ComplexPkg is type Complex is record

X,Y : Float := 0.0; end record;

procedure Set ( Z: out complex; A, B : in Float := 0.0); function Getr (Z : in Complex) return float;

function Geti (Z : in Complex) return Float; function i return complex;

procedure Put(Z: in Complex); end ComplexPkg;

with text_io; use text_io; package body ComplexPkg is

package real_io is new float_io(float); use real_io; procedure Set ( Z: out Complex; A, B : in Float := 0.0) is begin

Z.X := A; Z.Y := B; end Set;

function Getr (Z : in Complex) return Float is begin

return Z.X; end Getr;

function Geti (Z : in Complex) return Float is begin

return Z.Y; end Geti;

function I return Complex is tmp : complex ;

begin

Set ( tmp, 0.0, 1.0); return Tmp;

end I;

procedure Put(Z: in Complex) is begin

Put("["); Put(Z.X); Put(", "); Put(Z.Y); Put("]"); end Put;

begin -- initialisation optionnelle null;

end ComplexPkg;

A ce stade on dispose d'un modèle de composant satisfaisant mais n'offrant aucun niveau de protection des données. Ainsi un type de données défini dans la spécification d'un composant verra son implantation librement accessible, avec le risque évident de dysfonctionnement à la moindre mise à jour.

Pour contrer ces mauvaises pratiques potentielles, ADA complète son modèle composant en affinant la notion de spécification, désormais partitionnée en deux blocs : une partie publique et une partie privée.

On pourra donc se contenter d'indiquer l'existence d'un type de donnée dans la partie publique, quand son implantation sera réservée à la partie privée. Pour un tel type de données aucun accès direct à l'implantation n'est possible. Seules les primitives proposées par le package permettront d'obtenir ou de fixer les valeurs de variables du type concerné.

package ComplexPkg is type Complex is private;

procedure Set ( Z: out complex; A, B : in Float := 0.0); function Getr (Z : in Complex) return float;

function Geti (Z : in Complex) return Float; function i return complex;

(4)

procedure Put(Z: in Complex);

private

type Complex is record

X,Y : Float := 0.0; end record;

end ComplexPkg;

Lorsqu'une unité de compilation doit faire appel à un package, l'instruction with est utilisée. Elle permet à la fois de disposer d'une visibilité sur les types et sous-programme définis, mais instruit aussi le compilateur pour les opérations de link futures. On peut imaginer que l'instruction with équivaut au couple #include, -lXXX du langage C.

with ComplexPkg; procedure exemple7 is z1, z2: ComplexPkg.complex; begin ComplexPkg.set(z1, 6.0, 8.0); ComplexPkg.set(a =>9.0, b =>3.0, z => z2); ComplexPkg.put(z2); end exemple7;

On couple le plus souvent l'instruction with à la clause use. Ceci va importer l'ensemble des symboles définis dans le package directement le contexte courant. Il devient alors inutile de préfixer les noms de types, variables, sous-programmes par le nom du package. En cas de conflit, notamment entre deux packages, il suffira de préfixer au cas par cas.

with complexPkg; use complexPkg; procedure exemple8 is z1, z2: complex; begin set(z1, 6.0, 8.0); set(a =>9.0, b =>3.0, z => z2); put(z2); end exemple8;

III.2. Librairies hiérarchiques

Le package en tant que tel est un modèle de composant satisfaisant. Cependant, il est apparu avec l'expérience que les différents niveaux de visibilité offerts étaient trop rigides, avec pour conséquences recompilations et modifications de code à répétition.

Pour résoudre ces problèmes, la version 1995 du langage introduit le concept de librairie fille sur les packages. Il devient alors possible de créer une arborescence de librairies complétant chacune un noeud de l'arbre.

package Complexpkg.Numerics is

function "+" ( Z1,Z2 : in Complex) return Complex; Function "*" ( Z1,Z2 : in Complex) return Complex; end Complexpkg.Numerics;

En outre il est possible de définir des extensions privées d'une librairie, soit pour traiter des éléments internes, soit encore pour permettre des extensions vendeur (en fonction du matériel par exemple).

with Ada.Numerics;

private package Complexpkg.Internals is type PolComplex is private;

function Cart2pol(Z: in Complex) return Polcomplex;

private

subtype Positivefloat is Float range 0.0 .. Float'Last;

subtype Angle is Float range -Ada.Numerics.PI .. Ada.Numerics.PI; type Polcomplex is record

R : Positivefloat := 0.0; A : Angle := 0.0;

end record;

end Complexpkg.Internals;

Avec ces librairies hiérarchiques il devient possible de faire évoluer des composants sans remettre en cause l'existant et notamment sans devoir procéder à de nouvelles campagnes de tests sur les éléments existants.

IV. Généricité

Pour compléter son modèle composant ADA dispose de mécanismes permettant la réalisation de composants génériques. On peut voir un composant générique comme un élément paramétré. Lors de l'utilisation effective on précisera les paramètres et une version spécifique du composant sera créée et utilisable. De manière imagée on peut se représenter le composant générique comme un moule qui va permettre de créer autant de composants que nécessaires.

En règle général un composant générique permettra d'implanter un

algorithme et sera paramétré par un type de données auquel appliquer cet algorithme (exemple la gestion de piles ou de files d'attentes).

generic

type Item is private;

with function "+" (A, B : in Item) return Item; with function "*" (A, B : in Item) return Item; Zero : Item;

with procedure put (i : in item); package Matrixpkg is

type Matrix is array (Integer range <>, Integer range <>) of Item; function "+"(A, B : in Matrix ) return Matrix;

function "*"(A, B : in Matrix ) return Matrix; procedure put(m : in matrix);

end Matrixpkg; Mod èle de p acka ge Pa ram ètre s : types , vale urs, fon ctions Nouveau package

(5)

Il convient à chaque fois de bien étudier les paramètres requis par un module générique. Si naturellement un type de donnée est en général requis, le plus souvent il est nécessaire de l'accompagner de diverses opérations et des éléments neutres associés.

Le corps d'un module générique ne diffère en rien du corps d'un module classique. Le type de donnée générique est manipulé comme un type standard, à ceci près que seuls l'affectation, le test d'égalité et les opérations passés en paramètre sont disponibles. Ce qui est logique puisque rien ne permet de supposer l'existence d'autres opérations sur ce type.

A l'usage, un module générique doit être instancié. Cela revient à créer un module classique à partir de son modèle générique et de paramètres.

with Matrixpkg; -- impossible d'utiliser use sur un module générique with text_io; use text_io;

procedure Exemple9 is

package Int_Io is new Integer_Io(Integer); use Int_Io; procedure Put2(I :in Integer) is

begin -- petite astuce car notre module attend une fonction Put(I); -- à un seul paramètre, or la fonction put livrée par end Put2; -- défaut comporte plusieurs paramètres, par défaut, -- mais plusieurs quand même.

package Int_Matrix is new Matrixpkg(Integer, "+", "*", 0, Put2); use Int_Matrix; -- use est utilisable sur la version instanciée A : Matrix( 1..3, 1..2); B : Matrix ( 0..1, 2..4); begin A := ( others => ( others => 1 ) ); B := ( others => ( others => 3 ) ); A := A + B; put(a); end Exemple9;

V. Modèle objet

Malgré la richesse de la notion de package, il manquait au langage un modèle objet et plus précisément le concept d'héritage. Plutôt que d'introduire la notion de class brutalement, comme le fait C++, ADA se contente de tirer profit des éléments déjà présents et va préférer aux classes l'extension de la notion de types. C'est pourquoi on parlera de programmation par extension.

V.1. Extension de types

Cette possibilité d'extension va s'appliquer au type record (l'équivalent des structures en C). En l'absence d'enveloppe pour l'objet (la classe) on va appeler méthodes (ou procédures et fonctions primitives) les procédures ou fonctions prenant le type étendu en paramètre ou pour les fonctions retournant ce type. C'est donc sur cet ensemble type plus primitives que va porter l'héritage.

Pour préciser qu'un type record va pouvoir faire l'objet d'extensions on va utiliser le mot clef tagged. Le mot tagged est réellement adapté car en fait le type record se retrouve comme

affublé d'un attribut supplémentaire, un tag, permettant de déterminer son type.

Pour étendre effectivement un type, on utilisera le mot clef with en précisant un record additionnel. On peut naturellement étendre un objet sans ajouter aucun attribut en indiquant que le record ajouté est vide.

procedure exemple10 is

type Point is tagged record

X, Y : Float; end record;

type Circle is new Point with record

R : Float; end record;

type Sphere is new Circle with null record; type Box is new Point with record

W, H : Float; end record; P : Point ;

c : circle := ( p with 8.0); -- un cercle est un point + un rayon begin

P := Point(C); -- on peut aussi caster en sens inverse end exemple10;

V.2. Polymorphisme, Class wide programming

Avec les types étendus on définit une véritable arborescence de types. ADA permet d'accéder en globalité à l'ensemble de ces types. On parle de class wide programming.

Pour désigner le type représentant l'ensemble des types dérivés d'un type racine, on utilise la qualification avec l'attribut class. Une classe en ADA n'est donc pas un type d'objet, mais plutôt la réunion d'un ensemble de types dérivés.

Il est ainsi possible de définir des fonctions opérant sur toute une classe, classe dont certains types

peuvent être encore non définis au moment de la conception de la fonction, et ajoutés ultérieurement dans des librairies filles.

Avec ce mécanisme il devient possible de sélectionner les méthodes appliquées au plus tard, au moment de l'exécution, ceci en fonction de la nature effective du paramètre (on parle de late binding).

with Text_Io; use Text_Io; with ada.tags; use ada.tags;

with ada.Integer_Text_Io; use ada.Integer_Text_Io; procedure exemple11 is

type Point is tagged record

X,Y : Float; end record;

type Circle is new Point with record

A B D Is new Is new Is new C A'Class B'Class

(6)

R : Float; end record;

type Rectangle is new Point with record

Width, Height : Float; end record;

type object is access point'class;

procedure Dispatch(O : in point'class) is begin

if (O in Circle'Class) then

null; -- on devrait faire qquechose elsif (O in Rectangle'Class) then null;

else

Put_Line("Hum, this " & external_tag(o'tag) & "looks weird to me."); end if; end Dispatch; I : Integer; O : Object; begin Get(I); case I is when 1 => O := new Point; when 2 => O := new Circle; when others => O := new Rectangle; end case; dispatch(O.all); end exemple11; V.3. types abstraits

Enfin, ADA comme la plupart des langages, offre la possibilité de définir des types et sous-programmes abstraits, ceux-ci permettent de définir un cadre à respecter et implanter par les types dérivés. Naturellement ces types et sous-programmes ne sont pas utilisables en tant que tels.

procedure exemple12 is

type Object is abstract tagged record

X, Y : Float; end record;

-- Object n'est pas utilisable en tant que tel -- mais les types dérivés oui.

Type Point is new object with null record; type Circle is new Point with record

R : Float; end record; P : Point ; C : Circle := ( P with 8.0); begin P := Point(C); end Exemple12;

V.4. Types dérivés et package

On a vu qu'un package peut cacher l'implantation des types définis dans sa spécification. La définition détaillée restant alors protégée dans la zone privée. Dans le cas de la programmation par extension, ce fonctionnement permet de définir avec beaucoup de finesse le degré de visibilité des types définis.

procedure Exemple13 is package Test1 is

-- on nous cache meme le fait que t est tagged -- il nous sera impossible de créer des types -- dérivés

type T is private; private

type T is tagged record X,Y : Float; end record; end Test1;

package body Test1 is end Test1;

package Test2 is

-- ici, meme si l'implantation exacte de t est -- inconnue, on pourra créer un type dérivé type T is tagged private;

private

type T is tagged record X,Y : Float; end record; end Test2;

package body Test2 is end Test2;

-- on etend donc un type opaque (test2.t) avec de -- nouveau éléments.

type T2 is new Test2.T with record Z : Float; end record; -- ce qui est aussi possible dans un package tiers : package Test3 is

type T is new Test2.T with private; private

type T is new Test2.T with record Z: Float; end record; end Test3;

package body Test3 is end Test3;

-- à cela il faudrait ajouter les possibilités propres -- aux librairies filles.

begin null; end Exemple13;

(7)

VI. Environnement multitâche intégré

La prise en compte au sein même du langage du multitâche est une autre originalité d'ADA. Cet élément clef a d'ailleurs longtemps été mal supporté dans les outils Open Source, notamment parce que les systèmes sous-jacents (Les Unix et GNU/Linux en particulier) ne supportaient pas le multi-threading en natif.

VI.1. Les objets tâches

Dans un programme classique il n'existe qu'une tâche, le sous-programme principal. Celui-ci se déroule en exécutant en séquence ses instructions (et celles des sous-programmes appelés). En ADA il est élémentaire de créer des tâches additionnelles. Le runtime (seul ou avec l'OS) va alors donner l'illusion que toutes ces tâches s'exécutent simultanément en exécutant successivement des petits morceaux de chaque tâche (C'est le principe du multitâche, tout l'objet du jeu étant alors de correctement réaliser le partage de la ressource CPU).

with text_io; use text_io; procedure exemple14 is task T1;

task body T1 is begin

Put("hello from t1"); end T1;

begin

Put("hello from main"); end exemple14;

Excepté dans le cas particulier d'une tâche créée dynamiquement, toutes les tâches voit leur exécution commencer au begin du bloc où elles ont été déclarées. Cette exécution comporte : une phase d'élaboration des déclarations (variables, sous-programmes) et l'exécution des instructions proprement dites.

A noter qu'une tâche peut avoir terminé l'exécution de ses instructions (on dit qu'elle est achevée) sans pour autant être terminée. En effet, lorsque plusieurs tâches sont définies au sein d'un même bloc, la terminaison effective de l'ensemble, tâches + bloc, ne survient que lorsque tous sont achevés. Il faut donc prêter attention aux tâches qui pourraient boucler et ne jamais autoriser la terminaison du bloc qui les contient, par exemple une procédure ou une fonction.

La définition d'une tâche en ADA peut être (très grossièrement) comparée à celle déjà détaillée du package. Chaque tâche va en effet disposer d'une spécification et d'une implantation (le corps).

with text_io; use text_io; procedure exemple15 is task T1;

task t2;

task type Simple;

type Simpleptr is access Simple; T3 : Simple;

T4 : Simpleptr; task body T1 is begin

Put_Line("Hello from t1"); end T1;

task body T2 is begin

Put_Line("Hello from t2"); end T2;

task body simple is begin

Put_line("hello from simple"); end simple;

begin

Put_line("hello from main"); T4 := new Simple;

Put_line("Created new simple"); end exemple15;

Dans le cas d'une tâche si le corps représente naturellement les instructions qui seront exécutées, les spécifications de leur coté vont préciser l'API de la tâche (on parle de points d'entrée). Cette API décrit les différents appels reconnus par la tâche, appels (qu'on peut comparer aux appels système d'un noyau GNU/Linux par exemple) qui serviront à communiquer avec elle.

En effet en l'absence de points d'entrée une tâche s'exécute dans son environnement indépendamment du reste. Cette situation est rarement souhaitée.

activation

begin

new

t1

t2

t3

t4

Activation

Execution

Achèvement

terminaison

(8)

VI.2. Le modèle du rendez-vous

Les points d'entrée définis sont donc des moyens de communiquer avec une tâche, mais selon quel protocole? ADA adopte le modèle du rendez-vous (le même que celui de la vie courante). Il s'agit d'un modèle avec synchronisation.

Du coté de l'appelant (on peut dire client), on doit attendre que la tâche appelée (tâche serveur) soit prête à traiter l'appel. Pendant le traitement, l'appelant est suspendu. Il reprend le déroulement de ses instructions une fois l'appel terminé.

Coté appelé (tâche serveur), à supposer que l'on soit en attente d'appel on reste bloqué tant qu'aucun appel n'est reçu. Lorsqu'un appel arrive, les instructions associées à ce point d'entrée sont exécutées. Ceci fait, les deux tâches reprennent le déroulement de leurs instructions, chacune de leur coté.

Dans la pratique, pour appeler une tâche on utilise la notation pointée : <nom de tâche>.<nom du point d'entrée> ( <paramètres éventuels>); Coté tâche appelée, ou serveur, on utilise l'instruction accept pour attendre un appel. Ce sont les spécifications de ce bloc qui

sont reprises dans les spécifications de la tâche serveur en utilisant l'instruction entry. with text_io; use text_io;

procedure exemple16 is task T1 is entry Bye; end T1; task T2 is entry Hello; end T2; task body T1 is begin T2.Hello; accept Bye do

Put_Line("T2 a eu notre bonjour et nous dit au revoir"); end Bye;

end T1;

task body T2 is begin

accept Hello do

Put_Line("T1 nous dit bonjour, mais on ne veut pas parler"); end Hello; T1.Bye; end T2; begin null; end exemple16;

VI.3. Traitement amélioré des rendez-vous

En utilisant l'instruction accept dans une boucle on peut facilement traiter les rendez-vous en séquence, créant une véritable tâche serveur. Cependant cela ne permet pas de répondre à des rendez-vous différents. Pour cela ADA dispose de l'instruction select.

Select permet d'attendre des rendez-vous différents. L'instruction est bloquante (comme accept) tant qu'aucun appel n'est en attente. Lorsqu'un rendez-vous est demandé, l'instruction accept correspondante est utilisée. En cas de demandes multiples, un rendez-vous est choisi aléatoirement. En plaçant select au sein d'une boucle les demandes de rendez-vous ignorés seront traitées lors d'un prochain tour de boucle.

with Text_Io; use Text_Io;

with Ada.Numerics.Discrete_Random; procedure exemple17 is

-- on veut des valeurs entre 0 et 10

-- il suffit de definir le sous-type adhoc subtype Smalldelay is Integer range 0 .. 10;

-- d'instancier le package fournissant un générateur -- de nombres aléatoires pour notre type à nous

package Random_Smalldelay is new Ada.Numerics.Discrete_Random (Smalldelay);

use Random_Smalldelay; gen : generator;

package int_io is new integer_io(integer); use int_io; task Server is

-- le serveur répond à 3 appels entrant entry Init(I : in Integer);

entry Set(I : in Integer); entry Get(I : out Integer); end Server;

-- le client à aucun task type Client;

Clients : array ( 1..10) of Client; task body Server is

local : integer; begin

accept Init(I : in Integer) do local := i;

end Init;

put_line("le server est initialisé"); loop

select

accept Set(I: in Integer) do Local := I;

end Set; or

accept Get(I : out Integer) do I := Local; end Get; or terminate; end select;

t1

t2

t2.hello

t1.bye

Acce

pt

he

llo

Acce

pt

by

e

(9)

end loop; end Server;

task body Client is s : integer := 5; begin

put_line("je suis un client"); for I in 1 .. 10 loop

if (I mod 2) = 0 then Server.Get(S);

delay duration(random(gen)); Put("lecture de la variable :"); Put(s);

New_Line; else

Server.Set(S + 10 - I); delay duration(random(gen));

Put_Line ("ecriture de la variable"); end if ; delay 0.0; end loop; end Client; begin delay 2.0;

Put_Line("Initialisation server"); server.init(5);

Put_Line("programme principal"); end exemple17;

Avec select une tâche serveur va donc pouvoir répondre à de multiples rendez-vous. Mais l'instruction select recèle de nombreux raffinements :

• la possibilité de sortir de la phase d'attente, soit sur timeout (clause delay) ou lorsqu'il n'est plus possible pour la tâche d'être appelée (clause terminate, qui achève la tâche),

• la possibilité de conditionner (on parle de gardes) chaque bloc accept à une expression booléenne.

VI.4. Types tâches et création dynamique

Jusqu'à présent les tâches sont créées une à une. Cependant ADA permet de définir des types d'objet tâches.

Dans ce cas, on dispose de types au sens classique du terme, il est alors possible de créer des variables tâches unitaires, ou sous forme de tableau.

with text_io; use text_io; procedure exemple18 is task type simple; task body simple is

begin

Put("hello from simple"); end simple;

t1, t2 : simple; begin

Put("hello from main"); end exemple18;

Enfin, il est possible de définir un type pointeur sur le type tâche précédemment défini. Avec dans ce cas l'opportunité de créer de nouvelles tâches dynamiquement au coeur d'un bloc d'instructions.

VII. Distribution des tâches

A suivre.

Références

Documents relatifs

Dans le cadre de la réduction du temps de travail (RTT), la loi du 19 janvier 2000, dite « Aubry 2 », a posé le principe d’une garantie d’évolution du pouvoir d’achat des

En revanche, suivant l’hypothèse (2), inspirée par la division entre initiale et rime, la réalisation de A est une descente, dans tous les cas ; une fois la courbe des syllabes à

ainsi, pour ce dernier aspect, qu'une étude mettant en évidence le décalage entre l'offre de zones aménagées pour les activités économiques et le flux

For example, the environmental impact on climate change and human toxicity (cancer effects) for electricity production by a current mono- Si laminate installed in site 1

Pour tenter de cerner cette sp´ecificit´e, nous comparerons la th´eorie de la lem- niscate de Fagnano et Euler avec la mani`ere dont, quelques d´ecennies plus tard, Legendre

C’est à ce niveau qu’opèrent les ouvertures et les blocages : les relations entre l’Etat, les milieux de la recherche et les entreprises, le degré d’autonomie

composition en même temps qu’il tisse sa diégèse autour de ce procédé, est amplifiée par des textes programmatiques, dans lesquels le cut-up est présenté non plus comme

Yuan- Yuan, « Simulation of transition from townsend mode to glow discharge mode in a helium dielectric barrier discharge at atmospheric pressure », Chi- nese Physics B , vol.