Aller au contenu

Programmation Ada/FAQ/Le Langage Ada

Un livre de Wikilivres.

École Nationale Supérieure des Télécommunications Département Informatique et Réseaux

Copyright © 2001-2007 Samuel Tardieu et les différents auteurs des réponses.

Cet article est livré en l'état, sans garantie déclarée ni implicite. Bien que tous les efforts aient été déployés pour s'assurer de la validité des informations contenues dans ce document, l'auteur ne pourra être tenu pour responsable d'erreurs, ou d'omissions, ni d'éventuels dommages résultant de leur utilisation.

Ce document peut être librement copié et distribué en respectant les conditions de la Licence de documentation libre GNU.

Le langage Ada

[modifier | modifier le wikicode]

Comment transformer un entier en chaîne de caractères ?

[modifier | modifier le wikicode]

En Ada, les types scalaires tels que les entiers, les réels et les types énumérés disposent d'un attribut Image qui permet d'obtenir une chaîne de caractères à partir d'une valeur de ce type et un attribut Value qui permet d'effectuer l'opération inverse. L'usage de ces deux attributs est illustré dans l'Exemple 1.

Exemple 1. Renvoyer une chaîne contenant un montant en Euros

function Euros (Montant : Natural) return String is
begin
    return Natural'Image (Montant) & "EUR";
end Euros;

Comment transformer une chaîne de caractères en entier ?

[modifier | modifier le wikicode]

Comme indiqué dans la question précédente, les types scalaires disposent d'un attribut Value, comme illustré dans l'Exemple 2.

Exemple 2. Lire la valeur d'un entier

A : constant Integer := Integer'Value ("10");  -- A vaut 10

Comment obtenir le code ASCII d'un caractère ?

[modifier | modifier le wikicode]

Dans ce cas également, des attributs sur le type Character existent : Pos, qui sert à obtenir la position d'une valeur dans le type énuméré qui le définit, et Val qui effectue l'opération inverse. L'Exemple 3 illustre l'utilisation de ces attributs.

Exemple 3. Obtenir le code ASCII d'un caractère

X : constant Integer   := Character'Pos ('A');  -- X vaut 65
Y : constant Character := Character'Val (66);   -- Y vaut 'B'

Est-il possible de définir un nouvel attribut (par exemple Somme, qui ferait la somme de plusieurs valeurs d'un type donné) ?

[modifier | modifier le wikicode]

Non. Seuls certains attributs existants peuvent être modifiés, mais en aucun cas un nouvel attribut ne peut être ajouté par l'utilisateur.

Comment obtenir le minimum (resp. maximum) de deux entiers ?

[modifier | modifier le wikicode]

Les types numériques disposent d'un attribut Min (resp. Max) qui permet d'obtenir le minimum (resp. le maximum) de deux nombres de ce type. L'Exemple 4 présente l'utilisation de ces attributs.

Exemple 4. Maximum de deux entiers

--  La fonction suivante retourne le double du maximum de deux entiers
function Double_Maximum (X, Y : Integer) return Integer is
begin
    return 2 * Integer'Max (X, Y);
end Double_Maximum;

Comment saisir une chaîne de caractères de taille variable ?

[modifier | modifier le wikicode]

On peut utiliser Ada.Text_IO.Get_Line, qui prend deux paramètres en lecture/écriture. Dans la version à deux paramètres, le premier est la chaîne dans laquelle lire les caractères et le second contient, à la sortie de la procédure, la position du dernier caractère dans le tampon de saisie, comme illustré dans l'Exemple 5.

La variable contenant cette position doit pouvoir contenir toutes les valeurs possibles, y compris celle désignant la chaîne vide.

Exemple 5. Lire une chaîne de taille variable

with Ada.Text_IO;
[...]
declare
    Buffer : String (1 .. 30);         -- Tampon de 30 caractères
    Last   : Natural;
begin
    Ada.Text_IO.Get_Line (Buffer, Last);   -- Lit au plus 30 caractères
    if Last = 0 then
        Ada.Text_IO.Put_Line ("Vous n'avez entré aucun caractère");
    else
        Ada.Text_IO.Put_Line ("Ok, vous avez entré: " & Buffer (1 .. Last));
    end if;
end;

Pourquoi après un Ada.Text_IO.Get_Line le curseur ne se trouve-t-il pas en début de la ligne suivante ?

[modifier | modifier le wikicode]

Si l'utilisateur rentre le nombre de caractères maximum fixé par le second paramètre de Get_Line, la saisie s'arrêtera à ce moment là et aucun retour chariot ne sera pris en compte. Une solution consiste à utiliser Ada.Text_IO.Set_Col (Ada.Text_IO.Standard_Input, 1) avant une nouvelle entrée ou une nouvelle sortie.

Pourquoi Get ou Get_Line après un Get n'attendent rien et se comportent comme si j'avais tapé uniquement sur la touche entrée ?

[modifier | modifier le wikicode]

La version de Get permettant de saisir un type scalaire ne consomme que les caractères nécessaires. Le caractère entrée correspondant au retour chariot est alors conservé dans le tampon de saisie. L'utilisation de Get ou Get_Line consommera alors ce caractère.

Notons que ce comportement, qui peut paraître au premier abord peu intuitif, introduit une symétrie entre Put et Get, entre Put_Line et Get_Line et entre New_Line et Skip_Line.

Quel est le caractère de fin de chaîne ?

[modifier | modifier le wikicode]

Aucun. Pour le langage C, une chaîne est uniquement une suite de caractères terminée par un caractère NUL (0). Ada dispose pour sa part d'un véritable type String qui permet de représenter une chaîne de caractères à l'aide de sa longueur, et non pas à l'aide d'un caractère arbitraire.

Comment récupérer la ligne de commande ?

[modifier | modifier le wikicode]

En utilisant le paquetage standard Ada.Command_Line, qui offre notamment les fonctions Argument_Count et Argument.

Comment effacer l'écran ?

[modifier | modifier le wikicode]

Cette question est trop imprécise : on n'utilisera pas la même méthode pour effacer l'écran sous DOS, sous Windows ou sous X-Window. Il n'y a pas de méthode normalisée en Ada.

Comment tracer un trait ?

[modifier | modifier le wikicode]

Même réponse que dans la question ci-dessus : la question n'est pas assez précise.

Comment effacer l'écran sous Windows ?

[modifier | modifier le wikicode]

Il est possible d'utiliser le paquetage NT_Console.

Comment libérer un pointeur ?

[modifier | modifier le wikicode]

Il faut instancier la procédure générique Ada.Unchecked_Deallocation. L'instance fournit une procédure permettant de libérer un pointeur du type voulu, comme illustré dans l'Exemple 6.

Exemple 6. Désallouer une zone mémoire

with Ada.Unchecked_Deallocation;
package Foo is
    type Tab is array (Natural range <>) of Integer;
    type Tab_Access is access Tab;
    procedure Free is
        new Ada.Unchecked_Deallocation (Tab, Tab_Access);
    --  Si on a une variable T de type Tab_Access, on
    --  peut maintenant invoquer Free (T).
    end Free;
end Foo;

Doit-on vraiment instancier Ada.Unchecked_Deallocation pour chaque type ?

[modifier | modifier le wikicode]

Oui. Cependant, en utilisant la programmation « class-wide », il est possible de créer un déallocateur pour l'ensemble de la hiérarchie, avec une seule instanciation.

Existe-t-il un ramasse-miettes pour Ada ?

[modifier | modifier le wikicode]

Très peu d'implémentations proposent un ramasse-miettes[1]. En règle générale, seuls les compilateurs ciblant la machine virtuelle Java en fournissent un. Si un compilateur Ada cible la machine virtuelle .NET, il est probable qu'il utilisera le ramasse-miettes sous-jacent. Cependant, le paquetage Ada.Finalisation permet de contrôler la déallocation profonde d'un objet qui sort du contexte dans lequel il est défini.

Comment créer une chaîne de caractères de taille inconnue à priori ?

[modifier | modifier le wikicode]

Il faut initialiser la chaîne de caractères au moment de sa création, comme dans l'Exemple 7.

Exemple 7. Création de chaîne

S : String := "ma chaîne"         -- S est de taille 9 et de contenu modifiable
T : String := F (X, Y);           -- T aura la taille requise
U : constant String := "bonjour"; -- U est de taille 7 et de contenu non modifiable

Est-il possible de gérer les chaînes de caractères comme en C ?

[modifier | modifier le wikicode]

Oui, c'est possible, en utilisant un type «pointeur sur chaîne de caractères», mais il existe la plupart du temps de meilleures solutions.

Est-il possible de gérer des chaînes de caractères de taille variable ?

[modifier | modifier le wikicode]

Il existe un type standard Ada.Strings.Unbounded.Unbounded_String qui permet de créer des chaînes de caractères de taille variable. Lorsque l'on connaît la taille maximale de la chaîne, le type standard Ada.Strings.Bounded.Bounded_String est généralement plus efficace.

Existe-t-il un équivalent en Ada de la fonction realloc() du C, qui permet de changer la taille allouée pour une zone mémoire ?

[modifier | modifier le wikicode]

Non. Toutefois, la possibilité d'utiliser une valeur initiale pour la primitive new permet de remplacer facilement cette fonction, comme illustré dans l'Exemple 8.

Exemple 8. Ajouter un caractère à une chaîne

with Ada.Unchecked_Deallocation;
[...]
type String_Access is access String;
procedure Free is new Ada.Unchecked_Deallocation (String, String_Access);
S, Old_S : String_Access;
[...]
Old_S := S;
S := new String'(S.all & " ");
Free (Old_S);

Que signifie le pragma Pure ?

[modifier | modifier le wikicode]

Cette directive de compilation signifie que le paquetage auquel elle s'applique ne contient que des sous-programmes sans effet de bord, et déterministes. En gros, les sorties ne dépendent que des entrées, et pas d'un quelconque état interne ou externe.

Comment convertir une chaîne de caractères en minuscules ?

[modifier | modifier le wikicode]

Il faut utiliser la fonction To_Lower du paquetage Ada.Characters.Handling.

Comment faire des opérations bit à bit ?

[modifier | modifier le wikicode]

En Ada, les opérations bit à bit se font sur les types modulaires. Les opérations and, not, or et xor sont définies. Le paquetage standard Interfaces définit des opérations de décalage supplémentaires.

Est-il possible d'obtenir des tranches de tableaux multi-dimensionnels ?

[modifier | modifier le wikicode]

Non.

Comment attendre un temps donné ?

[modifier | modifier le wikicode]

Il faut utiliser l'instruction delay suivie d'un nombre de secondes en virgule fixe (type Duration), comme illustré dans l'Exemple 9.

Exemple 9. Attendre deux secondes et demie

delay 2.5;    -- Attend 2,5 secondes

Comment effectuer un tirage aléatoire ?

[modifier | modifier le wikicode]

Ada propose plusieurs paquetages de gestion de nombres aléatoires. Le plus simple, pour obtenir un élément parmi une énumération, est d'utiliser Ada.Numerics.Discrete_Random, un paquetage générique qu'il faut instancier avec un type représentant l'ensemble tel qu'illustré dans l'Exemple 10. Pour un tirage aléatoire sur un nombre réel, il est possible d'utiliser le paquetage Ada.Numerics.Float_Random qui permet d'obtenir une variable aléatoire uniforme entre 0 et 1.

Exemple 10. Tirer un nombre entre 0 et 36

with Ada.Numerics.Discrete_Random;
with Ada.Text_IO;
procedure Roulette is
    type Numero is range 0 .. 36;
    package La_Roulette is new Ada.Numerics.Discrete_Random (Numero);
    use La_Roulette;    -- Rend Generator, Reset et Random visibles
    A : Numero;
    G : Generator;
begin
    Reset (G);          -- Initialise le générateur (à faire une seule fois)
    A := Random (G);    -- Tire un nombre au hasard entre 0 et 36
    Ada.Text_IO.Put_Line ("Le numéro est: " & Numero'Image (A));
end Roulette;

Où se trouvent les fonctions mathématiques ?

[modifier | modifier le wikicode]

Les fonctions mathématiques élémentaires (comme la racine carrée) se trouvent dans le paquetage Ada.Numerics.Elementary_Functions. Les fonctions présentes dans ce paquetage permettent de travailler sur le type de base Float.

Pour utiliser de telles fonctions avec un autre type de nombres flottants, il faut utiliser une instanciation du paquetage générique Ada.Numerics.Generic_Elementary_Functions.

Comment augmenter la taille de la pile d'une tâche ?

[modifier | modifier le wikicode]

La directive de compilation Storage_Size permet de donner la taille, en unités de stockage (octets en pratique), de la pile d'une tâche ou d'un type de tâche, comme illustré dans l'Exemple 11.

Exemple 11. Choisir la taille de la pile d'une tâche

task T is
    pragma Storage_Size (300_000);   -- 300 kilo-octets pour la pile
end T;

task body T is
    [...]
end T;

Comment écrire un paquetage générique prenant, en paramètre, une classe dérivée d'une classe donnée ?

[modifier | modifier le wikicode]

L'exemple 12 montre un paquetage générique prenant en paramètre une classe dérivée d'une autre classe, appelée ici Classe_Abstraite.

Exemple 12. Paquetage générique et classe dérivée

with Abstrait;
generic
    type Classe_Concrete is new Abstrait.Classe_Abstraite with private;
package Generic_Test is
    [...]
end Generic_Test;