You are here: Tutoriels » L'Ada » Les packages

Pour organiser nos programmes, on peut regrouper différentes fonctions dans un package come Ada.Text_IO qui contient toute les fonctions d'entrée/sortie de texte.

Les packages

Un package se compose de 2 partie :
  • La spécification (.ads)
  • Le corps du package (.adb)
Attention: un package nommé "toto" doit être dans les fichiers "toto.ads" et "toto.adb"

La spécification

La spécification d'un package est la liste des fonctions/procédure/types/... que ce package veux faire connaitre au reste du monde. La syntaxe est la suivante:
package Package_Name is
  procedure Une_Procedure;
end Package_Name;

Le corps

Le corps du package est son implémentation. Il faut au minimum implémenter les fonctions déclarées dans la spécification. On peut rajouter des fonctions qui ne seront visibles que par les autres fonctions du package, pas par les utilisateurs.

La syntaxe est la suivante :
package body Package_Name is
  procedure Une_Procedure is
  begin
    null;
  end Une_Procedure;
end Package_Name;

La partie private

Un package peut contenir une partie private :
package Package_Name is
  procedure Une_Procedure;
private
  procdure Une_Procedure_Private;
end Package_Name;

Cette partie "private" peut contenir un peu tout et n'importe quoi. Ce qui y sera définit n'est visible que par les membres du package et de ses sous-packages (cf. plus loin). Mais surtout, grâce à cette partie "private", on peut définir des types "private".

Exemple : on veut faire un package "Stack", avec 2 méthodes "Push" et "Pop" et un type : "Stack_Type". Ce type va être un record avec un tableau et un pointeur sur le 1er élément de la pile. Idéalement, les utilisateurs du package ne devrait pas savoir ce qu'il y a dans ce type, comme ça on est sur qu'ils ne le modifieront pas, et si jamais on change de façon de faire (avec par exemple une allocation dynamique de la mémoire) on est sur que tout marchera encore. Pour faire cela, on va déclarer notre type "private":
package Stack is
  type Stack_Type is private;
  procedure Push (Stack : in out Stack_Type; Value : Integer);
  procedure Pop (Stack : in out Stack_Type; Value : out Integer);
private
  Max_Size : constant Integer := 100;
type Buffer_Type is array (1 .. Max_Size) of Integer;
type Stack_Type is record
    Buffer : Buffer_Type;
    Top : Integer := 0;
  end record;
end Stack;

Un utilisateur pourra déclarer un élément de type Stack.Stack_Type mais ne pourra pas savoir ce que cette élément contient.

Initialisation

Un package peut aussi comporter une partie d'initialisation. La syntaxe est très simple:
package body Package_Name is
  procedure Une_Procedure is
  begin
    ...
  end Une_Procedure;
  ...
begin
  Code_D_Initialisation;
end Package_Name;

Le code entre le begin et le end Package_Name sera exécuté une seule fois, au début du programme.

Ordre d'élaboration

L'élaboration d'un programme consiste à initialiser ses variables et exécuter son éventuelle partie d'initialisation.

L'ordre d'élaboration peut poser un problème puisque les parties d'initialisation peuvent appeler des procédures : il faut être sur que toute les variables utilisé par ces procédures sont initialisé.

Une règle simple aurait pu être d'élaborer un package dès que quelqu'un en fait un "with" mais cela empêcherait les packages qui se "with" l'un l'autre : lequel initialiser en premier ? Ada utilise des règles pour définir un ordre d'élaboration intelligeant. Généralement, ca se passe bien. Pour influer sur l'ordre d'élaboration, on peut utiliser quelque pragma :

pragma Elaborate_All

Le plus courant : lorsque le compilateur se rend compte qu'on utilise une fonction qui risque de ne pas être élaborée, on reçoit un message de ce type :

        >>> warning: call to "Set_X" may raise Program_Error
        >>> warning: missing pragma Elaborate_All for "Shix.Engines_Base"
        >>> warning:  called at line 18
Dans ce cas, il faut rajouter pragma Elaborate_All (Shix.Engines_Base)après le with Shix.Engines_Base.

pragma Elaborate_Body

Normalement, un programme est une procédure. Il arrive qu'il soit plus simple d'utiliser un package. Dans ce cas, l'initialisation du package est le programme principale. L'ads du package est donc vide. Hors, si le compilateur voir un ads vide, il ne comprend pas qu'il puisse y avoir un adb. Pour dire au compilateur que l'ads est vide et que c'est normal, on met un pragma dans l'ads, par exemple :
package Simulator is
  pragma Elaborate_Body;
end Simulator;

Pour aller plus loin

Pour plus d'info, voir par exemple la : http://gnu.huihoo.org/gcc/gcc-3.3.6/gnat_ug_unx/Elaboration-Order-Handling-in-GNAT.html#Elaboration-Order-Handling-in-GNAT

Sous-package

Un package peut avoir des packages enfants. Cela permet d'organiser un peu ses packages. Par exemple, pour notre robot, tous les packages qui ce rapport à la shix sont des sous packages de Shix.

Nom du package

La hiérarchie des packages se représente en utilisant un "." pour séparer les noms de packages. Attention : dans les noms de fichiers, le "." devient un "-". Ainsi, un sous-package B d'un package A a pour nom A.B et il se trouvera dans les fichiers a-b.ads et a-b.adb. 

Visibilité

Un sous-package fait automatiquement un "with" de tous ses parents. En revanche, il n'est pas possible de "wither" un sous-package dans son parent.

De plus, un sous-package peut accéder à la partie "private" de ses parents. Ceci permet, entre autre de mettre des choses pour le débogage/test dans la partie private d'un package. Les utilisateurs standards n'y auront donc pas accès alors que les programmes de tests feront un "with" d'un sous-package qui aura lui accès à ses fonctions et pourra même éventuellement les exporter.