Copyright IBM Corp. 1987, 2004. All Rights Reserved.

Le terme "Rational" et les produits Rational sont des marques de Rational Software Corporation. Les références à d'autres entreprises et à leurs produits utilisent des marques qui sont la propriété de leurs entreprises respectives et ne sont fournies qu'à titre indicatif.

Sommaire

A propos de ce document

Introduction

Principes fondamentaux
Hypothèses
Classification des recommandations
Première et ultime recommandation

Agencement du code

Généralités
Casse des caractères
Indentation
Longueur de lignes et retour à la ligne
Alignements

Commentaires

Généralités
Recommandations pour l'utilisation des commentaires

Conventions de dénomination

Généralités
Packages
Types
Exceptions
Sous-programmes
Paramètres d'objets et de sous-programme (ou d'entrée)
Unités génériques
Dénomination des stratégies pour les sous-systèmes

Déclarations des types, objets et unités de programmation

Types énumératifs
Types numériques
Types réels
Types article
Types accès
Types privés
Types dérivés
Déclarations d'objet
Sous-programmes et unités génériques

Expressions et instructions

Expressions
Instructions
Conseils pour le codage

Problèmes de visibilité

Surcharge et homographes
Clauses de contexte
Changement de nom
Remarque à propos des clauses Use

Considérations de structure du programme et de compilation

Décomposition des packages
Structure des parties déclaratives
Clauses de contexte
Ordre d'élaboration

Accès concurrent

Traitement des erreurs et des exceptions

Programmation de bas niveau

Clauses de représentation et attributs
Conversions sans vérification

Récapitulatif

Références

Glossaire


Chapitre 1

A propos de ce document

Ce document, Rational Unified Process - Recommandations de programmation Ada, constitue un modèle d'où vous pouvez dériver une norme de programmation dans votre propre organisation. Il spécifie comment rédiger des programmes Ada. Il est destiné à tous les concepteurs et développeurs de logiciels d'applications utilisant Ada comme langage d'implémentation ou comme langage de conception, par exemple pour la spécification d'interfaces ou de structures de données.

Les règles décrites dans ce document couvrent la plupart des aspects de rédaction du code. Les règles générales s'appliquent à l'agencement du programme, aux conventions de dénomination et à l'usage de commentaires. Des règles spécifiques s'adressent à des fonctions Ada sélectionnées et spécifient les constructions interdites, les schémas d'utilisation recommandés et fournissent des conseils d'ordre général visant à l'amélioration de la qualité des programmes.

Vous pourrez constater un certain chevauchement entre les principes de conception du projet et les principes de programmation détaillés ici. Ce chevauchement est délibéré. De nombreuses règles de programmation, notamment dans le domaine des conventions de dénomination, ont été intégrées pour permettre un support actif et renforcer une approche orientée objet de la conception de logiciel.

Ces principes ont été initialement rédigés pour Ada 83. Ils comprennent des règles de compatibilité avec Ada 95, mais sans recommandations spécifiques quant à l'utilisation des nouvelles fonctionnalités du langage présentes dans sa norme révisée (comme les types marqués, les unités enfants ou les types décimaux).

L'organisation de ce document suit, dans ses grandes lignes, la structure du Manuel de Référence Ada [ISO 8052].

Le chapitre 2, Introduction, explique les principes fondamentaux sur lesquels se basent les recommandations et introduit une classification de ces recommandations.

Le chapitre 3, Agencement du code, traite de l'organisation visuelle globale du texte des programmes.

Le chapitre 4, Commentaires, indique comment utiliser des commentaires afin de documenter le code sous une forme structurée, utile et propice à sa maintenance.

Le chapitre 5, Conventions de dénomination, soumet certaines règles générales, assorties d'exemples, pour l'attribution de noms aux entités du langage. Ce chapitre doit être adapté aux besoins particuliers de votre projet ou de votre organisation.

Le chapitre 6, Déclarations et le chapitre 7, Expressions et instructions, fournissent des conseils supplémentaires pour chaque type de construction du langage.

Le chapitre 8, Problèmes de visibilité et le chapitre 9, Considérations de structure du programme et de compilation, fournissent des orientations pour la structure et l'organisation globale des programmes.

Le chapitre 10, Accès concurrent, traite du thème particulier de l'exploitation des fonctions du langage relatives aux tâches et au temps.

Le chapitre 11, Traitement des erreurs et des exceptions donne des indications méthodiques et limpides sur l'opportunité d'utilisation d'exceptions pour la gestion des erreurs.

Le chapitre 12, Programmation de bas niveau, traite des problèmes associés aux clauses de représentation.

Le chapitre 13, Récapitulatif, résume les recommandations les plus importantes.

Le présent document remplace le document intitulé Ada Guidelines: Recommendations for Designers and programmers, Application Note #15, Rational, Santa Clara, CA., 1990.


Chapitre 2

Introduction

Principes fondamentaux

Ada a été explicitement conçu pour épauler le développement de logiciels de haute qualité, réutilisables, fiables et portables [ISO 87, sect. 1.3]. Cependant, aucun langage de programmation ne peut atteindre ce but de par lui-même. La programmation doit s'effectuer dans le cadre d'un processus hautement discipliné.

La production d'un code source Ada clair et intelligible représente le principal objectif de la majorité des recommandations décrites ici. Ce facteur est critique pour la fiabilité et la maintenabilité du programme. Par code clair et intelligible, on peut entendre le respect des trois principes fondamentaux suivants :

Surprise minimale

Au cours de son cycle de vie, le code source est lu plus fréquemment qu'il n'est écrit, particulièrement les spécifications. Idéalement, le code devrait pouvoir se lire comme une description en français des opérations qu'il effectue, tout en se chargeant de leur exécution. Les programmes sont écrits plus à l'intention des individus que des ordinateurs. La lecture du code implique un processus mental complexe pouvant être facilité par l'uniformité, également désigné dans ce guide comme le principe de surprise minimale. Un style uniforme à travers la totalité d'un projet constitue une raison majeure pour l'acceptation de normes de programmation par une équipe de développeurs de logiciels et ne doit pas être interprété comme une sanction ou comme un obstacle à la créativité et à la productivité.

Point de maintenance unique

Un autre principe important sous-jacent dans ce guide est le principe du point de maintenance unique. Dans la mesure du possible, une décision de conception doit être exprimée à un seul point dans la source Ada, et la plupart de ses conséquences doivent dériver par voie de programmation à partir de ce point. La violation de ce principe remet sérieusement en cause la maintenabilité et la fiabilité, tout comme l'intelligibilité du programme.

Bruit minimal

Enfin, comme contribution majeure à la lisibilité, le principe du bruit minimal a été appliqué. A savoir, un effort a été investi pour éviter d'encombrer le code source avec des "parasites" visuels : barres, cases et texte avec contenu informatif restreint ou informations sans grande contribution à la compréhension de l'objet du logiciel.

La portabilité et la réutilisabilité sont aussi à l'origine de plusieurs recommandations. Le code devra être porté vers plusieurs compilateurs différents destinés à des ordinateurs cible distincts et, en fin de compte, vers une version plus sophistiquée d'Ada, dénommée "Ada 95" [PLO92, TAY92].

Hypothèses

Les recommandations présentées ici reposent sur un petit nombre d'hypothèses élémentaires :

Le lecteur connaît Ada.

L'utilisation de fonctionnalités Ada avancées est encouragée là où elle s'avère bénéfique, plutôt que découragée car non familières à certains développeurs. Ceci est l'unique moyen de tirer profit de l'utilisation d'Ada pour le projet. Ada ne doit pas être utilisé comme s'il s'agissait de Pascal ou de FORTRAN. Il est déconseillé de paraphraser le code dans des commentaires ; tout au contraire, Ada devrait être utilisé à leur place, dans la mesure du possible.

Le lecteur est à l'aise en anglais.

Les conventions de dénomination se basent souvent sur l'anglais, tant au niveau du vocabulaire que de la syntaxe. De plus, les mots-clés d'Ada sont des termes anglais usuels et leur mélange avec une autre langue dégrade leur lisibilité.

L'utilisation de clauses Use fait l'objet de sévères restrictions.

Les conventions de dénomination et certaines autres règles supposent que des clauses "use" ne sont pas utilisées.

Le projet traité est particulièrement volumineux.

Beaucoup de règles connaissent leur apport majeur dans les systèmes Ada de grande taille, bien qu'elles puissent aussi être utilisées dans un petit système, ne serait-ce que par souci de pratique et d'uniformité au niveau du projet ou de l'entreprise.

Le code source est développé sous l'environnement Rational.

En utilisant l'environnement Rational, les éléments problématiques comme l'agencement du code, les identificateurs dans les constructions fermantes, et ainsi de suite, sont pris en charge par l'éditeur et le programme de formatage d'Ada. Néanmoins, les recommandations d'agencement présentées dans ce document peuvent être appliquées à n'importe quelle plateforme de développement.

Le code suit une conception orientée objet

De nombreuses règles sont compatibles avec une mise en correspondance systématique de concepts orientés objets vers les fonctionnalités et conventions de dénomination spécifiques à Ada.

Classification des recommandations

Toutes ces recommandations n'ont pas une importance égale. Elles s'échelonnent approximativement comme suit :

Conseil : Icône en forme de doigt

La recommandation n'est qu'un simple conseil. Aucune conséquence négative ne s'ensuit si vous ne la suivez pas. Vous êtes totalement libre de l'adopter ou de la rejeter. Dans ce document, les conseils sont signalés par le symbole ci-dessus.

Recommandation :Icône OK

La recommandation se base généralement sur des considérations plus techniques ; la portabilité ou la réutilisabilité peuvent être affectées, ainsi que les performances sous certaines implémentations. Les recommandations devraient être suivies sauf si vous avez une bonne raison de ne pas le faire. Certaines exceptions sont mentionnées dans ce document. Les recommandations sont signalées dans ce document par le symbole ci-dessus.

Restriction :Icône en forme de main

L'utilisation de cette fonctionnalité comporte des risques mais n'est pas complètement proscrite ; la décision de l'utiliser ou non doit se faire au niveau du projet et avec toute la visibilité nécessaire. Les restrictions sont signalées dans ce document par le symbole présenté ci-dessus.

Exigence : Icône en forme de doigt

Une violation conduirait inévitablement à un code défectueux, non fiable ou non portable. Les exigences ne doivent pas être enfreintes. Elles sont signalées dans ce document par une main, comme ci-dessus.

L'utilitaire de conception de Rational sera utilisé pour dénoter l'utilisation de fonctionnalités sujettes à des restrictions et pour mettre en vigueur les règles requises et de nombreuses recommandations.

A la différence de nombreuses autres normes de rédaction de code, un très petit nombre de fonctionnalités Ada sont en fait totalement proscrites dans ces recommandations. La clé d'un bon logiciel réside dans :

Première et ultime recommandation

Icône en forme de doigtUtilisez votre bon sens.

Lorsque vous ne trouvez pas de règle ou de recommandation, ou que la règle, de toute évidence, ne s'applique pas, ou quand rien d'autre ne résout la question, utilisez le sens commun et vérifiez les principes fondamentaux. Cette règle prévaut sur toutes les autres. Le sens commun est indispensable.


Chapitre 3

Agencement du code

Généralités

L'agencement d'une unité de programme est sous le contrôle complet du composant de formatage Rational Environment Formatter et ne devrait guère préoccuper le programmeur, à l'exception des commentaires et des espaces vierges. Les conventions de formatage adoptées par cet outil sont celles reprises dans l'annexe E du document Reference Manual for the Ada Programming Language [ISO87]. Elles suggèrent, en particulier, d'aligner verticalement les mots clés commençant et terminant une construction structurée. De même, l'identificateur d'une construction doit être systématiquement répété à la fin de la construction.

Le comportement précis du composant de formatage est régi par une série de commutateurs de bibliothèque, qui reçoivent un jeu de valeurs uniformes à travers le projet, basé sur un monde modèle commun. Les commutateurs pertinents sont répertoriés ci-dessous avec leur valeur habituelle pour l'univers modélisé recommandé.

Casse des caractères

Format . Id_Case : Letter_Case := Capitalized

Stipule la casse des identificateurs dans les unités Ada : la toute première lettre et la première lettre après chaque caractère de soulignement sont en majuscules. La forme majuscule est reconnue comme la plus lisible par le lecteur humain, avec la majorité des écrans modernes et des polices d'imprimantes laser.

Format . Keyword_Case : Letter_Case := Lower

Stipule la casse des mots-clés Ada. Ceci les distingue légèrement des identificateurs.

Format . Number_Case : Letter_Case := Upper

Stipule la casse de la lettre "E" dans les littéraux à virgule flottante et chiffres basés ("A" à "F") dans les littéraux basés.

Indentation

Une unité Ada est mise en forme conformément aux conventions globales exprimées dans l'annexe E du document Ada Reference Manual [ISO87]. Ceci implique que les mots clés commençant et terminant une construction structurée sont alignés. Par exemple, "loop" et "end loop", "record" et "end record". Les éléments situés à l'intérieur de constructions structurés sont mis en retrait sur la droite.

Format . Major_Indentation : Indent_Range := 3

Stipule le nombre de colonnes de mise en retrait par le composant de formatage des constructions structurées (principales) telles que : instructions "if", "case" et "loop".

Format . Minor_Indentation : Indent_Range := 2

Stipule le nombre de colonnes de mise en retrait par le composant de formatage des constructions mineures : déclarations d'enregistrements, déclarations de variantes d'enregistrements, déclarations de types, gestionnaires d'exceptions, alternatives, instructions nommées et étiquetées.

Longueur de ligne et sauts de ligne

Format . Line_Length : Line_Range := 80

Stipule le nombre de colonnes utilisé par le composant de formatage pour l'impression de lignes dans les unités Ada avant un renvoi à la ligne. Ceci permet l'affichage d'unités mises en forme avec des terminaux traditionnels du type VT100.

Format . Statement_Indentation : Indent_Range := 3

Stipule le nombre de colonnes mises en retrait par le composant de formatage dans la seconde ligne et les suivantes d'une instruction lorsque celle doit être décomposée car excédant la valeur Line_Length. Le composant ne met la ligne en retrait d'après le nombre Statement_Indentation que s'il n'existe pas de construction lexicale avec laquelle puisse être aligné le code mis en retrait.

Format . Statement_Length : Line_Range := 35

Stipule le nombre de colonnes réservé sur chaque ligne pour l'affichage d'une instruction. Si le niveau de retrait actuel ne permet pas l'affichage sur une même ligne de toutes les colonnes définies dans Statement_Length, le composant de formatage utilise la valeur Wrap_Indentation comme nouveau niveau de mise en retrait. Cette pratique empêche d'imprimer au delà de la marge droite les instructions imbriquées en profondeur.

Format . Wrap_Indentation : Line_Range := 16

Stipule la colonne à laquelle le composant de formatage commence le niveau de retrait suivant lorsque le niveau actuel est insuffisant pour Statement_Length. Cette pratique empêche d'imprimer au delà de la marge droite les instructions imbriquées en profondeur.

Alignements

Format . Consistent_Breaking : Integer := 1

Contrôle la mise en forme des listes de la forme (xxx:aaa; yyy:bbb) apparaissant dans les sections formelles de sous-programmes et en tant que discriminants dans le déclarations de types. Contrôle également la mise en forme de celles du type (xxx=>aaa, yyy=>bbb) apparaissant dans les appels de sous-programmes et les agrégats. Comme cette option est autre que zéro (True), lorsqu'une liste n'entre pas sur une seule ligne, chaque élément de la liste débute sur une nouvelle ligne.

Format . Alignment_Threshold : Line_Range := 20

Stipule le nombre d'espaces vides pouvant être insérés par le composant de formatage afin d'aligner les constructions lexicales d'instructions consécutives (comme colonnes, affectations et flèches dans une notation nommée). Si ce nombre d'espaces est encore insuffisant, la construction n'est pas alignée.

Icône en forme de doigtNotez que s'il désire imposer un agencement particulier, le programmeur peut insérer une fin de ligne ou un saut de ligne, qui ne seront pas supprimés par le composant de formatage, en entrant <space> <space> <retour chariot>.

Icône OKSi vous utilisez cette technique, dans un souci de lisibilité et de maintenabilité, les listes d'éléments Ada doivent être décomposées de sorte à ne contenir qu'un seul élément par ligne lorsqu'elles en comportent plus de trois et ne peuvent entrer sur une seule ligne. Ceci s'applique notamment aux constructions Ada suivantes (telles que définies à l'annexe E du document Ada Reference Manual [ISO87]) :

Association argument

pragma Suppress (Range_Check,
                 On => This_Type,
                 On => That_Type,                 On => That_Other_Type);      

Liste d'identificateurs, liste de composants

Next_Position,
Previous_Position,
Current_Position : Position;
type Some_Record is 
    record
        A_Component,
        B_Component,
        C_Component : Component_Type;
    end record;      

Définition de type énumération

type Navaid is 
       (Vor, 
        Vor_Dme, 
        Dme, 
        Tacan, 
        VorTac, 
        NDB);      

Contrainte de discriminant

subtype Constrained is Element 
        (Name_Length    => Name'Length,
         Valid          => True,
         Operation      => Skip);      

Séquence d'instructions (réalisée par le composant de formatage)

Partie formelle, partie générique formelle, partie de paramètre effectif, partie de paramètre effectif générique

procedure Just_Do_It (This     : in Some_Type;
                      For_That : in Some Other_Type;
                      Status   : out Status_Type);
Just_Do_It (This     => This_Value;
            For_That => That_Value;
            Status   => The_Status);      

Chapitre 4

Commentaires

Généralités

Contrairement à des croyances répandues, les bons programmes ne se distinguent pas par le nombre de leurs commentaires, mais par leur qualité.

Les commentaires doivent être utilisés en tant que complément du code Ada , et non pas pour le paraphraser. Ada lui-même est un langage de programmation fort lisible, d'autant plus lorsqu'il est accompagné de conventions d'attribution de noms adéquates. Les commentaires doivent enrichir le code Ada en expliquant ce qui n'est pas évident ; ils ne doivent pas plagier la syntaxe ou la sémantique Ada. Ils doivent aider le lecteur à saisir les concepts de fond, les dépendances et les codes ou algorithmes particulièrement complexes. Ils doivent mettre en évidence les déviations des normes de codage ou de conception, l'utilisation de fonctions à usage restreint et de "stratagèmes" spéciaux. Les cadres de commentaire, ou formulaires, qui apparaissent systématiquement pour chaque construction Ada majeure (telles que les sous-programmes et les packages) offrent l'avantage de l'uniformité et de rappeler au programmeur de documenter son code mais conduisent souvent à un style le paraphrasant. Pour chaque commentaire, le programmeur doit pouvoir répondre de manière satisfaisante à la question : "Quelle est la valeur ajoutée de ce commentaire ?"

Un commentaire erroné ou induisant en erreur est pire que l'absence de commentaire. Les commentaires, sauf s'ils sont partie intégrante d'un langage formel ADL (Ada Design Language) ou PDL (Program Design Language), comme c'est le cas avec l'utilitaire Rational Design Facility, ne sont pas vérifiés par le compilateur. Par conséquent, selon le principe du point de maintenance unique, les décisions de conception doivent être exprimées en Ada et non pas par le biais de commentaires, même au prix de quelques déclarations supplémentaires.

Examinez l'exemple (à ne pas suivre) de déclaration suivante :

------------------------------------------------------------
-- procedure Create
------------------------------------------------------------
--
   procedure Create
              (The_Subscriber: in out Subscriber.Handle;
               With_Name     : in out Subscriber.Name);
--
-- Purpose: This procedure creates a subscriber with a given
-- name. 
--
-- Parameters: 
-     The_Subscriber    :mode in out, type Subscriber.Handle
-               It is the handle to the created subscriber
-     With_Name         :mode in, type Subscriber.Name
-               The name of the subscriber to be created.
-               The syntax of the name is
--                 <letter> { <letter> | <digit> }
-- Exceptions:
--    Subscriber.Collection_Overflow when there is no more
--    space to create a new subscriber
--    Subscriber.Invalid_Name when the name is blank or
--    malformed
--
-------------------------------------------- end Create ----      

Plusieurs points peuvent être relevés à propos de cet exemple :

- Procédure Create : si le nom doit être changé, il peut l'être à plusieurs endroits ; des changements cohérents du commentaire ne seront pas mis en vigueur par le compilateur.
- Les paramètres, avec leur nom, mode et type, n'ont pas besoin d'être répétés dans les commentaires.
- Des noms appropriés pour chaque entité Ada impliquée rendent redondants l'explication de son but et de ses paramètres. Notez que ceci est vrai s'il s'agit d'un sous-programme simple comme celui ci-dessus. Un sous-programme plus complexe nécessite néanmoins une explication de son but et paramètre.

Dans le cas ci-dessus, il serait préférable et plus utile d'utiliser cette version plus concise :

procedure Create (The_Subscriber : in out Subscriber.Handle;
                  With_Name      : in    Subscriber.Name);--
--Raises Subscriber.Collection_Overflow.
--Raises Subscriber.Invalid_Name when the name is 
--blank or malformed (see syntax description 
--attached to  declaration of type Subscriber.Name).      

Instructions pour l'utilisation de commentaires

Icône OKLes commentaires doivent être placés à proximité du code associé, avec le même retrait, et rattachés au code, c'est-à-dire avec une ou des lignes de commentaires vierges raccordant visuellement le bloc de commentaires à la construction Ada :

procedure First_One;
--
-- This comment relates to First_One.
-- But this comment is for Second_One.
-- 
procedure Second_One (Times : Natural);      

Icône OKUtilisez des lignes vierges pour séparer les blocs associés du code source (commentaires et code) plutôt que des lignes de commentaires lourdes.

Icône OKUtilisez des commentaires vides, plutôt que des lignes vides, dans un bloc de commentaires unique, pour séparer les paragraphes :

-- Some explanation here that needs to be continued in a
-- subsequent paragraph.
--
-- The empty comment line above makes it clear that we 
-- are dealing with a single comment block.      

Icône OKBien que des commentaires puissent être placés au dessus, ou au dessous, de la construction Ada à laquelle ils se réfèrent, placez ceux s'appliquant à plusieurs constructions Ada, comme un titre de section ou un élément d'information essentiel, au dessus de ces constructions. Placez les commentaires du type remarques ou informations complémentaires au dessous de la construction Ada à laquelle ils s'appliquent.

Icône OKRegroupez les commentaires au début de la construction Ada, en utilisant toute la largeur de la page. Evitez d'insérer des commentaires sur la même ligne qu'une construction Ada. Ces commentaires perdent souvent leur alignement. Ce type de commentaire est toutefois toléré dans les descriptions de chaque élément dans les déclarations longues (comme les littéraux de type énumératif).

Icône OKUtilisez un hiérarchie réduite de blocs standard de commentaires pour les titres de sections mais ce, uniquement dans les unités Ada de très grande taille (>200 déclarations ou instructions) :

--===========================================================
--
--               MAJOR TITLE HERE
--
--===========================================================


-------------------------------------------------------------
--               Minor Title Here
-------------------------------------------------------------


--             --------------------
--               Subsection Header
--             --------------------      

Placez plus de lignes vierges au dessus de tels commentaires de type titre qu'au-dessous (par exemple, deux lignes au dessus et une ligne après). Ceci associe visuellement le titre avec le texte qui le suit.

Icône OKEvitez d'utiliser des en-têtes contenant des informations comme auteur, numéros de téléphone, dates de création et de modification, et emplacement de l'unité (ou nom de fichier) car ces données se périment rapidement. Placez en fin d'unité les informations relatives aux droits de propriété et aux notices de copyright, particulièrement lorsque vous utilisez l'environnement Rational. Lors de l'accès au code source d'une spécification de package (par exemple, en cliquant sur [Definition] dans l'environnement Rational), l'utilisateur ne désire pas avoir à faire défiler deux ou trois pages de texte qui ne contribuent pas à sa compréhension du programme, et/ou de texte ne contenant aucune information de programmation (comme une notice de copyright). Evitez d'utiliser des barres verticales ou des cadres et des cases fermées car elles ne créent qu'une distraction visuelle et sont difficiles à maintenir cohérentes. Utilisez des notes Rational CMVC (ou une autre forme de fichiers de développement logiciel) pour conserver l'historique de l'unité.

Icône OKNe reproduisez pas des informations disponibles ailleurs mais proposez un pointeur vers ces informations.

Icône OKUtilisez Ada chaque fois que possible, au lieu d'un commentaire. Pour ce faire, vous pouvez utiliser des noms plus expressifs, des variables temporaires supplémentaires, des qualifications, des changements de noms, des sous-types, des expressions statiques et des attributs, aucun d'eux n'affectant le code généré (tout au moins avec un bon compilateur). Vous pouvez également utiliser des fonctions de prédicat en ligne et fractionner le code en plusieurs procédures dépourvues de paramètres dont le nom fournira le titre de plusieurs sections de code discrètes.

Exemples :

Remplacement :

exit when Su.Locate (Ch, Str) /= 0; 
-- Exit search loop when found it.      

Search_Loop : loop

Found_It := Su.Locate (Ch, Str) /= 0;

exit Search_Loop when Found_It

end Search_Loop;

Remplacement :

if Value < 'A' or else Value > 'Z' then 
-- If not in uppercase letters.      

subtype Uppercase_Letters is Character range 'A' .. 'Z';
if Value not in Uppercase_Letters then ...      

Remplacement :

X := Green;         -- This is the Green from 
                    -- Status, not from Color.
raise Fatal_Error;  -- Depuis Outer_Scope du package.
delay 384.0;        -- Equal to 6 minutes and 24 
                    -- seconds.      

The_Status := Green;      

X := Status'(Green);
raise Outer_Scope.Fatal_Error;
delay 6.0 * Minute + 24.0 * Second;      

Remplacement :

if Is_Valid (The_Table (Index).Descriptor(Rank).all) then
-- This is the current value for the iteration; if it is 
-- valid we append to the list it contains.
   Append (Item,           To_List => The_Table (Index).Descriptor(Rank).Ptr);|      

declare
    Current_Rank : Lists.List renames The_Table 
                    (Index).Descriptor (Rank);
begin
    if Is_Valid (Current_Rank.all) then
        Append (Item, To_List => Current_Rank.Ptr);
    end if;
end;      

Icône OKSoyez méticuleux avec le style, la syntaxe et l'orthographe des commentaires. N'utilisez pas un style télégraphique ou hermétique. Utilisez un correcteur orthographique (dans l'environnement Rational, appelez Speller.Check_Image).

Icône en forme de doigtN'utilisez pas de caractères accentués ou absents de l'alphabet anglais. Ces caractères peuvent être pris en charge par certains systèmes de développement et, dans les commentaires uniquement, par certains compilateurs Ada (Réf : Ada Issue AI-339). Mais ce support n'est pas portable et échouera probablement sur d'autres systèmes.

Icône OKPour les sous-programmes, documentez au minimum :

Icône OKPour les types et les objets, documentez toute non variante ou contrainte supplémentaire ne pouvant être exprimée en Ada.

Icône en forme de doigtEvitez les répétitions dans les commentaires. Par exemple, la section traitant de l'objet devrait être une réponse concise à la question "Que fait cette section ?" et non pas "Comment s'y prend-elle ?". La vue d'ensemble doit constituer une brève présentation de la conception. La description ne doit pas détailler les algorithmes utilisés mais, par contre, expliquer comment doit être utilisé le package.

La section Data_Structure et algorithme doit contenir suffisamment d'informations pour comprendre la stratégie d'implémentation principale (de sorte à utiliser correctement le package), mais n'a pas à soumettre tous les détails de l'implémentation ou des informations sans pertinence avec l'utilisation appropriée du package.


Chapitre 5

Conventions de dénomination

Généralités

Le choix de noms adéquats pour la désignation des entités Ada (unités de programme, types, sous-types, objets, littéraux, exceptions) est l'une des questions les plus délicates à résoudre par toutes les applications logicielles. Dans les applications de moyenne à grande taille, un autre problème émerge : les conflits entre noms, ou plutôt la difficulté de trouver suffisamment de synonymes pour designer des notions distinctes, mais toutefois similaires, relatives à un même concept du monde réel (ou pour nommer un type, un sous-type, un objet ou un paramètre). Vous pouvez exploiter ici la règle de non utilisation de clauses "use" (ou uniquement dans des conditions sous hautes restrictions). Dans de nombreuses situations, ceci permettra de raccourcir un nom et de réutiliser les mêmes termes descriptifs sans risque de confusion.

Icône OKChoisissez des noms clairs, lisibles et expressifs.

Contrairement à d'autres langages de programmation, Ada ne limite pas la longueur des identificateurs à 6, 8, ou 15 caractères. La vitesse de saisie n'est pas une justification acceptable pour des noms courts. Des identificateurs composés d'une seule lettre sont généralement signe de paresse ou d'un choix insatisfaisant. Il peut y avoir de rares exceptions, comme l'utilisation de E pour la base des logarithmes naturels, ou une poignée d'autres cas bien connus.

Icône OKSéparez les divers éléments d'un nom par un caractère de soulignement :

Is_Name_Valid au lieu de IsNameValid

Icône OKUtilisez le nom complet au lieu d'abréviations.

Icône OKUtilisez uniquement des abréviations validées pour le projet

Si des abréviations sont utilisées, elles doivent soit être répandues dans le domaine de l'application (par exemple, FFT pour Fast Fourier Transform), soit provenir d'une liste d'abréviations admises au niveau du projet. Sinon, il est fort probable que des abréviations similaires, mais non identiques, seront utilisées à l'occasion, provoquant par la suite des confusions et des erreurs (par exemple, Track_Identification abrégé en Tr_Id, Trck_Id, Tr_Iden, Trid, Tid, Tr_Ident, et ainsi de suite).

Icône OKUtilisez avec parcimonie les suffixes indiquant la catégorie de construction Ada. Ces suffixes n'améliorent pas la lisibilité.

Les suffixes par catégorie d'entités Ada, comme _Package pour packages, _Error pour exceptions, _Type pour type, et _Param pour paramètres de sous-programme ne sont généralement pas très efficaces dans le processus de lecture et de compréhension du code. Cette situation s'aggrave avec des suffixes comme _Array, _Record et _Function. Le compilateur Ada, tout comme le lecteur humain, peut distinguer une exception d'un sous-programme d'après le contexte : il est évident que seul un nom d'exception peut apparaître dans une instruction raise ou dans un gestionnaire d'exceptions. De tels suffixes peuvent être utiles dans les situations (limitées) suivantes :

Type formel générique suivi du suffixe _Constrained

Type d'accès suivi du suffixe _Pointer ou par une autre forme de référence indirecte : _Handle, ou _Reference

Sous-programme cachant un appel d'entrée potentiellement bloquant par _Or_Wait

Icône OKSélectionnez l'apparence des noms depuis une perspective d'utilisation.

Réfléchissez au contexte dans lequel sera utilisée une entité exportée et choisissez le nom en fonction de cette perspective. Une entité est déclarée une seule fois mais utilisée à maintes reprises. Ceci est particulièrement vrai pour les noms de sous-programmes et leurs paramètres : les appels qui s'ensuivent, utilisant des associations nommées, doivent se rapprocher le plus possible du langage naturel. Rappelez-vous que l'utilisation de clauses use impose le nom détaillé de la plupart des entités déclarées. Des compromis satisfaisants doivent être trouvés pour les paramètres formels génériques, lesquels peuvent être utilisés plus fréquemment dans l'unité générique que du côté client, mais privilégiez néanmoins l'apparence côté client pour les paramètres formels de sous-programme.

Icône OKUtilisez des termes anglais correctement orthographiés.

Un mélange de langues (par exemple, d'anglais et de français) rend difficile la lecture du code et génère éventuellement des ambiguïtés quant à la signification des identificateurs. Comme les mots clés d'Ada sont eux-mêmes en anglais, des termes anglais sont requis. Il est préférable d'utiliser l'orthographe américaine pour pouvoir exploiter le correcteur orthographique intégré à l'environnement Rational.

Icône en forme de doigtNe redéfinissez aucune entité du package Standard. Ceci est catégoriquement proscrit.

Ceci mène à des confusions et à des erreurs dramatiques. Cette règle peut être étendue pour inclure d'autres unités de bibliothèque prédéfinies : Calendar, System. Ceci inclut l'identificateur Standard lui-même.

Icône OKEvitez de redéfinir des identificateurs provenant d'autres packages prédéfinis (comme System ou Calendar).

Icône en forme de doigtN'utilisez pas d'identificateurs Wide_Character et Wide_String, destinés à être inclus dans le package Standard d'Ada 95. N'introduisez pas d'unité de compilation nommée Ada.

Icône en forme de doigtN'utilisez pas comme identificateurs les termes : abstract, aliased,protected, requeue, tagged et until, destinés à devenir des mots clés d'Ada 95.

Ci-dessous figurent quelques suggestions pour la dénomination de certaines entités Ada. Elles reposent sur l'hypothèse qu'un style de conception de type "objet" sera adopté. Pour plus d'explications, reportez-vous à l'annexe A.

packages

Icône OKLorsqu'un package introduit une classe d'objets, donnez-lui le nom de cette classe (en général, un nom commun singulier), suivi si nécessaire par le suffixe _Generic (c'est-à-dire, si une classe paramétrée est définie). N'utilisez le pluriel que si les objets se présentent invariablement en groupes. Par exemple :

package Text is
package Line is
package Mailbox is
package Message is
package Attributes is
package Subscriber is
package List_Generic is      

Icône OKLorsqu'un package spécifie une interface ou un regroupement de fonctionnalités sans relation à un objet, exprimez cet aspect dans son nom :

package Low_Layer_Interface is
package Math_Definitions is      

Icône OKLorsqu'un package "logique" doit être exprimé à travers plusieurs packages, en utilisant une décomposition uniforme, utilisez des suffixes émanant d'une liste convenue au niveau du projet. Le package logique Mailbox, par exemple, pourrait être implémenté avec :

package Mailbox_Definitions is
package Mailbox_Exceptions is
package Mailbox_Io is
package Mailbox_Utilities is
package Mailbox_Implementation is
package Mailbox_Main is      

D'autres suffixes acceptables sont mentionnés ci-dessous :

_Test_Support 
_Test_Main 
_Log 
_Hidden_Definitions 
_Maintenance 
_Debug      

Types

Icône OKDans un package définissant une classe d'objets, utilisez :

type Object is ...      

 

Lorsque la sémantique de copie est implicite, c'est-à-dire lorsque le type est instanciable et qu'une forme quelconque d'affectation est envisageable. Notez que le nom de la classe ne doit pas être répété dans l'identificateur puisqu'il sera toujours utilisé sous sa forme complète :

Mailbox.Object
Line.Object      

Icône en forme de doigtLorsqu'une sémantique partagée est implicite, c'est-à-dire que le type est implémenté avec des valeurs d'accès (ou une autre forme d'indirection), et que l'affectation, si elle est disponible, ne copie pas l'objet, indiquez ce fait en utilisant :

 

Les éléments sont utilisés comme suffixes lorsque leur utilisation isolée, avec le nom du package en préfixe, est obscure ou ambiguë.

Icône en forme de doigtLorsque plusieurs objets sont impliqués, utilisez l'une des formes suivantes :

Icône en forme de doigtPour une désignation de l'objet sous forme de chaîne, utilisez :

type Name

Icône OKLe nom qualifié du type doit aussi être utilisé tout au long du package de définition, pour une meilleure lisibilité. Dans l'environnement Rational, ceci engendre un comportement amélioré lors de l'utilisation de la fonction [Complete] sur un appel de sous-programme.

Notez, par exemple, le nom complet Subscriber.Object ci-après :

package Subscriber is
    type Object is private;
    type Handle is access Subscriber.Object;
    subtype Name is String;
    package List is new List_Generic (Subscriber.Handle);
    Master_List : Subscriber.List.Handle;
    procedure Create (The_Handle : out Subscriber.Handle;
                      With_Name  : in  Subscriber.Name);
    procedure Append (The_Subscriber : in     Subscriber.Handle;
                      To_List        : in out Subscriber.List.Handle);
    function Name_Of (The_Subscriber : Subscriber.Handle) return
            Subscriber.Name;
    ...
private
    type Object is
        record
            The_Name : Subscriber.Name (1..20);
                    ...
end Subscriber;    

Icône en forme de doigtDans les autres circonstances, utilisez des noms ou la combinaison qualificateur+nom pour nommer un type. Vous pouvez utiliser le pluriel pour le type, en conservant le singulier pour les objets (variables) :

type Point is record ...
type Hidden_Attributes is ( ...
type Boxes is array ...    

Pour les types énumératifs, utilisez Mode, Kind, Code, et ainsi de suite, seuls ou en tant que suffixes.

Pour les types tableau, le suffixe _Table peut être utilisé lorsque le nom simple est déjà utilisé pour le type de composant. N'utilisez des noms ou des suffixes comme like _Set et _List que lorsque le tableau est entretenu avec la sémantique implicite. Réservez _Vector et _Matrix pour les concepts mathématiques correspondants.

Icône en forme de doigtDans la mesure où les objets de tâche au singulier seront évités (pour des raisons expliquées ultérieurement), un type de tâche doit être introduit même lorsqu'il existe un seul objet de ce type. Ceci constitue un cas où une stratégie de suffixe rudimentaire comme _Type est satisfaisante :

task type Listener_Type is ...
for Listener_Type'Storage_Size use ...
Listener : Listener_Type;    

Icône en forme de doigtSimilairement, lorsqu'un conflit existe entre l'utilisation d'un nom (ou d'une phrase avec un nom) pour le nom du type, ou à plusieurs endroits entre le nom de l'objet ou du paramètre, apposez au nom le suffixe _Kind pour le type et conservez le nom simple pour l'objet :

type Status_Kind is (None, Normal, Urgent, Red);
Status : Status_Kind := None;    

Icône en forme de doigtOu, pour les éléments se présentant toujours comme des multiples, utilisez la forme plurielle pour le type.

Icône OKComme le type d'accès présente des dangers inhérents, l'utilisateur doit en être conscient. En règle générale, ils sont nommés Pointer. Utilisez le suffixe _pointer si le nom seul est ambigu. Alternativement, vous pouvez utiliser _Access. ;

Icône en forme de doigtL'attribution de nom est parfois simplifiée par l'utilisation d'un sous-package imbriqué pour introduire une abstraction secondaire :

package Subscriber is    ...
    package Status is
        type Kind is (Ok, Deleted, Incomplete, Suspended, 
                      Privileged);
        function Set (The_Status    : Subscriber.Status.Kind;
                      To_Subscriber : Subscriber.Handle);
    end Status;
    ...    

Exceptions

Icône OKComme des exceptions doivent être utilisées pour gérer les situations d'erreur, utilisez un nom ou une phrase nominale qui véhicule clairement une idée négative :

Overflow, Threshold_Exceeded, Bad_Initial_Value    

Lorsqu'il est défini dans un package de classe, il est inutile que l'identificateur contienne le nom de la classe (par exemple, Bad_Initial_Subscriber_Value) vu que l'exception sera toujours utilisée comme Subscriber.Bad_Initial_Value.

Icône OKUtilisez l'un des termes Bad, Incomplete, Invalid, Wrong, Missing, ou Illegal comme partie du nom plutôt que d'utiliser systématiquement le mot Error, qui n'intègre pas d'informations spécifiques :

Illegal_Data, Incomplete_Data    

Sous-programmes

Icône OKUtilisez des verbes pour les procédures (et entrées de tâches). Utilisez des noms avec les attributs ou les caractéristiques de la classe d'objets pour les fonctions. Utilisez des adjectifs (ou des participes passés) pour les fonctions retournant une valeur booléenne (prédicats). s

Subscriber.Create
Subscriber.Destroy
Subscriber.List.Append
Subscriber.First_Name          -- Returns a string.
Subscriber.Creation_Date       -- Renvoie une date.
Subscriber.List.Next
Subscriber.Deleted             -- Returns a Boolean.
Subscriber.Unavailable         -- Renvoie une valeur booléenne.
Subscriber.Remote    

Icône en forme de doigtIl peut être utile, pour les prédicats, d'ajouter dans certains cas le préfixe Is_ or Has_ avant le nom. Soyez alors précis et respectez la cohérence des temps :

function Has_First_Name ...
function Is_Administrator ...
function Is_First...
function Was_Deleted ...    

Cette méthode est utile lorsque le nom simple est déjà utilisé comme nom de type ou de littéral énumératif.

Utilisez la forme affirmative dans les prédicats (ne doivent pas contenir "Not_").

Pour les opérations usuelles, utilisez de manière cohérente des verbes tirés d'une liste de choix du projet (laquelle sera enrichie en progressant dans la connaissance du système) :

Create
Delete
Destroy
Initialize
Append
Revert
Commit
Show, Display    

Icône en forme de doigtUtilisez des noms positifs pour les fonctions de prédicats et les paramètres booléens. L'utilisation de noms négatifs peut engendrer des doubles négations (par ex., Not Is_Not_Found), et rendre plus ardue la lecture du code.

function Is_Not_Valid (...) return Boolean
procedure Find_Client (With_The_Name : in  Name;
                       Not_Found     : out Boolean)    

devrait être définie ainsi :

function Is_Valid (...) return Boolean;
procedure Find_Client (With_The_Name: in Name;
                       Found: out Boolean)    

ce qui permet au client d'utiliser alors la forme négative dans son expression (aucune pénalité ne s'ensuit en termes de délai d'exécution) :

if not Is_Valid (...) then ....    

Dans certains cas, un prédicat négatif peut être rendu positif, sans modification de la sémantique, en utilisant un antonyme, comme "Is_Invalid" au lieu de "Is_Not_Valid." Cependant, les noms positifs sont plus lisibles : "Is_Valid" est plus facile à comprendre que "not Is_Invalid."

Icône OKUtilisez le même terme lorsque le sens global est le même, plutôt que de rechercher des synonymes ou des variations. La surcharge est donc encouragée pour favoriser l'uniformité, conformément au principe de surprise minimale.

Icône en forme de doigtSi des sous-programmes sont utilisés comme "habillages" ou comme "encapsuleurs" d'appels d'entrées, il peut être judicieux que le nom évoque ce fait en annexant au verbe le suffixe _Or_Wait ou en utilisant une phrase comme Wait_For_ suivie d'un nom :

Subscriber.Get_Reply_Or_Wait
Subscriber.Wait_For_Reply    

Certaines opérations doivent être systématiquement définies en utilisant les mêmes noms :

Icône en forme de doigtPour les conversions de type en chaînes, ou à partir de celles-ci, les fonctions symétriques :

    fonction Image et fonction Value    

Icône en forme de doigtPour les conversions de type en et à partir d'une représentation de bas niveau (comme Byte_String pour l'échange de données) :

    procédure Read et Write    

Icône en forme de doigtPour les données attribuées :

    function Allocate (rather than Create)
    function Destroy (or Release, to express that the object will disappear)    

Lorsque ceci est fait systématiquement, en utilisant des noms cohérents, la composition de type est grandement facilitée.

Icône OKPour les itérateurs actifs, les primitives suivantes doivent toujours être définies :

Initialize
Next
Is_Done
Value_Of
Reset
. Si plusieurs types d'itérateurs sont introduits dans la même portée, ces primitives doivent être surchargées plutôt que d'introduire un jeu d'identificateurs distincts pour chaque itérateur. Voir : [BOO87].

Icône OKLors de l'utilisation d'attributs Ada prédéfinis comme noms de fonctions, veillez à ce qu'ils emploient la même sémantique générale : 'First, 'Last, 'Length, 'Image, 'Value, etc. Notez que plusieurs attributs (par ex., 'Range et 'Delta) ne peuvent pas être utilisés comme noms de fonctions car il s'agit de termes réservés.

Paramètres d'objets et de sous-programmes (ou d'entrées)

Icône OKPour signaler une unicité ou pour indiquer qu'une entité est le pôle principal d'une action, ajoutez au nom de l'objet ou du paramètre le préfixe The_ ou This_. Pour indiquer un objet annexe, temporaire ou auxiliaire, utilisez le préfixe A_ ou Current_ :

procedure Change_Name (The_Subscriber : in Subscriber.Handle;
                       The_Name       : in Subscriber.Name );
declare
    A_Subscriber : Subscriber.Handle := Subscriber.First;
begin
    ...
    A_Subscriber := Subscriber.Next (The_Subscriber);
end;    

Icône OKPour les objets booléens, utilisez une clause de prédicat avec la forme positive :

Found_It
Is_Available    

 

Is_Not_Available doit être évité.

Icône OKPour les objets tâches, utilisez un nom ou une phrase nominale impliquant une entité active :

Listener
Resource_Manager
Terminal_Driver    

Icône OKPour les paramètres, l'apposition d'une préposition comme préfixe du nom de la classe ou du nom caractéristique accroît la lisibilité, particulièrement du côté de l'appelant lorsqu'une association nommée est utilisée. D'autres préfixes utiles pour les paramètres auxiliaires prennent la forme Using_ ou, dans le cas d'un paramètre in out affecté par un effet secondaire, Modifying_:

procedure Update (The_List     : in out Subscriber.List.Handle;
                  With_Id      : in     Subscriber.Identification;
                  On_Structure : in out Structure;
                  For_Value    : in     Value);
procedure Change (The_Object   : in out Object;
                  Using_Object : in     Object);    

Icône OKL'ordre dans lequel les paramètres sont définis est également très important du point de vue de l'appelant :

Ceci permet de tirer profit des valeurs par défaut sans avoir à utiliser une association nommée pour les principaux paramètres.

Icône OKLe mode "in" doit être indiqué explicitement, même dans les fonctions.

Unités génériques

Icône OKChoisissez le meilleur nom que vous pourriez utiliser pour une version non générique : le nom de classe pour un package, ou un verbe transitif (ou une phrase verbale) pour une procédure (voir ci-dessus), en lui annexant le suffixe _Generic.

Icône OKPour les types génériques formels, lorsque le package générique définit une structure de données abstraite, utilisez Item ou Element pour la partie générique formelle et Structure, ou un autre nom plus approprié, pour l'abstraction exportée.

Icône OKPour les itérateurs passifs, utilisez un verbe comme Apply, Scan, Traverse, Process, ou Iterate dans l'identificateur :

generic
		with procedure Act	(Upon : in out Element);
procedure Iterate_Generic	(Upon : in out Structure);    

Icône OKLes noms de paramètres formels génériques ne peuvent pas être des homographes.

generic
    type Foo is private;
    type Bar is private;
    with function Image (X : Foo) return String;
    with function Image (X : Bar) return String;
package Some_Generic is ...    

sera remplacé par :

generic
    type Foo is private;
    type Bar is private;
    with function Foo_Image (X : Foo) return String;
    with function Bar_Image (X : Bar) return String;
package Some_Generic is ...    

En cas de besoin, les paramètres génériques formels peuvent être remplacés dans l'unité générique :

function Image (Item : Foo) return String Renames Foo_Image;
function Image (Item : Bar) return String Renames Bar_Image;    

Dénomination des stratégies pour les sous-systèmes

Lorsqu'un système volumineux est partitionné en sous-systèmes Rational (ou une autre forme de bibliothèques de programmes interconnectées), il est utile de définir une stratégie de dénomination permettant :

D'éviter les conflits de nom

Dans un système comprenant plusieurs centaines d'objets et de sous-objets, il est vraisemblable que des conflits de nom surviennent au niveau de l'unité de bibliothèque et les programmeurs risquent d'être à court de synonymes pour certains noms très utiles comme Utilities, Support, Definitions, etc.

De localiser facilement les entités Ada

Il est facile de déterminer à l'aide d'utilitaires de navigation sur l'hôte Rational l'endroit où est définie une entité, mais lors du portage du code vers une cible utilisant ses propres outils (débogueurs, outils de test, etc), la recherche de l'emplacement d'une procédure Utilities.Get parmi 2 000 unités sur 100 sous-systèmes peut être un défi de taille pour un nouveau venu dans le projet .

Icône OKAnnexez, en préfixe des noms d'unités au niveau de la bibliothèque, l'abréviation en quatre lettres du sous-système qui la contient.

La liste des sous-systèmes peut être consultée dans le document intitulé Software Architecture Document (SAD). Exemptez de cette règle les bibliothèques de composants hautement réutilisables, lesquelles seront probablement employées dans des projets, des produits COTS et des unités standard multiples.

Exemple :

Comm (Communication)

Dbms (Gestion de base de données)

Disp (Affichages)

Math (packages mathématiques)

Pilotes de périphérique d'unité

Par exemple, toutes les unités de bibliothèque exportées du sous-système Disp porteront le préfixe Disp_, ce qui laissera à l'équipe ou à l'entreprise en charge de Disp toute latitude dans le choix de leurs autres dénominations. Si tant DBMS que Disp doivent introduire une classe d'objets nommée Subscriber, il en résultera des packages comme :

Disp_Subscriber
Disp_Subscriber_Utilities
Disp_Subscriber_Defs
Dbms_Subscriber
Dbms_Subscriber_Interface
Dbms_Subscriber_Defs    

Chapitre 6

Déclarations de types, d'objets et d'unités de programmation

Icône OKLa puissante capacité de typage d'Ada sera utilisée pour prévenir le mélange de types différents. Des types différents sur le plan conceptuel doivent être réalisés en tant que types distincts définis par l'utilisateur. Des sous-types doivent être utilisés pour améliorer la lisibilité du programme et l'efficacité des vérifications opérées par le compilateur en phase d'exécution.

Types énumératifs

Icône OKChaque fois que possible, introduisez dans l'énumération une valeur littérale supplémentaire représentant une valeur non initialisée, non valide ou absente :

type Mode  is (Undefined, Circular, Sector, Impulse);
type Error is (None, Overflow, Invalid_Input_Value,Ill-formed_Name);    

Ceci se conformera aux règles d'initialisation systématique des objets. Placez ce littéral au début et non pas à la fin de la liste afin de faciliter la maintenance et de permettre des sous-intervalles contigus de valeurs valides comme :

subtype Actual_Error is Error range Overflow .. Error'Last;    

Types numériques

Icône OKEvitez d'utiliser des types numériques prédéfinis.

Quand l'objectif recherché est un haut degré de portabilité et de réutilisabilité, ou qu'un contrôle est requis sur l'espace mémoire occupé par des objets numériques, des types numériques prédéfinis (dans le package Standard) ne doivent pas être utilisés. La raison de cette exigence tient à ce que les caractéristiques des types prédéfinis Integer et Float sont laissées (délibérément) non spécifiées dans le document Reference Manual for the Ada Programming Language [ISO87].

Icône en forme de doigtUne première stratégie systématique consiste à introduire des types numériques spécifiques au projet, par exemple dans un System_Types du package, avec des noms indicateurs de la précision ou de la taille de la mémoire :

package System_Types is
        type Byte is range -128 .. 127;
        type Integer16 is range -32568 .. 32567;
        type Integer32 is range ...
        type Float6 is digits 6; 
        type Float13 is digits 13;
...
end System_Types;    

Icône en forme de doigtNe redéfinissez pas de types standard (types du package Standard).

Icône en forme de doigtNe spécifiez pas le type de base dont ils doivent être dérivés mais laissez le compilateur choisir. L'exemple suivant est à ne pas suivre :

type Byte is new Integer range -128 .. 127;    

Icône en forme de doigtFloat6 est un meilleur nom que Float32, même si sur la plupart des ordinateurs les types float de 32 bits atteignent six chiffres de précision.

Icône en forme de doigtDérivez dans les diverses parties du projet des types avec des noms plus significatifs que ceux dans Baty_System_Types. Certains des types les plus précis doivent être rendus privés afin de prendre en charge un port éventuel vers une cible avec prise en charge limitée de la précision.

Cette stratégie doit être utilisée lorsque :

Si tel n'est pas le cas, une stratégie plus simple consiste à toujours définir de nouveaux types, en spécifiant l'intervalle et la précision requises, mais en ne spécifiant jamais le type de base dont ils doivent dériver. Déclarez, par exemple :

type Counter is range 0 .. 100;
type Length is digits 5;    

Ce qui est préférable à la déclaration suivante :

type Counter is new Integer range 1..100; -- could be 64 bits
type Length is new Float digits 5; -- could be digits 13    

Cette seconde stratégie force le programmeur à réfléchir aux bornes et à la précision exactes requises par chaque type, plutôt que de sélectionner arbitrairement un certain nombre de bits. Soyez néanmoins conscient que si son intervalle n'est pas identique à celle d'un type de base, des vérifications d'intervalle systématiques seront effectuées par le compilateur (par exemple, pour le type Counter ci-dessus, pour déterminer si le type de base est un entier sur 32 bits).

Icône en forme de doigtSi les vérifications de l'intervalle deviennent un problème, une façon de les éviter consiste à déclarer :

type Counter_Min_Range is range 0 .. 10_000;
type Counter is range Counter_Min_Range'Base'First .. Counter_Min_Range'Base'Last;    

Icône OKEvitez que des types standard ne s'infiltrent dans le code via des constructions telles que des boucles, des intervalles d'index, etc.

Des sous-types de types numériques prédéfinis ne sont utilisés que dans les circonstances suivantes :

Exemple :

for I in 1 .. 100 loop ...     
-- I is of type Standard.Integer
type A is array (0 .. 15) of Boolean; 
-- index is Standard.Integer.    

Utilisez plutôt la forme : Some_Integer range L .. H

for I in Counter range 1 .. 100 loop ...
type A is array (Byte range 0 .. 15) of Boolean;    

Icône en forme de doigtN'essayez pas d'implémenter des types non signés.

Les types Integer avec arithmétique non signés n'existent pas dans Ada. D'après la définition du langage, tous les types integer sont dérivés, indirectement ou non, des types prédéfinis et ces derniers doivent à leur tour être symétriques autour de zéro.

Types réels

Icône OKEn vue de leur portabilité, n'utilisez que des types réels avec des valeurs comprises dans les intervalles :

[-F'Large .. -F'Small]  [0.0]  [F'Small .. F'Large]    

Notez que F'Last et F'First ne sont pas nécessairement des nombres modèles et peuvent même ne pas être inclus dans un intervalle modèle quelconque. La position relative de F'Last et de F'Large dépend de la définition du type et du matériel sous-jacent. Un exemple particulièrement problématique provient du cas où 'Last d'un type point fixe n'appartient pas au type, comme ci-dessous :

type FF is delta 1.0 range -8.0 .. 8.0;    

où, selon une lecture stricte du document Ada Reference Manual 3.5.9(6), FF'Last = 8.0 ne peut pas appartenir au type.

Pour représenter de grands ou de petits nombres réels, utilisez les attributs 'Large ou 'Small (et leur contreparties négatives), et non pas 'First et 'Last, comme vous le feriez pour des types integer.

Icône OKPour les types en virgule flottante, utilisez uniquement <= et >=, jamais =, <, >, /=.

La sémantique de comparaison absolue est mal définie (égalité de représentation et non pas égalité dans le degré de précision requis). Par exemple, X < Y peut ne pas déboucher sur le même résultat que : not (X >= Y). Des tests de l'égalité , A = B, doivent être exprimés ainsi :

abs (A - B) <= abs(A)*F'Epsilon    

Pour améliorer la portabilité et la maintenabilité, envisagez de fournir un opérateur Equal encapsulant l'expression ci-dessus.

Notez également que l'expression plus simple :

abs (A - B) <= F'Small    

n'est valide que pour les petites valeurs de A et de B, et, par conséquent, n'est pas généralement recommandée.

Icône OKEvitez toute référence à l'exception prédéfinie Numeric_Error. Une interprétation exécutoire du comité Ada a entraîné que tous les cas déclenchant auparavant l'erreur Numeric_Error soulèvent dorénavant l'erreur Constraint_Error. L'exception Numeric_Error est obsolète dans Ada 95.

Icône en forme de doigtSi l'erreur Numeric_Error est encore déclenchée par l'implémentation (comme c'est le cas avec le compilateur natif de Rational), recherchez toujours dans un gestionnaire d'exceptions une erreur Constraint_Error accompagnant Numeric_Error dans la même alternative :

when Numeric_Error | Constraint_Error => ...    

Icône en forme de doigtMéfiez-vous des dépassements négatifs.

Le dépassement négatif n'est pas détecté dans Ada. Le résultat est 0.0 et aucune exception n'est déclenchée. Notez qu'une vérification de dépassement négatif peut être obtenue explicitement en testant le résultat d'une multiplication ou d'une division par rapport à 0.0, lorsque aucun des opérandes n'est égal à 0.0. Notez aussi que vous pouvez implémenter vos propres opérateurs pour effectuer automatiquement une telle vérification, bien que cela nuise à l'efficacité.

Icône en forme de mainL'utilisation des types point fixe est soumise à des restrictions.

Utilisez dans la mesure du possible des types à virgule flottante. Une implémentation inégale de types point fixe dans une implémentation Ada provoque des problèmes de portabilité.

Icône en forme de doigtPour les types point fixe, 'Small doit être égal à 'Delta.

Le code doit le spécifier. Le fait que le choix par défaut pour 'Small soit une puissance de 2 entraîne toute sorte de problèmes. Une manière de rendre le choix clair est d'écrire :

Fx_Delta : constant := 0.01;
type FX is delta Fx_Delta range L .. H;
for FX'Small use Fx_Delta;    

Si des clauses de longueur pour les types point fixe ne sont pas prises en charge, l'unique moyen de se plier à cette règle est de spécifier explicitement un 'Delta qui soit une puissance de 2. Les sous-types peuvent avoir un 'Small différent de 'Delta (la règle s'applique uniquement à la définition du type (ou "premier sous-type nommé", pour employer la terminologie d'Ada Reference Manual).

Types article

Icône OKChaque fois que possible, fournissez des valeurs initiales simples et statiques pour les composants d'un type article (souvent des valeurs comme comme 'First ou 'Last peuvent être utilisés).

Mais n'appliquez pas cette règle à des discriminants. Les règles du langage sont telles que les discriminants aient toujours des valeurs. Des articles modifiables (à savoir, ceux avec des valeurs par défaut pour les discriminants) ne doivent être introduits que lorsque la mutabilité est une caractéristique souhaitée. Sinon, ils conduisent à une surcharge supplémentaire de l'espace mémoire (fréquemment, la variante la plus grande est allouée) et en temps (les vérifications de variantes sont plus complexes à effectuer).

Icône OKEvitez les appels de fonctions dans les valeurs initiales par défaut de composants car ils peuvent conduire à une erreur "accès avant élaboration" (voir "Considérations de structure du programme et de compilation").

Icône ne forme de doigtPour les articles modifiables (ceux dont les discriminants ont des valeurs par défaut), si un discriminant est utilisé dans le dimensionnement d'un autre composant, spécifiez-le de sorte à couvrir un intervalle de taille raisonnable.

Exemple :

type Record_Type (D : Integer := 0) is 
        record 
            S : String (1 .. D);
        end record;
A_Record : Record_Type;    

déclenchera probablement une erreur Storage_Error dans la plupart des implémentations. Spécifiez un intervalle plus raisonnable pour le sous-type du discriminant D.

Icône OKNe faites aucune présomption quant à l'agencement physique des articles.

En particulier, et à la différence d'autres langages de programmation, les composants ne sont pas nécessairement disposés dans l'ordre soumis dans la définition.

Types d'accès

Icône en forme de mainLimitez l'utilisation des types d'accès.

Ceci est particulièrement vrai pour les applications conçues pour s'exécuter sans interruption sur de petits ordinateurs sans mémoire virtuelle. Les types d'accès sont dangereux étant donné que de petites erreurs de programmation peuvent conduire à la saturation des ressources de stockage et, même si la programmation est de bonne qualité, à une fragmentation de la mémoire. Les types d'accès sont également plus lents. L'utilisation de types d'accès doit s'inscrire dans une stratégie plus vaste et les collections, leur taille et points d'allocation et de libération doivent être contrôlés. Pour aviser les clients d'une abstraction que les valeurs d'accès sont manipulées, le nom choisi doit le signaler : Pointer ou nom suivi du suffixe _Pointer.

Icône OKAllouez des collections lors de l'élaboration du programme et spécifiez systématiquement la taille de chacune d'entre elles.

La valeur donnée (en unités de mémoire) peut être statique ou calculée dynamiquement (par exemple, lue dans un fichier). La logique derrière cette règle tient à ce que le programme devrait échouer immédiatement au démarrage et non pas expirer de manière mystérieuse N jours plus tard. Les packages génériques peuvent y pourvoir avec un paramètre générique formel spécifiant la taille.

Notez que chaque objet alloué implique souvent une surcharge : il se peut que les fichiers d'exécution sur le système cible allouent des informations supplémentaires pour gestion interne avec chaque fragment mémoire. Ainsi, pour stocker N objets d'unités mémoire de taille M, il peut s'avérer nécessaire d'allouer plus que N * M unités mémoire pour la collection, par exemple : N * (M + K). Vous pouvez déterminer la valeur de surcharge K en consultant l'annexe F of [ISO87] ou en effectuant vos propres expériences.

Icône OKEncapsulez l'utilisation des allocateurs (primitive Ada new) et release. Si possible, gérez une liste interne libre au lieu de vous en remettre à Unchecked_Deallocation.

Si un type d'accès est utilisé afin d'implémenter une structure de données récursive, il est fort probable qu'il accédera à un type article avec (comme l'un de ses composants) le même type d'accès. Ceci permet le recyclage de cellules libres par leur chaînage dans une liste libre et sans surcharge supplémentaire de l'espace (à part le pointeur en tête de la liste).

Gérez explicitement les exceptions Storage_Error déclenchées par new, et réexportez une exception plus significative, venant indiquer la saturation de la taille mémoire de la collection.

Un point unique d'allocation et de libération facilite le suivi et le débogage en cas de problème.

Icône OKN'utilisez la libération que pour les cellules allouées de même taille (et donc les mêmes discriminants).

Ceci est important afin d'éviter la fragmentation de la mémoire. Il est improbable qu'Unchecked_Deallocation assure un service de compactage mémoire. Vérifiez si le système d'exécution assure la coalescence des blocs adjacents libérés.

Icône OKFournissez systématiquement une primitive Destroy (ou Free, ou Release) avec les types d'accès.

Ceci est tout particulièrement important pour les types de données abstraites implémentés avec des types accès et doit être réalisé systématiquement pour garantir une capacité de composition de types multiples de ce genre.

Icône OKLibérez systématiquement les objets.

Essayez de mapper les appels d'allocation et de libération pour vous assurer que toutes les données allouées soient libérées. Essayez de libérer les données dans la même portée où elles ont été allouées. Rappelez-vous de libérer également les données lorsque des exceptions sont déclenchées. Notez qu'il s'agit d'une circonstance propre à l'utilisation de l'alternative when others, en la terminant avec une instruction raise.

La stratégie de prédilection consiste à utiliser le canevas : Get-Use-Release. Le programmeur acquiert (Gets) les objets (ce qui crée une forme de structure de données dynamique), puis les utilise (Uses) et doit enfin les libérer (Release). Veillez à ce que ces trois opérations soient clairement identifiées dans le code et à ce que la libération soit appliquée lors de chaque instruction 'exit' du cadre, y compris en cas d'exception.

Icône en forme de doigtPrenez soin de libérer les structures de données composites temporaires pouvant être contenues dans des articles.

Exemple :

type Object is
record
    Field1: Some_Numeric;
    Field2: Some_String;
    Field3: Some_Unbounded_List;
end record;    

où 'Some_Unbounded_List' représente une structure liée composite, c'est-à-dire composée d'un certains nombre d'objets liés ensemble. Considérons maintenant une fonction attribut typique, rédigée ainsi :

function Some_Attribute_Of(The_Object: Object_Handle) return 
Boolean is Temp_Object: The_Object;
begin
    Temp_Object := Read(The_Object);
    return Temp_Object.Field1 < Some_Value;
end Some_Attribute_Of;    

La structure composite créée implicitement dans le segment mémoire lors de la lecture de l'objet dans Temp_Object n'est jamais libérée, mais ne peut plus être atteinte. Nous sommes en présence d'une fuite de mémoire. La solution appropriée consiste à utiliser un paradigme Get-Use-Release pour des structures gourmandes comme celle-ci. En d'autres termes, le client devrait d'abord acquérir l'objet, puis l'utiliser pour ses besoins et enfin le libérer (Get - Use - Release) :

procedure Get (The_Object  : out Object;
               With_Handle : in  Object_Handle);
function Some_Attribute_Of(The_Object : Object) 
                        return Some_Value;
function Other_Attribute_Of(The_Object	: Object) 
                        return Some_Value;
...
procedure Release(The_Object: in out Object);    

Le code client pourrait ressembler à ceci :

 declare
    My_Object: Object;
begin
    Get (My_Object, With_Handle => My_Handle);
    ...
    Do_Something
      (The_Value => Some_Attribute_Of(My_Object));
      ...
    Release(My_Object);
end;    

Types privés

Icône OKDéclarez des types comme private (privé) lorsqu'il est nécessaire de masquer les détails de l'implémentation.

Ces détails doivent être masqués via un type privé lorsque :

Dans l'environnement Rational, les types privés, utilisés de concert avec les parties et sous-systèmes fermés, réduisent notablement l'impact d'une modification éventuelle de la conception de l'interface.

Icône en forme de doigtContrairement à la programmation orientée objet "pure", n'utilisez pas de types privés lorsque le type complet correspondant constitue la meilleure abstraction possible. Soyez pragmatique : déterminez si un type privé apporterait quoi que ce soit.

Un vecteur mathématique, par exemple, est mieux représenté par un tableau, ou par un point sur le plan, que par un type privé :

type Vector is array (Positive range <>) of Float;
Type Point is 
    record
        X, Y : Float := Float'Large;
    end record;    

L'indexation de tableau, la sélection de composant d'article, et la notation d'agrégat seront bien plus lisibles (et, en fin de compte, plus efficaces) qu'une série d'appels de sous-programme, lesquels s'imposeraient si le type était, sans raison, privé.

Icône OKDéclarez les types privés comme limited (limités) lorsque l'affectation par défaut ou la comparaison des objets réels n'a pas de sens, n'est pas intuitive, ou n'est pas possible.

Ceci est le cas lorsque :

Icône OKUn type privé limité doit s'auto-initialiser.

Une déclaration d'objet de ce type doit recevoir une valeur initiale raisonnable car, en général, il ne sera pas envisageable de lui en affecter une autre ultérieurement sans risquer de déclencher une exception lors d'un appel de sous-programme.

Icône en forme de doigtLorsque la chose est possible et utile, fournissez une procédure Copy (ou Assign) et une procédure Destroy pour les types limités.

Icône en forme de doigtLors de la conception de types formels d'unité générique, spécifiez des types privés limités tant qu'une égalité ou une affectation n'est pas requise en interne, pour une meilleure ergonomie de l'unité correspondante.

En phase avec la règle précédente, envisagez alors d'importer des procédures formelles génériques Copy et Destroy et un prédicat Are_Equal, s'ils présentent une utilité.

Icône en forme de doigtPour les types privés formels génériques, indiquez dans la spécification si le type effectif doit être contraint ou non.

Vous pouvez y parvenir à l'aide d'une convention de dénomination et/ou d'un commentaire :

generic
    --Must be constrained.
    type Constrained_Element is limited private;
package ...    

Vous pouvez également utiliser le pragma Must_Be_Constrained, défini dans Rational :

generic
    type Element is limited private;
    pragma Must_Be_Constrained (Element);
package ...    

Types dérivés

Icône en forme de doigtRappelez-vous que le fait de dériver un type entraîne aussi la dérivation de tous les sous-programmes déclarés dans la même partie déclarative comme le type parent : les sous-programmes dérivables. Par conséquent, il ne sert à rien de les redéfinir tous comme des habillages dans la partie déclarative du type dérivé. Par contre, les sous-programmes génériques ne sont pas dérivables et il peut être nécessaire de les redéfinir comme des habillages.

Exemple :

package Base is
    type Foo is
        record
            ...
        end record;
    procedure Put(Item: Foo);
    function Value(Of_The_Image: String) return Foo;
end Base;
with Base;
package Client is
    type Bar is new Foo;
    -- At this point, the following declarations are 
    -- implicitly made:
    -- 
    -- function "="(L,R: Bar) return Boolean;
    -- 
    -- procedure Put(Item: bar);
    -- function Value(Of_The_Image: String) return Bar;
    -- 
end Client;    

Par conséquent, il n'est pas nécessaire de redéfinir ces opérations comme des habillages. Notez cependant que les sous-programmes génériques (tels que les itérateurs passifs) ne sont pas dérivés en même temps que ces opérations et doivent donc être réexportés en tant qu'habillages. Les sous-programmes définis ailleurs que dans la déclaration contenant le type de base ne sont pas non plus dérivables et doivent aussi être réexportés en tant qu'habillages.

Déclarations d'objets

Icône OKSpécifiez des valeurs initiales dans les déclarations d'objets, sauf si l'objet s'auto-initialise ou s'accompagne d'une valeur initiale par défaut implicite (par exemple, les types d'accès, les types tâche et les articles avec valeurs par défaut pour les zones sans discriminants).

La valeur affectée doit être réelle et significative (il ne doit pas s'agir d'une valeur quelconque). Si la valeur initiale réelle (par exemple, l'un des paramètres d'entrée) est disponible, affectez-la. S'il n'est pas possible de calculer une valeur significative, envisagez de déclarer l'objet plus tard ou affectez-lui une valeur "nil", si disponible.

Icône en forme de doigtLe nom "Nil" signifie ici "Non initialisé" et sert à déclarer des constantes pouvant être utilisées comme "valeur non utilisable mais connue" pouvant être rejetée de manière contrôlée par des algorithmes.

Autant que possible, la valeur Nil ne doit pas être utilisée dans un but autre que l'initialisation afin que son apparition puisse toujours signaler une erreur de variable non initialisée.

Notez qu'il n'est pas toujours possible de déclarer une valeur Nil pour tous les types, notamment les types modulaires (angle, par exemple). Choisissez dans ce cas la valeur la moins probable.

Icône en forme de doigtNotez que l'utilisation de code pour initialiser des articles volumineux peut être coûteuse, particulièrement si l'article connaît des variantes et qu'une valeur initiale n'est pas statique (ou, plus précisément, si la valeur ne peut pas être calculée lors de la compilation). Il est parfois plus efficace d'élaborer une fois pour toutes une valeur initiale (éventuellement dans le package définissant le type) et de l'affecter explicitement :

R : Some_Record := Initial_Value_For_Some_Record;    

Remarque :

L'expérience prouve que les variables non initialisées sont une des principales sources de difficultés lors du portage du code et l'une des causes majeures des erreurs de programmation. Cette situation est aggravée lorsque l'hôte de développement cherche à "faciliter la tâche" au programmeur en fournissant des valeurs par défaut pour au moins certains objets (par exemple le type Integer sur le compilateur Rational natif) ou lorsque le système cible met la mémoire à zéro avant le chargement du programme (par ex., sur un système DEC VAX). Pour garantir la portabilité, présumez toujours le pire.

Icône ne forme de doigtL'affectation d'une valeur initiale dans la déclaration peut être omise lorsqu'elle est coûteuse et qu'il est évident qu'une valeur sera affectée à l'objet avant son utilisation.

Exemple :

procedure Schmoldu is
    Temp : Some_Very_Complex_Record_Type;
 -- initialized later
begin
    loop
        Temp := Some_Expression ...
        ...    

Icône OKEvitez d'utiliser des valeurs littérales dans le code.

Utilisez des constantes (avec un type) quand la valeur définie est liée à un type. Sinon, utilisez des nombres nommés, particulièrement pour toutes les valeurs sans dimensionnalité (valeurs pures) :

Earth_Radius : constant Meter := 6366190.7;   -- En mètres.
Pi           : constant       := 3.141592653; -- Nbre d'unités.    

Icône ne forme de doigtDéfinissez les constantes associées avec des expressions universelles, statiques :

Bytes_Per_Page :   constant := 512;
Pages_Per_Buffer : constant := 10;
Buffer_Size :      constant := Bytes_Per_Page * Pages_Per_Buffer;
Pi_Over_2   :      constant := Pi / 2.0;    

Ceci tire profit du fait que ces expressions doivent être calculées exactement lors de la compilation.

Icône OKNe déclarez pas d'objets avec des types anonymes. Pour plus d'informations, reportez-vous au document Ada Reference Manual 3.3.1.

La maintenabilité est réduite, les objets ne peuvent pas être passés en tant que paramètres et ceci mène souvent à des erreurs dues à des conflits entre types.

Sous-programmes et unités génériques

Icône en forme de doigtLes sous-programmes peuvent être déclarés en tant que procédures ou fonctions. Vous pouvez vous reporter aux critères généraux ci-dessous pour déterminer quelle forme déclarer.

Déclarez une fonction lorsque :

Déclarez une procédure lorsque :

Icône OKEvitez d'attribuer des valeurs par défaut aux paramètres formels génériques utilisés pour le dimensionnement de structures (tables, collections, etc.)

Icône OKRédigez des procédures locales avec aussi peu d'effets secondaires que possible et des fonctions sans aucun de ces effets. Documentez les effets secondaires.

Les effets secondaires sont généralement des modifications de variables globales et ne peuvent être détectés qu'à la lecture du corps du sous-programme. Le programmeur peut ne pas être conscient de ces effets depuis le site d'appel.

La transmission des objets requis en tant que paramètres rend le code plus robuste, plus facile à comprendre et moins dépendant de son contenu.

Cette règle s'applique principalement aux sous-programmes locaux : les sous-programmes exportés requièrent souvent et de manière légitime un accès aux variables globales dans le corps du package.


Chapitre 7

Expressions et instructions

Expressions

Icône en forme de doigtUtilisez des parenthèses redondantes pour rendre plus claires les expressions composées.

Le niveau d'imbrication d'une expression est défini comme le nombre de paires de parenthèses imbriquées requis pour évaluer une expression de gauche à droite si les règles de priorité des opérateurs sont ignorées.

Icône ne forme de doigtLimitez à quatre le niveau d'imbrication des expressions.

Icône OKLes agrégats article doivent utiliser des associations nommées et être qualifiés :

Subscriber.Descriptor'(Name    => Subscriber.Null_Name,
                       Mailbox => Mailbox.Nil,
                       Status  => Subscriber.Unknown,
                   ...);    

Icône en forme de doigtL'utilisation de when others est proscrite pour les agrégats article.

La raison en est que, contrairement aux tableaux, les articles sont des structures naturellement hétérogènes et que, par conséquent, une affectation uniforme ne serait pas raisonnable.

Icône en forme de doigtUtilisez des expressions booléennes simples à la place d'instructions "if...then...else" pour les prédicats simples :

function Is_In_Range(The_Value: Value; The_Range: Range)
         return Boolean is 
         begin 
            if The_Value >= The_Range.Min and The_Value <= The_Range.Max then return True; 
            end if; 
         end Is_In_Range;

Il serait préférable d'utiliser la formule suivante :

function Is_In_Range(The_Value: Value; The_Range: Range)
     return Boolean is
begin
    return The_Value >= The_Range.Min 
        and The_Value <= The_Range.Max;
end Is_In_Range;    

Les expressions complexes contenant deux instructions 'if', ou plus, ne doivent pas être modifiées de cette manière si ceci affecte leur lisibilité.

Instructions

Icône OKLes instructions en boucle doivent porter un nom :

Forever: loop
   ...
end loop Forever;    

Icône OKLorsqu'une boucle a un nom, toute instruction 'exit' qu'elle contient doit le mentionner.

Icône OKLes boucles requérant un test d'achèvement à leur début doivent utiliser la forme de boucle "while". Celles qui réclament ce test à un autre endroit doivent utiliser la forme générale et une instruction 'exit'.

Icône ne forme de doigtMinimisez le nombre d'instructions 'exit' dans une boucle.

Icône OKDans une boucle "for" effectuant une itération sur un tableau, utilisez l'attribut 'Range appliqué à l'objet du tableau plutôt qu'un intervalle explicite, ou qu'un autre sous-type.

Icône e forme de doigtDéplacez hors de la boucle le code qui n'en est pas dépendant. Bien que la "traction de code" soit une optimisation habituelle du compilateur, vous ne devez pas y recourir lorsque le code de type invariant effectue des appels vers d'autres unités de compilation.

Exemple :

World_Search:
while not World.Is_At_End(World_Iterator) loop
    ...
    Country_Search:
    while not Nation.Is_At_End(Country_Iterator) loop
    declare
        City_Map: constant City.Map := City.Map_Of
            (The_City => Nation.City_Of(Country_Iterator),
             In_Atlas => World.Country_Of(World_Iterator).Atlas);
    begin
        ...    

Dans le code ci-dessus, l'appel à "World.Country_Of" est indépendant de la boucle (c.a.d que 'country' est inchangé dans la boucle interne). Cependant, dans la plupart des cas, le compilateur n'a pas le droit de déplacer l'appel hors de la boucle vu que l'appel peut induire des effets secondaires affectant l'exécution du programme. Le code s'exécute donc sans nécessité à chaque itération de la boucle.

La boucle est plus efficace et plus intelligible rédigée sous cette forme :

Country_Search:
while not World.Is_At_End(World_Iterator) loop
    declare
        This_Country_Atlas: constant Nation.Atlas 
            := World.Country_Of
                    (World_Iterator).Atlas;
    begin
        ...
        City_Search:
        while not Nation.Is_At_End (The_City_Iterator) loop
            declare
                City.Map_Of (
                    The_City => Nation.City_Of
                                        (Country_Iterator),
                    In_Atlas => This_Country_Atlas );
            begin
                ...    

Icône OKLes appels de sous-programmes et d'entrées doivent utiliser des associations nommées.

Cependant, s'il est clair que le premier (ou seul) paramètre est le pôle principal de l'opération (par exemple, l'objet direct d'un verbe transitif), le nom peut être omis pour ce paramètre uniquement :

Subscriber.Delete (The_Subscriber => Old_Subscriber);    

où Subscriber.Delete est le verbe transitif et Old_Subscriber l'objet direct. Les expressions suivantes dépourvues de l'association nommée The_Subscriber => Old_Subscriber sont acceptables :

Subscriber.Delete	(Old_Subscriber);
Subscriber.Delete (Old_Subscriber, 
                   Update_Database  => True,
                   Expunge_Name_Set => False);
if Is_Administrator (Old_Subscriber) then ...    

Dans certains cas, le sens des paramètres est à ce point évident qu'une association nommée nuirait à la lisibilité. Ceci est vrai, par exemple, lorsque tous les paramètres sont du même type et mode et ne comportent pas de valeurs par défaut :

if Is_Equal (X, Y) then ...
Swap (U, B);    

Icône OKUn when others ne doit pas être utilisé dans les instructions 'case' ou dans les définitions de type enregistrement (pour des variantes).

Le fait de ne pas utiliser de when others aide durant la phase de maintenance en rendant non valides ces constructions lorsque la définition du type discret est modifiée, forçant ainsi le programmeur à réfléchir aux mesures à prendre pour gérer cette modification. Il est cependant toléré lorsque le sélecteur est un intervalle d'entier de grande taille.

Icône en forme de doigtUtilisez une instruction 'case' plutôt qu'une série de "elsif" lorsque la condition de branchement est une valeur discrète.

Icône OKLes sous-programmes doivent avoir un seul point de retour.

Essayez de sortir des sous-programmes à la fin de la partie instruction. Les fonctions doivent avoir une seule instruction de retour. De telles fonctions, si elles sont disséminées inconsidérément à travers le corps d'une fonction ont un effet similaire à des instructions goto, rendant difficiles la lecture et la maintenance du code.

Les procédures ne doivent pas comporter d'instructions de retour.

Icône ne forme de doigtDes retours multiples ne peuvent être tolérés que pour de toutes petites fonctions où tous les retours peuvent être visualisés et lorsque la structure du code est très régulière :

function Get_Some_Attribute return Some_Type is
begin
    if Some_Condition then
        return This_Value;
    else
        return That_Other_Value;
    end if;
end Get_Some_Attribute;    

Icône en forme de mainL'utilisation d'instructions goto est soumise à des restrictions.

A la décharge des instructions "goto", il convient de noter que la syntaxe des libellés goto et leurs restrictions d'utilisation dans Ada les rendent moins dangereux qu'on pourrait le penser et que, dans de nombreux cas, ils sont préférables, plus lisibles et expressifs, que d'autres constructions équivalentes (un goto factice construit avec une exception, par exemple).

Conseils de codage

Icône en forme de doigtLors de la manipulation de tableaux, ne supposez pas que leur index débute à 1. Utilisez les attributs 'Last, 'First, 'Range.

Icône en forme de doigtDéfinissez le sous-type contraint le plus commun de vos types non contraints (principalement, articles) et utilisez ces sous-types pour les valeurs de paramètre et de retour afin de promouvoir l'auto-vérification dans le code client :

type Style is (Regular, Bold, Italic, Condensed);
type Font (Variety: Style) is ...
subtype Regular_Font is Font (Variety => Regular);
subtype Bold_Font is Font (Variety => Bold);
function Plain_Version (Of_The_Font: Font) return Regular_Font;
procedure Oblique (The_Text   : in out Text;
                   Using_Font : in     Italic_Font);
...    

Chapitre 8

Problèmes de visibilité

Surcharge et homographes

Il est recommandé de suivre les lignes directrices suivantes :

Icône OKSurchargez les sous-programmes.

Assurez-vous cependant, lorsque vous utilisez le même identificateur, qu'il implique véritablement le même type d'opération.

Icône OKEvitez de masquer les identificateurs d'homographe dans des portées imbriquées.

Ceci entraîne une confusion du lecteur et des risques potentiels pour la maintenance. Soyez également conscient de l'existence et de la portée des variables de contrôle de boucles "for".

Icône OKNe surchargez pas les opérations sur les sous-types mais toujours sur le type.

Contrairement à ce qu'un lecteur inexpérimenté pourrait croire, la surcharge s'appliquera au type de base et à tous ses sous-types.

Exemple :

subtype Table_Page is Syst.Natural16 range 0..10;
function "+"(Left, Right: Table_Page) return Table_Page;    

Le compilateur recherche le type de base et non pas le sous-type d'un paramètre lors de la mise en correspondance de sous-programmes. Par conséquent, dans l'exemple ci-dessus, "+" est en fait redéfini en all (toutes) valeurs Natural16 dans le package en cours, et non pas seulement Table_Page. Donc, toute expression "Natural16 + Natural16" serait alors mappée avec un appel de "+"(Table_Page, Table_Page), lequel retournerait probablement un résultat erroné ou une exception.

Clauses de contexte

Icône OKMinimisez le nombre de dépendances introduites par des clauses "with".

Lorsque la visibilité est approfondie par l'utilisation d'une clause "with", celle-ci doit couvrir une région de code aussi réduite que possible. N'utilisez une clause "with" que lorsqu'elle est nécessaire, dans l'idéal uniquement sur le corps ou même sur une souche de taille conséquente du corps.

Utilisez des packages d'interface pour réexporter des entités de bas niveau, évitant ainsi un usage visible de clauses "with" pour un grand nombre de packages de bas niveau. Pour ce faire, utilisez des types dérivés, des changements de noms, des sous-programmes d'habillage et éventuellement, des types prédéfinis tels que des chaînes (comme vous le feriez pour des packages de commande Environment).

Utilisez un couplage souple (faible) entre unités à l'aide de paramètres formels génériques, au lieu d'un couplage rigide (fort) à l'aide de clauses "with".

Exemple : pour exporter une procédure Put sur un type composite, importez en tant que formel générique une procédure Put pour ses composants, au lieu d'utiliser directement une clause with sur Text_Io.

Okay Icon Des clauses "Use" ne doivent pas être utilisées.

Le fait d'éviter l'utilisation, dans la mesure du possible, de clauses "use" améliore la clarté et la lisibilité, sous réserve que cette règle soit adéquatement couplée à des conventions de dénomination faisant un usage efficace du contexte et à une attribution adéquate de nouveaux noms (voir "Conventions de dénomination", plus haut). Il contribue aussi à éviter certaines surprises quant à la visibilité, particulièrement en phase de maintenance.

S'il s'agit d'un package définissant un type caractère, une clause "use" est nécessaire dans toute unité de compilation devant définir des littéraux chaîne basés sur ce type caractère :

package Internationalization is
    type Latin_1_Char is (..., 'A', 'B', 'C', ..., U_Umlaut, ...);
    type Latin_1_String is array (Positive range <>) of 
            Latin_1_Char;
end Internationalization ;
use Internationalization;
Hello : constant Latin_1_String := "Baba"    

L'absence de clause "use" empêche l'utilisation d'opérateurs sous forme 'infix'. Ces derniers peuvent être renommés dans l'unité client :

function "=" (X, Y : Subscriber.Id) return Boolean 
            renames Subscriber."=";
function "+" (X, Y :Base_Types.Angle) return Base_Types.Angle
            renames Base_Types."+";    

Icône OKEtant donné que l'absence d'une clause "use" conduit fréquemment à l'inclusion d'un même ensemble de réattributions de noms dans de nombreuses unités client, ces changements de noms peuvent être factorisés dans le package de définition lui-même par le biais d'un package Operations qui y sera imbriqué. Une clause "use" sur le package Operations est donc recommandée sur l'unité client :

package Pack is
    type Foo is range 1 .. 10;
    type Bar is private;
     ...
    package Operations is
        function "+" (X, Y : Pack.Foo) return Pack.Foo 
                renames Pack."+";
        function "=" (X, Y : Pack.Foo) return Boolean 
                renames Pack."=";
        function "=" (X, Y : Pack.Bar) return Boolean 
                renames Pack."=";
        ...
    end Operations;
private
	...
end Pack;
with Pack;
package body Client is
    use Pack.Operations; -- Makes ONLY Operations directly visible.
    ...
    A, B : Pack.Foo;    -- Still need prefix Pack.
    ...
    A := A + B ;        -- Note that "+" is directly 
                        -- visible.    

Le package Operations doit toujours porter ce nom et être placé en bas de la partie visible du package de définition. La clause "use" ne doit être placée que si elle est nécessaire (c.a.d., placée seulement dans le corps de Client si aucune opération n'est utilisée dans la spécification, ce qui est souvent le cas).

with Defs;
package Client is
    ...
    package Inner is
        use Defs;
        ...
    end Inner;		-- The scope of the use clause ends here.
    ...
end Client;
declare
    use Special_Utilities;
begin
    ...
end;                -- The scope of the use clause ends here.    

Changements de noms

Icône en forme de doigtUtilisez des conventions de changement de nom.

Le changement de nom est recommandé en conjonction avec la restriction sur l'utilisation de clauses "use" afin de rendre plus facile la lecture du code. Lorsque vous devez faire référence à de nombreuses reprises à une unité avec un nom très long, la lisibilité s'améliorera en fournissant pour celui-ci un nom court :

with Directory_Tools;
with String_Utilities;
with Text_Io;
package Example is
    package Dt renames Directory_Tools;
    package Su renames String_Utilities;
    package Tio renames Text_Io;
    package Dtn renames Directory_Tools.Naming;
    package Dto renames Directory_Tools.Object;
        ...    

Icône OKLe choix de noms courts doit être cohérent à travers tout le projet pour respecter le principe de surprise minimale. Pour ce faire, soumettez le nom court dans le package lui-même :

package With_A_Very_Long_Name is package Vln renames 
            With_A_Very_Long_Name;
    ...
end
with With_A_Very_Long_Name;
package Example is package Vln renames With_A_Very_Long_Name;
-- From here on Vln is an abbreviation.    

Notez que le changement de nom d'un package ne procure une visibilité accrue qu'à la partie visible du package renommé.

Icône OKLes changements de nom de packages importés doivent être regroupés au début de la partie déclarative et triés alphabétiquement.

Icône en forme de doigtLe changement de nom peut être utilisé localement là où il contribue à la lisibilité (aucune pénalité en terme de délai d'exécution ne s'ensuit). Les types peuvent être renommés en tant que sous-types sans restriction.

Comme illustré dans la section sur les commentaires, le changement de nom fournit souvent une méthode élégante et de maintenance facile pour documenter le code (par exemple, pour attribuer un nom simple à un objet complexe ou pour redéfinir localement la signification d'un type). La portée de l'identificateur de renommage doit être choisie afin d'éviter de générer des confusions.

Icône en forme de doigtLes exceptions de changement de nom permettent de factoriser les exceptions à travers plusieurs unités (par exemple, à travers toutes les instanciations d'un package générique). Notez que dans un package faisant dériver un type, les exceptions soulevées potentiellement par les sous-programmes dérivés doivent être réexportées de pair avec le type dérivé afin que les clients n'aient pas à recourir à des clauses "with" du package d'origine :

with Inner_Defs; 
package Exporter is ... 
procedure May_Raise_Exception; -- Raises exception Inner_Defs.Bad_Schmoldu when ... ... 
Bad_Schmoldu : exception renames Inner_Defs.Bad_Schmoldu; ...

Icône en forme de doigtLe changement de nom de sous-programmes avec des valeurs par défaut différentes pour les paramètres "in" peut permettre une factorisation simple du code et accroître la lisibilité :

procedure Alert (Message : String;
                 Beeps   : Natural);
procedure Bip (Message : String := "";
               Beeps   : Natural := 1) 
        renames Alert;
procedure Bip_Bip (Message : String := "";
                   Beeps   : Natural := 2) 
        renames Alert;
procedure Message (Message : String;
                   Beeps   : Natural := 0)
        renames Alert;
procedure Warning (Message : String;
                   Beeps   : Natural := 1)
        renames Alert;    

Icône OKEvitez d'utiliser le nom de l'entité renommée (l'ancien nom) dans la portée immédiate de la déclaration la renommant ; utilisez uniquement l'identificateur ou le symbole de l'opérateur introduit par cette déclaration (le nouveau nom).

Remarque à propos des clauses Use

La communauté Ada a connu pendant plusieurs années une controverse, frisant parfois la guerre ouverte, autour de la clause "use". Les deux camps ont avancé divers arguments souvent ne cadrant pas avec des projets à grande échelle ou des exemples trop éloignés de la réalité ou délibérément partisans.

Les tenants de la clause "use" soutiennent qu'elle améliore la lisibilité et citent des exemples de noms particulièrement illisibles, longs et redondants qui auraient tout avantage à être renommés s'ils doivent être utilisés à plusieurs reprises. Ils avancent aussi qu'un compilateur Ada peut résoudre une surcharge mais qu'un être humain plongé dans un programme Ada volumineux ne peut procéder à la résolution de la surcharge de manière aussi fiable qu'un compilateur, et, en tout état de cause, pas aussi vite. Ils soutiennent que les APSE sophistiqués, comme l'environnement Rational, rendent inutiles les noms complets explicites ; cela n'est pas exact : l'utilisateur ne devrait pas avoir à appuyer sur [Definition] pour chaque identificateur dont il n'est pas certain . Il ne devrait pas avoir à deviner mais, au contraire, pouvoir déterminer immédiatement quels sont les objets et abstractions utilisés. Les avocats de la clause "use" nient ses dangers potentiels pour la maintenance du programme et suggèrent qu'un un programmeur qui créerait de tels risques devrait être recalé d'office ; nous pensons que les noms qualifiés complets éliminent ce risque.

Si les méthodes suggérées plus haut pour alléger l'impact des restrictions d'utilisation de clauses "use" semblent requérir trop de saisie, réfléchissez à la conclusion de Norman H. Cohen : "Le temps ainsi gagné sur la saisie d'un programme sera maintes fois perdu en révision, débogage et maintenance."

Finalement, il a été prouvé que pour les grands systèmes, l'absence de clauses "use" se traduit par de meilleurs temps de compilation en réduisant la surcharge due à des recherches dans les tables de symboles.

Le lecteur intéressé par plus d'informations sur la controverse autour de la clause 'use' peut se reporter aux sources suivantes :

D. Bryan, "Dear Ada," Ada Letters, 7, 1, January-February 1987, pp. 25-28.

J. P. Rosen, "In Defense of the Use Clause," Ada Letters, 7, 7, November-December 1987, pp. 77-81.

G. O. Mendal, "Three Reasons to Avoid the Use Clause," Ada Letters, 8, 1, January-February 1988, pp. 52-57.

R. Racine, "Why the Use Clause Is Beneficial," Ada Letters, 8, 3, May-June 1988, pp. 123-127.

N. H. Cohen, Ada as a Second Language, McGraw-Hill (1986), pp. 361-362.

M. Gauthier, Ada-Un Apprentissage, Dunod-Informatique, Paris (1989), pp. 368-370.]


Chapitre 9

Considérations de structure du programme et de compilation

Décomposition de packages

Vous disposez de deux méthodes fondamentales pour décomposer un package "logique" volumineux, provenant de la phase de conception initiale, en unités de bibliothèque Ada plus petites qui seront plus faciles à gérer, à compiler, à maintenir et à comprendre :

a) La décomposition imbriquée

Cette approche met l'accent sur l'utilisation de sous-unités et/ou de sous-packages Ada. Les principaux sous-programmes, corps de tâches et corps de packages internes sont séparés systématiquement. Le processus est répété récursivement dans ces sous-unités et sous-packages.

b) La décomposition à plat

Le package logique est décomposé en un réseau de packages plus petits interconnectés par des clauses "with", et le package originel devient essentiellement un habillage pour la réexportation (ou un artefact n'existant même plus).

Chaque approche a ses atouts et ses inconvénients. La décomposition imbriquée requiert moins d'écriture de code et conduit à des noms plus simples (beaucoup d'identificateurs n'ont pas besoin de préfixe) ; et, tout au moins dans l'environnement Rational, la structure est clairement visible dans l'image de la bibliothèque et facile à transformer (commandes Ada.Make_Separate, Ada.Make_Inline). La décomposition à plat requiert souvent moins de recompilation et donne une structure meilleure ou plus propre (particulièrement aux bornes du sous-système) ; elle favorise également la réutilisation. Elle est aussi plus facile à gérer avec des outils de recompilation et de gestion de configuration automatisés. Cependant, la structure à plat comporte un risque plus élevé de transgression de la conception originale par l'utilisation de "with" sur certains des packages de bas niveau créés au cours de la décomposition.

Icône OKLe niveau d'imbrication doit être limité à 3 pour les sous-programmes et à 2 pour les packages ; n'imbriquez pas de packages au sein de sous-programmes.

package Level_1 is
    package Level_2 is
package body Level_1 is
    procedure Level_2 is
        procedure Level_3 is    

Icône en forme de doigtUtilisez des souches de corps pour les unités imbriquées ("corps séparés") dans les circonstances suivantes :

Structure des parties déclaratives

Spécification du package

Icône OKLa partie déclarative d'une spécification de package contient des déclarations devant être rangées dans l'ordre suivant :

1) Déclaration de changement de nom pour le package lui-même

2) Déclarations de changement de nom pour les entités importées

3) Clauses "Use"

4) Nombres nommés

5) Déclaration de types et de sous-types

6) Constantes

7) Déclarations d'exceptions

8) Spécifications de sous-programmes exportés

9) Packages imbriqués, le cas échéant

10) Partie privée

Icône en forme de doigtPour un package introduisant plusieurs types majeurs, il peut être préférable de disposer de plusieurs jeux de déclarations associées :

5) Déclarations de type et de sous-types pour A

6) Constantes

7) Déclarations d'exceptions

8) Spécifications de sous-programmes exportés pour les opérations sur A

9) Déclarations de type et de sous-types pour B

6) Constantes

7) Déclarations d'exceptions

8) Spécifications de sous-programmes exportés pour les opérations sur B

Etc.

Icône en forme de doigtLorsque la taille de la partie déclarative est importante (>100 lignes), utilisez de petits blocs de commentaires pour délimiter les diverses sections.

Corps du package

Icône OKLa partie déclarative du corps d'un package contient des déclarations devant être rangées dans l'ordre suivant :

1) Déclarations de changements de nom (pour les entités importées)

2) Clauses "Use"

3) Nombres nommés

4) Déclaration de types et de sous-types

5) Constantes

6) Déclarations d'exceptions

7) Spécifications de sous-programmes locaux

8) Corps de sous-programmes locaux

9) Corps de sous-programmes exportés

10) Corps de packages imbriqués, le cas échéant

Autres constructions

Icône OKLes autres parties déclaratives, comme dans les corps de sous-programmes, les corps de tâches et les instructions bloc suivent le même canevas général.

Clauses de contexte

Icône OKUtilisez une clause "with" par unité de bibliothèque importée. Triez les clauses 'with' par ordre alphabétique. Si une clause "use" sur une unité avec clause "with" est appropriée, elle doit dans ce cas suivre immédiatement la clause "with" correspondante. Voir ci-dessous le pragma Elaborate.

Ordre d'élaboration

Icône OKNe comptez pas sur l'ordre d'élaboration des unités de bibliothèque en vue d'obtenir un effet donné.

Chaque implémentation Ada est libre de choisir une stratégie pour le calcul de l'ordre d'élaboration, dans la mesure où elle répond aux règles très simples énoncées dans le document Ada Reference Manual [ISO87]. Certaines implémentations utilisent des stratégies plus intelligentes que d'autres (comme l'élaboration des corps aussitôt que possible après la spécification correspondante), et certaines ignorent ces préoccupations (particulièrement pour les instanciations génériques), ce qui conduit à de très graves problèmes de portabilité.

Trois causes principales sont à l'origine de l'erreur malheureusement bien connue "accès avant élaboration" lors de l'élaboration du programme (ce qui devrait normalement déclencher l'exception Program_Error) :

task type T;
type T_Ptr is access T;
SomeT : T_Ptr := new T; -- Access before elaboration.    

Icône OKPour éviter des problèmes lors du portage d'applications depuis un compilateur Ada vers un autre, le programmeur doit soit les éliminer en restructurant le code (ce qui n'est pas toujours possible), soit assumer explicitement le contrôle de l'ordre d'élaboration via le pragma Elaborate, à l'aide de la stratégie suivante :

Dans la clause de contexte d'une unité Q, un pragma Elaborate doit être appliqué à chaque unité P figurant dans une clause "with" :

Par ailleurs, si P exporte un type T tel que l'élaboration d'objets du type T appelle une fonction dans un package R, la clause de contexte de Q doit alors contenir :

with R;
pragma Elaborate (R);    

et ce, même s'il n'y a pas de références directes à R dans Q !

En pratique, il peut être plus facile (mais pas toujours possible) d'énoncer une règle comme quoi le package P doit inclure :

with R; 
pragma Elaborate (R);

Le package doit simplement porter :

with P;
pragma Elaborate (P);    

fournissant ainsi, par transitivité, le bon ordre d'élaboration.


Chapitre 10

Accès concurrent

Icône en forme de mainLimitez l'utilisation de tâches.

Les tâches sont une fonctionnalité particulièrement puissante mais leur usage est délicat. Une surcharge importante en termes de temps et d'espace peut découler d'une utilisation inappropriée de tâches. De toutes petites modifications dans une partie du système peuvent remettre totalement en cause la viabilité d'un jeu de tâches, et conduire à leur dépérissement et à des blocages. Le test et débogage de tâches est ardu. Par conséquent, l'utilisation des tâches, leur placement et leur interaction doit être une décision au niveau du projet. Les tâches ne doivent pas être utilisées de manière masquée, ou rédigées par des programmeurs inexpérimentés. Le modèle de tâche d'un programme Ada doit être rendu visible et compréhensible.

Sauf en cas de support performant de matériel parallèle, les tâches ne doivent être déployées que lorsque un accès concurrent est indispensable. Ceci est le cas pour l'expression d'actions tributaires du temps : activités périodiques ou introduction de délais d'attente, ou d'actions dépendantes d'un événement externe comme une interruption ou l'arrivée d'un message extérieur. Des tâches doivent aussi être introduites pour découpler d'autres activités, comme : mise en mémoire tampon, mise en file d'attente, distribution et synchronisation de l'accès à des ressources communes.

Icône OKSpécifiez la taille d'espace mémoire de la tâche avec une clause de longueur 'Storage_Size.

Pour les mêmes raisons et dans les mêmes circonstances ayant conduit à l'exigence de clauses de longueur pour les collections (section "Types accès" ci-dessus), la taille d'une tâche doit être spécifiée au cas où la mémoire est une ressource précieuse. Pour ce faire, déclarez toujours des tâches d'un type déclaré explicitement (vu que la clause de longueur ne peut être appliquée qu'à un type). Un appel de fonction peut être utilisé pour dimensionner dynamiquement la pile mémoire.

Remarque : il peut être très difficile de deviner la taille de pile requise par chaque tâche. Pour faciliter cette opération, le système d'exécution peut être instrumenté avec un mécanisme de "niveau d'alerte".

Icône OKUtilisez un gestionnaire d'exceptions dans le corps d'une tâche pour éviter, ou tout au moins signaler, l'arrêt inopiné de la tâche.

Les tâches qui ne gèrent pas les exceptions s'éteignent, habituellement en silence. Si possible, essayez d'obtenir un compte-rendu de la cause de leur extinction, particulièrement s'il s'agit de Storage_Error. Ceci permettra d'ajuster avec précision la taille de la pile. Notez que ceci requiert que l'allocation (primitive new) soit encapsulée dans un sous-programme réexportant une exception autre que Storage_Error.

Icône OKCréez les tâches lors de l'élaboration du programme.

Pour les mêmes raisons et dans les mêmes circonstances ayant conduit à l'exigence d'allocation des collections lors de l'élaboration du programme (section "Types accès" ci-dessus), toute la structure de tâches de l'application doit être créée rapidement au démarrage du programme. Il est préférable que le programme ne démarre pas du tout en raison de la saturation puisement de la mémoire plutôt que d'expirer quelques jours plus tard.

Les règles ultérieures font la distinction entre tâches de service et d'application. Les tâches de service sont des tâches de petite taille et simples du point de vue de l'algorithme et elles sont utilisées pour fournir un "adhésif" entre les tâches relatives à l'application. Comme exemple de ces taches (ou de tâches intermédiaires), on peut citer les mémoires tampons, les transporteurs, relais, agents, moniteurs, etc., qui fournissent habituellement les services de synchronisation, de découplage, de mise en mémoire tampon et d'attente. Les tâches d'application, comme leur nom l'indique, sont liées plus directement aux fonctions principales de l'application.

Icône OKEvitez les tâches hybrides : les tâches d'application doivent être conçues comme de purs appelants et les tâches de service, comme de purs appelés.

Une tâche appelée pure ne contient que des instructions 'accept' ou des attentes sélectives et aucun appel d'entrée.

Icône OKEvitez la circularité dans le graphe des appels d'entrée.

Ceci réduira considérablement le risque d'interblocages. Si elles ne peuvent être éliminées complètement, évitez tout au moins les circularités à l'état stationnaire du système. Ces deux règles facilitent aussi la compréhension de la structure.

Icône ne forme de mainLimitez l'utilisation de variables partagées.

Soyez particulièrement attentifs aux variables partagées masquées, c'est-à-dire de variables dissimulées dans le corps d'un package, par exemple, et auxquelles accèdent des primitives visibles par plusieurs tâches. Des variables partagées peuvent être utilisées dans les cas extrêmes pour la synchronisation de l'accès aux structures de données communes, quand le coût de rendez-vous est trop élevé. Vérifiez si le pragma Check est bien pris en charge.

Icône en forme de mainLimitez l'utilisation d'instructions 'abort'.

L'instruction 'abort' est reconnue universellement comme l'une des primitives les plus dangereuses et les plus nocives du langage. Son arrêt inconditionnel (et presque asynchrone) des tâches rend quasiment impossible toute tentative d'explication du comportement d'une structure de tâches donnée. Cependant, dans de très rares circonstances, une instruction 'abort' peut s'avérer nécessaire.

Exemple : Certains services de bas niveau sont fournis sans dispositif d'expiration de délai. L'unique moyen d'introduire un délai d'attente est de fournir ce service via une tâche d'agent auxiliaire, d'attendre (pendant le délai fixé) une réponse de l'agent et d'arrêter l'agent avec une instruction 'abort' si le service n'a pas été fourni dans le délai imparti.

Une instruction 'abort' est tolérable lorsque l'on peut prouver que seuls son émetteur et son destinataire peuvent être affectés par cette instruction (par exemple,quand aucune autre tâche ne peut appeler la tâche abandonnée).

Icône en forme de mainLimitez l'utilisation d'instructions 'delay'.

La suspension arbitraire d'une tâche peut conduire à de graves problèmes d'ordonnancement, lesquels seront difficiles à détecter et à corriger.

Icône en forme de mainLimitez l'utilisation des attributs 'Count, 'Terminated, et 'Callable.

L'attribut 'Count ne doit être utilisé que comme une indication approximative et les décisions d'ordonnancement ne doivent pas se fonder sur une valeur zéro, ou non, de l'attribut vu que le nombre effectif de tâches en attente peut fluctuer entre le moment où l'attribut est évalué et celui où la valeur est utilisée.

Utilisez des appels d'entrée conditionnels (ou la construction équivalente avec 'accept') pour une vérification crédible de l'absence de tâches en attente.

select
    The_Task.Some_Entry;
else
    -- do something else
end select;    

Ceci est préférable à :

if The_Task.Some_Entry'Count > 0 then
    The_Task.Some_Entry;
else
    -- do something else
end if;    

L'attribut 'Terminated est significatif uniquement s'il renvoie True et l'attribut 'Callable, s'il renvoie False, ce qui limite considérablement leur utilité. Ils ne doivent pas être utilisés pour assurer la une synchronisation entre les tâches lors de l'arrêt du système.

Icône en forme de mainLimitez l'utilisation de priorités.

Les priorités dans Ada ont un impact restreint sur l'ordonnancement. En particulier, la priorité respective des tâches en attente d'entrées n'est pas prise en compte dans l'ordre des files d'attentes d'entrée ou pour la sélection de l'entrée à présenter dans une attente sélective. Ceci peut conduire à une inversion des priorités (voir [GOO88]). Les priorités ne sont utilisées par le planificateur que pour sélectionner la prochaine tâche à exécuter parmi celles prêtes pour exécution. En raison du risque d'inversion des priorités, ne comptez pas sur celles-ci pour les aspects d'exclusion mutuelle.

Il est possible, par l'utilisation de familles d'entrées, de fractionner la file d'attente d'entrées en plusieurs sous-files et, de la sorte, introduire un concept explicite d'urgence.

Si des priorités ne sont pas nécessaires, n'en affectez pas du tout aux tâches.

Icône OKUne fois qu'une priorité a été affectée à une tâche, affectez des priorités à toutes les tâches de l'application.

Cette règle est nécessaire car les priorités de tâches sans un pragma Priority sont indéfinies.

Icône OKPour la portabilité du programme, utilisez seulement un petit nombre de niveaux de priorité.

L'intervalle du sous-type System.Priority est défini par l'implémentation et l'expérience prouve que l'intervalle effectif disponible varie énormément d'un système à l'autre. Par ailleurs, il est judicieux de définir les priorités de manière centralisée, en leur attribuant des noms et des définitions, au lieu d'utiliser des littéraux entiers dans toutes les tâches. La disponibilité d'un tel package System_Priorities centralisé facilite la portabilité et, de pair avec la règle précédente, permet de localiser sans difficulté toutes les spécifications de tâches.

Icône en forme de doigtPour éviter une dérive des tâches cycliques, programmez l'instruction 'delay' de sorte à tenir compte du temps de traitement, des surcharges et de la préemption des tâches :

Next_Time := Calendar.Clock;
loop
    -- Do the job.
    Next_Time := Next_Time + Period;
    delay Next_Time - Clock;
end loop;    

Notez que Next_Time - Clock peut recevoir une valeur négative, ce qui indique un retard dans la tâche cyclique. Il peut être possible de sauter un cycle.

Icône en forme de doigtPour garantir la capacité d'ordonnancement, affectez des priorités aux tâches cycliques conformes à l'algorithme RMSA (Rate Monotonic Scheduling Algorithm), c.a.d., la plus haute priorité allant à la tâche la plus fréquente. Pour plus d'informations, voir [SHA90] .

Icône en forme de doigtAffectez une priorité plus élevée aux serveurs intermédiaires très rapides : moniteurs, mémoires tampon.

Assurez-vous cependant que ces serveurs ne se bloquent pas eux-mêmes par 'rendez-vous' avec d'autres tâches. Documentez cette priorité dans le code afin qu'elle soit respectée lors de la maintenance du programme.

Icône en forme de doigtPour minimiser l'effet d'"instabilité", utilisez des échantillons en entrée et des données en sortie avec enregistrement des informations de date plutôt que de la période elle-même.

Icône OKEvitez les formulations occupé - attente (interrogation).

Veillez à ce que les tâches attendent avec des appels select ou des appels d'entrées, ou soient différées, plutôt que de chercher sans trêve quelque chose à s'occuper.

Icône en forme de doigtPour chaque rendez-vous, assurez-vous qu'au moins un côté est en position d'attente et que seul un côté comporte un appel d'entrée conditionnel ou à délai programmé, ou patiente.

Sinon, particulièrement dans les boucles, vous courrez le risque que le code s'exécute au pas de course, avec un résultat semblable à une situation occupé - attente. Ceci peut être aggravé par un usage inconsidéré des priorités.

Icône en forme de doigtLors de l'encapsulation de tâches, veillez à conserver une haute visibilité de certaines de leurs caractéristiques.

Si des appels d'entrées sont cachés dans des sous-programmes, assurez-vous que le lecteur de la spécification de ces sous-programmes soit conscient que l'appel au sous-programme puisse se bloquer. De plus, spécifiez si l'attente est bornée et, dans ce cas, fournissez une estimation de la borne supérieure. Utilisez une convention de dénomination pour indiquer l'attente potentielle (section "Sous-programmes", plus haut).

Si l'élaboration d'un package, l'appel d'un sous-programme ou l'instanciation d'une unité générique active une tâche, rendez ce fait bien visible au client :

package Mailbox_Io is
    -- This package elaborates an internal Control task
    -- that synchronizes all access to the external 
    -- mailbox 
    procedure Read_Or_Wait
        (Name: Mailbox.Name; Mbox: in out Mailbox.Object);
        --
        -- Blocking (unbounded wait).    

Icône OKNe comptez pas sur un ordre spécifique pour la sélection des entrées dans une attente sélective.

Si un degré d'équité est requis pour la sélection de tâches mises en file d'attente dans des entrées, vous pouvez l'obtenir en introduisant les files sans attente dans l'ordre voulu, puis laisser en attente les autres entrées. N'utilisez pas 'Count.

Icône OKNe comptez pas sur un ordre d'activation spécifique pour les tâches élaborées dans une même partie déclarative.

Si une séquence de démarrage spécifique est souhaitée, vous pouvez y parvenir via un mécanisme de rendez-vous avec des entrées de démarrage spéciales.

Icône OKImplémentez les tâches afin qu'elles s'achèvent normalement.

Sauf si la nature de l'application requiert que les tâches, une fois activées, s'exécutent indéfiniment, elles doivent se terminer après achèvement normal de leur opération ou via une alternative 'terminate'. Ceci peut être impossible à réaliser pour les tâches dont le maître est une bibliothèque de haut niveau, vu que le document Ada Reference Manual ne précise pas dans quelles conditions elles devraient se terminer.

Si la structure dépendante d'un maître ne permet pas un arrêt en bonne et due forme, les tâches doivent être compatibles avec des entrées de fermeture spéciales qu'elles attendront (appelées à la fermeture du système).


Chapitre 11

Traitement des erreurs et des exceptions

L'approche globale est de n'utiliser des exceptions que pour les erreurs : erreurs dans la logique ou dans la programmation, erreurs de configuration, données endommagées, saturation des ressources, etc. La règle générale suppose que les systèmes, dans leur condition normale et en l'absence de surcharge ou de panne matérielle, ne devraient déclencher aucune exception.

Icône en forme de doigtUtilisez des exceptions pour gérer les erreurs de logique et de programmation, les erreurs de configuration, les données endommagées et la saturation des ressources. Rendez compte des exceptions par le mécanisme de journalisation approprié le plus rapidement possible, y compris à l'emplacement où elles sont déclenchées.

Icône en forme de doigtMinimisez le nombre d'exceptions exportées depuis une abstraction donnée.

Dans les systèmes de grande taille, la présence d'un grand nombre d'exceptions à gérer complique la lecture et la maintenance du code. Parfois le traitement des exceptions est sans commune mesure avec celui des autres activités.

Plusieurs moyens sont disponibles pour minimiser le nombre des exceptions :

Icône OKNe propagez pas les exceptions non spécifiées dans la conception.

Icône OKEvitez d'utiliser une alternative when others dans les gestionnaires d'exceptions, sauf si l'exception interceptée est déclenchée à nouveau.

Ceci permet une maintenance locale sans interférence avec les exceptions ne pouvant être gérées à ce niveau :

exception
    when others => 
        if Io.Is_Open (Local_File) then
            Io.Close (Local_File);
        end if;
        raise;
end;    

Vous pouvez également positionner une alternative when others au bas du corps d'une tâche.

Icône OKN'utilisez pas d'exceptions pour les événements fréquents et anticipés.

L'utilisation d'exceptions pour représenter des conditions qui ne sont pas manifestement des erreurs présente plusieurs inconvénients :

Par exemple, n'utilisez pas une exception comme une forme de valeur supplémentaire renvoyée par une fonction (comme Value_Not_Found dans une recherche) ; utilisez une procédure avec un paramètre "out", ou instaurez une valeur spéciale signifiant Not_Found (introuvable), ou englobez le type retourné dans un article avec un discriminant Not_Found.

Icône OKN'utilisez pas d'exceptions pour implémenter des structures de contrôle.

Ceci constitue un cas particulier de la règle précédente : les exceptions de doivent pas être utilisées comme une forme d'instruction "goto".

Icône en forme de doigtLors de l'interception d'exceptions prédéfinies, placez le gestionnaire dans un tout petit cadre encerclant la construction qui la déclenche.

Les exceptions prédéfinies comme Constraint_Error, Storage_Error, etc., peuvent survenir à beaucoup d'endroits. Si une d'entre-elles doit être interceptée pour une raison spécifique, la portée du gestionnaire doit être réduite au strict minimum :

begin
    Ptr := new Subscriber.Object;
exception
    when Storage_Error => 
        raise Subscriber.Collection_Overflow;
end;    

Icône OKArrêtez les gestionnaires d'exceptions placés dans des fonctions avec une instruction "return" ou "raise". sinon, l'exception Program_Error sera déclenchée dans l'appelant.

Icône en forme de mainLimitez la suppression des vérifications.

Avec les compilateurs Ada actuels, le gain potentiel en termes de performances et de réduction de la taille du code consécutif à une suppression de vérifications est devenu marginal. Par conséquent, la suppression de vérifications doit être restreinte à des tronçons de code très réduits ayant été identifiés (mesures à l'appui) comme des goulots d'étranglement des performances et ne doit jamais être appliqué à grande échelle à un système entier .

En corollaire, n'ajoutez pas de vérification supplémentaire explicite d'intervalles et de discriminants au cas, improbable, où quelqu'un déciderait plus tard de supprimer les vérifications. Faites confiance aux utilitaires de vérification de contraintes intégrés dans Ada.

Icône en forme de doigtNe propagez pas les exceptions au delà de la portée de leur déclaration.

Ceci rendrait impossible au code client de gérer explicitement l'exception, si ce n'est avec une alternative when others qui pourrait ne pas être suffisamment spécifique.

Corollaire à cette règle : lors de la réexportation d'un type par dérivation, pensez à réexporter les exceptions déclenchées par les sous-programmes dérivés (en les renommant, par exemple). Sinon, les clients devront utiliser une clause "with" sur le package de définition original.

Icône en forme de doigtGérez toujours de pair Numeric_Error et Constraint_Error.

Le comité Ada a décidé que toutes les circonstances ayant déclenché Numeric_Error devront désormais soulever à la place l'exception Constraint_Error.

Icône en forme de doigtVérifiez que les codes d'état ont une valeur appropriée.

Lors de l'utilisation du code d'état retourné par des sous-programmes comme un paramètre "out", vérifiez toujours qu'une valeur est affectée au paramètre "out" en le positionnant comme première instruction exécutable dans le corps du sous-programme. Attribuez systématiquement par défaut à l'état la valeur succès ou la valeur échec. Envisagez toutes les sorties possibles du sous-programme, y compris les gestionnaires d'exceptions.

Icône en forme de doigtProcédez localement à des contrôles de sécurité ; ne vous attendez pas à ce que le client s'en charge.

Spécifiquement, si un sous-programme peut générer un résultat erroné sans données d'entrée correctes, installez un code dans le sous-programme pour détecter et signaler les entrées non valides de manière contrôlée. Ne vous reposez pas sur un commentaire avisant le client de passer des valeurs correctes. Il est quasiment certain que, tôt ou tard, ce commentaire sera ignoré, provoquant alors des erreurs difficiles à déboguer si les paramètres incorrects ne sont pas détectés.

Pour plus d'informations, voir [KR90b].


Chapitre 12

Programmation de bas niveau

Cette section traite de fonctionnalités Ada non portables à priori. Celles-ci sont définies au chapitre 13 du Reference Manual for the Ada Programming Language [ISO87], et les fonctionnalités spécifiques au compilateur sont décrites à l'"Annexe F" fournie par les fournisseurs du compilateur Ada.

Clauses de représentation et attributs

Icône en forme de doigtEtudiez soigneusement l'annexe F du document Ada Reference Manual (et procédez à des expérimentations de petite envergure pour vous assurer d'avoir bien compris).

Icône en forme de mainLimitez l'utilisation de clauses de représentation.

Les clauses de représentation ne sont pas prises en charge uniformément d'une implémentation à l'autre. Leur utilisation cache de nombreux pièges. Par conséquent, elles ne doivent pas être utilisées librement dans un système.

Les clauses de représentation doivent :

Les clauses de représentation peuvent être évitées dans les types de situations suivants :

Exemple :

Remplacement :

type Foo is (Bla, Bli, Blu, Blo);
for Foo use (Bla => 1, Bli =>3, Blu => 4, Blo => 5);    

type Foo is (Invalid_0, Bla, Invalid_2, Bli, Blu, Blo);    

Icône OKRegroupez les types comportant des clauses de représentation dans des packages clairement identifiés comme contenant du code dépendant de l'implémentation.

Icône OKNe supposez jamais un ordre spécifique dans l'agencement d'un article.

Icône OKDans une clause de représentation d'article, spécifiez toujours le positionnement de tous les discriminants, et ceci, avant de spécifier un composant quelconque dans les variantes.

Icône OKEvitez les clauses d'alignement.

Vous pouvez vous fier au compilateur qui connaît les contraintes d'alignement de la cible. L'utilisation de ces clauses par le programmeur peut conduire ultérieurement à des conflits d'alignement.

Icône en forme de doigtSoyez conscient de l'existence de zones générées par le compilateur dans les types composites non contraints :

dans les articles : décalage de zones dynamiques, index de clause variante, bit contraint, etc.

dans les tableaux : vecteurs 'dope'.

Reportez-vous à l'annexe F du compilateur pour de plus amples informations. Ne vous fiez pas au contenu du chapitre 13 du document Ada Reference Manual [ISO87].

Conversions sans vérification

Icône en forme de mainLimitez l'utilisation d'Unchecked_Conversion.

Le niveau de support d'Unchecked_Conversion varie fortement d'un compilateur Ada à l'autre et son comportement précis peut être légèrement différent, notamment en ce qui concerne les types composites et les types accès.

Icône OKDans une instanciation d'Unchecked_Conversion,assurez vous que tant les types source que les types cible sont contraints et ont la même taille.

Ceci constitue l'unique moyen d'assurer une portabilité limitée et d'éviter d'être confronté à des problèmes quant aux informations ajoutées par l'implémentation, comme les vecteurs 'dope'. Un moyen de s'assurer que les deux types sont de la même taille est de les "emballer" dans un type article avec une clause de représentation d'article.

Une méthode pour rendre le type contraint consiste à effectuer l'instanciation dans une fonction d'"habillage", où la contrainte est calculée au préalable.

Icône en forme de doigtN'appliquez pas Unchecked_Conversion à des valeurs d'accès ou à des tâches.

Non seulement ceci n'est pas pris en charge par tous les systèmes (par exemple, le compilateur Rational natif), mais vous ne pouvez pas non plus supposer que :


Chapitre 13

Résumé

Les éléments les plus importants à surveiller sont résumés ci-après :

Fonctionnalités avec restrictions d'usage (Icône en forme de main):

"Interdictions" absolues (Icône en forme de doigt)


Références

Ce document dérive directement de Ada Guidelines: Recommendations for Designer and Programmers, Application Note #15, Rev. 1.1, Rational, Santa Clara, Ca., 1990. [KR90a]. Cependant, de nombreuses sources différentes ont contribué à son élaboration.

BAR88 B. Bardin & Ch. Thompson, "Composable Ada Software Components and the Re-export Paradigm", Ada Letters, VIII, 1, Jan.-Feb. 1988, p.58-79.

BOO87 E. G. Booch, Software Components with Ada, Benjamin/Cummings (1987)

BOO91 Grady Booch: Object-Oriented Design with Applications, Benjamin-Cummings Pub. Co., Redwood City, California, 1991, 580p.

BRY87 D. Bryan, "Dear Ada," Ada Letters, 7, 1, January-February 1987, pp. 25-28.

COH86 N. H. Cohen, Ada as a Second Language, McGraw-Hill (1986), pp. 361-362.

EHR89 D. H. Ehrenfried, Tips for the Use of the Ada Language, Application Note #1, Rational, Santa Clara, Ca., 1987.

GAU89 M. Gauthier, Ada-Un Apprentissage, Dunod-Informatique, Paris (1989), pp. 368-370.

GOO88John B. Goodenough and Lui Sha: "The Priority Ceiling Protocol," special issue of Ada Letters, Vol., Fall 1988, pp. 20-31.

HIR92 M. Hirasuna, "Using Inheritance and Polymorphism with Ada in Government Sponsored Contracts", Ada Letters, XII, 2, March/April 1992, p.43-56.

ISO87 Reference Manual for the Ada Programming Language, International Standard ISO 8652:1987.

KR90a Ph. Kruchten, Ada Guidelines: Recommendations for Designer and Programmers, Application Note #15, Rev. 1.1, Rational, Santa Clara, Ca., 1990.

KR90b Ph. Kruchten, "Error-Handling in Large, Object-Based Ada Systems," Ada Letters, Vol. X, No. 7, (Sept. 1990), pp. 91-103.

MCO93 Steve McConnell, Code Complete-A Practical Handbook of Software Construction, Microsoft® Press, Redmond, WA, 1993, 857p.

MEN88 G. O. Mendal, "Three Reasons to Avoid the Use Clause," Ada Letters, 8, 1, January-February 1988, pp. 52-57.

PER88 E. Perez, "Simulating Inheritance with Ada", Ada letters, VIII, 5, Sept.-Oct. 1988, p. 37-46.

PLO92 E. Ploedereder, "How to program in Ada 9X, Using Ada 83", Ada Letters, XII, 6, November 1992, pp. 50-58.

RAC88 R. Racine, "Why the Use Clause Is Beneficial," Ada Letters, 8, 3, May-June 1988, pp. 123-127.

RAD85 T. P. Bowen, G. B. Wigle & J. T. Tsai, Specification of Software Quality Attributes, Boeing Aerospace Company, Rome Air Development Center, Technical Report RADC-TR-85-37 (3 volumes).

ROS87 J. P. Rosen, "In Defense of the Use Clause," Ada Letters, 7, 7, November-December 1987, pp. 77-81.

SEI72 E. Seidewitz, "Object-Oriented Programming with Mixins in Ada", Ada Letters, XII, 2, March/April 1992, p.57-61.

SHA90 Lui Sha and John B. Goodenough: "Real-Time Scheduling Theory and Ada," Computer, Vol. 23, #4 (April 1990), pp. 53-62.)

SPC89 Software Productivity Consortium: Ada Quality and Style-Guidelines for the Professional Programmer, Van Nostrand Reinhold (1989)

TAY92 W. Taylor, Ada 9X Compatibility Guide, Version 0.4, Transition Technology Ltd., Cwmbran, Gwent, U.K., Nov. 1992.

WIC89 B. Wichman: Insecurities in the Ada Programming Language, Report DITC137/89, National Physical Laboratory (UK), January 1989.


Glossaire

La plupart des termes utilisés dans ce document sont définis à l'annexe D du Reference Manual for the Ada Programming Language, [ISO87]. Des termes supplémentaires sont définis ici :

ADL (Ada as a Design Language) Ada en tant que langage de conception ; fait référence à la manière dont Ada est utilisé pour exprimer les aspects d'une conception ; également dénommé PDL (Program Design Language) c.a.d. Langage de conception de programme.

Environnement : L'environnement de développement logiciel Ada en cours d'utilisation.

Commutateur de bibliothèque : Dans l'environnement Rational, option de compilation s'appliquant à toute une bibliothèque de programmes.

Monde modèle : Dans l'environnement Rational, bibliothèque spécialisée utilisée pour mémoriser des paramètres de commutateur de bibliothèque uniformes et au niveau du projet entier.

Mutable : Propriété d'un article dont les discriminants comportent des valeurs par défaut ; un objet de type mutable peut se voir affecter n'importe quelle valeur du type, même celles susceptibles de modifier ses discriminants, et donc sa structure.

Habillage : Sous-programme dont le corps agit uniquement en tant que relais. Idéalement, ne devrait contenir qu'une seule instruction : appel à un autre sous-programme avec un jeu de paramètres identiques ou paramètres convertibles en ceux ou depuis ceux du paramètre.

PDL (Program Design Language) : Langage de conception de programme.