Enseigner Ada, pourquoi ?, à qui ?, comment ? Choisir un langage : entre le tentant et le raisonnable !

Daniel Feneuille

À l'initiative de l'association Ada-France, ce texte est une réflexion sur les qualités pédagogiques (mais aussi professionnelles) du langage de programmation Ada normalisé en 1983, mais surtout, joliment réactualisé en 1995 ce qui lui a redonné une longueur d'avance avec les plus actuels. Les arguments développés dans ces lignes sont ceux de praticiens francophones (pédagogues ou professionnels) convaincus de leur choix Ada et qui, parfois, sont affligés de constater que l'on enseigne, plus volontiers, la programmation avec un langage que l'on maîtrise mieux, et/ou avec ce qui est plus « à la mode » ; éternel choix entre le sérieux et … le tentant ! Quand on enseigne l'informatique (sauf à viser le développement de petits modules sans lendemain) on ne peut faire l'impasse sur la mise en pratique des préceptes du génie logiciel (souvent cités dans les cursus, mais, hélas, pas toujours mis en application). La démarche génie logiciel implique de faire le choix d'enseigner, notamment à travers le langage, la qualité : c'est-à-dire en quelques mots faire : propre, rigoureux, fiable, lisible, portable, maintenable, réutilisable, extensible (donc modulaire et, si nécessaire, objet), et si possible (ce qui n'est pas négligeable) enseigner gratuit. Dans le cas où l'on souhaite toutes ces propriétés en même temps les langages candidats se font rares. Ce document, créé, avant tout, dans un but pédagogique, ne prétend pas présenter le langage Ada. Des pistes pour cela seront proposées (sites, cours, livres, didacticiel, etc.) voir le chapitre les aides et les outils (dans le plan ci-dessous). Ce texte s'adresse bien sûr aux enseignants informaticiens (même débutants), mais surtout aux décideurs pédagogiques (directeurs d'enseignement, prescripteurs de formation dans l'industrie et la recherche). Les praticiens du langage Ada convaincus de ses qualités n'y verront que des évidences, mais ils peuvent à tout moment réagir pour améliorer ou amender nos argumentations (contacts). ♪

- Titre : Enseigner Ada, pourquoi ?, à qui ?, comment ? Choisir un langage : entre le tentant et le raisonnable !

- Auteur : Daniel Feneuille

- Parution : 6 décembre 2003

- Mise à jour : 17 avril 2006

- Licence : Copyright ® 2003 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Le génie logiciel

- III. Les concepts bien illustrés

- III-1. Typage

- III-2. Encapsulation

- III-3. « Spécifications » et réalisation

- III-4. Généricité

- III-5. Conception hiérarchique

- III-6. Exceptions

- III-7. Approche modulaire

- III-8. Classes et objets

- III-9. Programmation d'activités concurrentes

- IV. Pour quels apprenants ?

- IV-1. Non-informaticiens

- IV-2. DEUG

- IV-3. IUT (Bac + 2)

- IV-4. Bac + 4

- IV-5. Ingénieurs et BAC + 5

- IV-6. Professionnels

- IV-7. CNAM

- IV-8. Suisse (Bac + 3)

- V. Les aides et les outils

- V-1. Les compilateurs

- V-2. Les environnements de développement (I.D.E.)

- V-3. Internet et Ada

- V-4. Les Bases de Données et Ada

- V-5. Méthodes et Ada

- V-6. Des bibliothèques

- V-7. Interfaçage avec d'autres langages

- V-8. Traducteurs avec d'autres langages

- V-9. Livres, cours, didacticiel

- VI. Des adresses d'établissements

- VII. Conclusion

- VIII. Annexes (des exemples et des compléments)

- VIII-1. Un exemple de typage

- VIII-2. Travail au niveau du bit

- VIII-3. Compléments interfaçage avec C.

- VIII-4. Exemples de lisibilité

- VIII-5. Exemple simple de paquetage

- VIII-6. Exemple de paquetage hiérarchique

- VIII-7. un exemple de classe

- VIII-8. Deux exemples de programmation concurrente

- VIII-9. Un exemple de programmation distribuée

- IX. Remerciements

I - Introduction

Le langage Ada a été conçu à l'initiative du D.O.D. (le département de la défense des USA) pour fédérer plus de 400 langages ou dialectes utilisés par cet organisme dans les années 1970 ! Des réunions et des documents de comités d'experts vont se succéder de 1975 à 1983 (appel d'offres, cahier des charges, choix, élection des projets et norme) pour présenter un langage généraliste, unificateur, normalisé et supportant les préceptes du génie logiciel : Ada83. Les premiers compilateurs (1985) sont onéreux, mais … respectent la norme ! (norme devenue internationale en 1987). Ada commence à faire ses preuves de fiabilité, de robustesse, mais, a des défauts de jeunesse. De 1990 à 1995 la révision de la norme aboutit à Ada95, qui corrige les petits défauts, comble un gros manque en rendant le langage complètement objet (Ada est le premier langage objet normalisé). Ada95 ajoute ainsi son lot de nouveautés toujours inédites 10 ans après. Une nouvelle révision est en cours (nommée Ada0Y ; Y = 5 donc sûrement pour 2005) ce qui montre la vitalité et la pérennité du langage.

Aujourd'hui (en 2003) Ada ne nous semble pas avoir la place qu'il mérite notamment dans les premiers apprentissages de l'informatique où l'on doit mêler la programmation elle même avec la bonne pratique de la programmation. Depuis longtemps Ada peut largement remplacer Pascal l'excellent langage pédagogique. Paradoxalement on trouve Ada plus volontiers enseigné dans des formations de très haut niveau, car, là encore, il permet d'enseigner de façon claire, cette fois des concepts qualifiés de plus ardus.

Faut-il enseigner (comme c'est trop le cas) les langages les plus récents ou les plus répandus (C/C++ notamment, voire Java pour être plus clairs), même si ces derniers semblent peu adaptés à l'apprentissage d'une bonne pratique ? Si la réponse est affirmative, et sous forme de boutade, il faut sacrer TF1 meilleure chaîne de télévision française, puisqu'elle est la plus regardée (ou encore si j'osais Windows le meilleur S.E. puisque le plus utilisé!). La modernité n'est pas un objectif pédagogique. Notons, comme contre exemple, que Java ne dispose toujours pas de la généricité (certes annoncée dans la version 1.5, mais déjà opérationnelle depuis 20 ans avec Ada83). La généricité est un concept essentiel pour la réutilisation.

En général les détracteurs d'Ada (enseignants ou professionnels) l'ont peu ou mal utilisé et confessent, volontiers, s'être arrêtés dans leurs tests, irrités par la rigueur du compilateur. Rigueur à laquelle ils ne sont pas habitués et qui reste (nous le montrerons), justement, la qualité essentielle pour un développement fiable.

Ada est encore bien utilisé (voire incontournable) en avionique et en informatique embarquée (fusée Ariane par exemple) ainsi que pour le contrôle de trafic (aérien, ferroviaire) où la fiabilité est cruciale. Il est aussi apprécié quand le code à développer est conséquent (donc très difficile à maintenir). Mais il n'en reste pas moins qu'actuellement peu de petites ou moyennes entreprises admettent utiliser Ada. Ou alors (comme le signale J.P. Rosen) ces entreprises cachent l'utilisation d'Ada comme on garde un secret de fabrication ! En effet, même pour des applications plus modestes, les gains de productivité avec Ada sont prouvés et très significatifs.

Les enseignants en informatique soucieux de faire développer du code de qualité pourront utiliser Ada qui est l'aboutissement des langages procéduraux, Ada, longtemps réservé aux applications de très haute technologie, est aujourd'hui enfin accessible aux moins fortunés. Un compilateur gratuit, ouvert et portable (GNAT) permet (notamment aux universitaires et aux autodidactes) de s'y frotter et … d'adopter le langage pour une formation (ou une culture) informatique de qualité (voir dans le chapitre Les aides et les outils : les compilateurs).

II - Le génie logiciel

Dès les années 1970 on parle, déjà, de la crise du logiciel (et, aujourd'hui, le problème est récurrent et même encore plus préoccupant) : coût de développement dépassé ou difficile à prévoir, délais non respectés, livraison non conforme aux spécifications, programmes trop gourmands, peu fiables, modifications souvent impossibles, etc. Passons en revue, succinctement, quelques critères ou propriétés que doit satisfaire une application conséquente et de qualité. Le décor ainsi planté permettra de mettre en évidence dans le chapitre suivant les points forts du langage Ada satisfaisant aux objectifs soulevés.

La réutilisabilité : c'est l'aptitude d'un logiciel à être repris, en partie ou même en totalité, pour développer de nouvelles applications. On parle alors volontiers de composants logiciels à l'instar des composants que l'on réutilise en électronique.

L'extensibilité : c'est la facilité d'adaptation d'un logiciel aux changements de spécifications, évolution de la structure de données ou ajout de fonctionnalités sont souhaitables. Ceci impliquant des modifications rapides et fiables permettant une maintenance adaptative sûre.

La portabilité : c'est la possibilité pour un produit logiciel de ne pas dépendre d'un environnement matériel, ni d'un système, ni d'un compilateur particulier, c'est particulièrement important pour des applications numériques. La portabilité facilite le transfert d'une configuration matériel et / ou d'un système logiciel à un autre.

La testabilité : c'est la mise en œuvre de processus agressifs dont le but est de trouver des erreurs dans un logiciel. Cette phase du développement d'un logiciel est capitale, mais souvent négligée ou bâclée. Cette étape se prépare avant l'implémentation, car on construit les plans de tests avant le codage (pendant les étapes de conception).

La maintenabilité : c'est l'aptitude d'un logiciel à être modifié élégamment, rapidement, sans remettre en cause fondamentalement la structure déjà spécifiée ni les applications existantes. Maintenance corrective cela s'entend, mais aussi maintenance adaptative.

La lisibilité : c'est la propriété d'un code accessible et compréhensible rapidement par un plus grand nombre de développeurs. La verbosité d'un langage parfois décriée peut devenir une qualité. À titre d'anecdote, on pourrait montrer le traditionnel « Bonjour le Monde » à des non spécialistes, dans différents langages, la version Ada est la plus lisible (même comparée à Pascal !). Deux autres exemples ?

La facilité de certification et de validation : c'est l'aptitude d'un logiciel à pouvoir être associé à des propriétés prouvant qu'il respecte ses spécifications, qu'il se termine correctement ou qu'il ne se bloque pas dans des situations de saturation de charge ou de manque de ressource. L'existence d'un langage normalisé et de possibilités de vérifications statiques contribuent à satisfaire cet objectif.

III - Les concepts bien illustrés

Ce chapitre est un survol rapide de quelques points forts du langage Ada qui permettent de discipliner le processus de développement et satisfaire ainsi les préceptes du génie logiciel. Le lecteur averti du langage pourra évidemment sauter ces illustrations ou nous en signaler d'autres.

III-1 - Typage

Ada propose peu de types prédéfinis (dits standard ou primitifs) notons pour l'essentiel les types : caractère, chaîne, booléen, numérique entier et numérique réel (à virgule flottante). Leur implémentation n'est pas spécifiée par la norme et de ce fait il est recommandé de définir, soi-même, ses propres types même les plus élémentaires et notamment les numériques (entiers, réels flottants et même réels fixes) gage d'une programmation plus sûre (portabilité notamment). Ada offre une puissance inégalée pour la déclaration de nouveaux types (et pas forcément numériques).

Ainsi la déclaration

type T_Température is range -20..55;

Créé une famille d'objets numériques entiers dont les valeurs sont contraintes entre les bornes -20 et 55. Cette déclaration insère dans le code une connaissance du domaine qui reste habituellement dans les commentaires, ou dans des documents, ou… nulle part. Cette déclaration est portable (le range de T_Température ne dépend pas de la plate-forme de compilation) et, en plus, les débordements sont vérifiés dans le code généré. Ada est fortement typé ce qui interdit de façon implicite les mélanges (accidentels ou voulus).

On pourra voir aussi, en annexe, l'exemple (qui suit celui-ci : travail au niveau du bit) où la spécialisation du type déclaré permet de garder une maîtrise sémantique de haut niveau tout en effectuant des opérations très pointues, détaillées et … transparentes.

III-2 - Encapsulation

Cette propriété, déjà essentielle dans l'antique T.A.D. (Type Abstrait de Données) des années 80, reste d'actualité avec les classes et les objets. Il s'agit de rendre, par une déclaration spécifique, étroitement solidaires les données d'un composant logiciel et les opérations associées. On parle respectivement aujourd'hui de données membres et de méthodes. En Ada cette enveloppe logicielle est réalisée avec les paquetages (package). Mais contrairement à Java (qui reprend ce concept 20 ans après !) il s'agit, en Ada, d'une entité bien concrète, puisque déclarée dans un fichier propre. Cette propriété est le gage d'une grande fédération des concepts où rien n'est éparpillé. Le paquetage est resté avec Ada95 la structure fondamentale du langage, car c'est une technique robuste et élégante de factorisation du code. Voir en annexe un exemple simple de paquetage.

III-3 - « Spécifications » et réalisation

Le paquetage Ada (structure idéale d'un composant logiciel) abrite ces deux entités bien distinctes (généralement dans deux fichiers). Les déclarations package d'une part et package body d'autre part sont les labels de ces deux entités (respectivement spec et réalisation). La partie spec (pas tout à fait des spécifications ! mais plus sûrement un contrat) présente uniquement les déclarations (données et sous programmes). Le corps du paquetage réalisera, quant à lui, le contrat proposé par les spec. Toute nouvelle entité (sous programme ou paquetage) s'appuyant sur un paquetage déjà spécifié annonce cette dépendance avec with. Le compilateur ne se réfère alors qu'à la partie spec pour contrôler la syntaxe de la nouvelle entité. Le codage de la réalisation peut être différé. Cette séparation incite à prototyper sans penser implémentation et cette technique de développement est très importante. Dans l'enseignement de l'informatique, ce procédé permet d'obliger les étudiants à réfléchir avant de coder et c'est le langage (et le compilateur) qui apportent aux enseignants une aide précieuse pour ce défi pédagogique. Ada fournit aux novices, des bases solides que ceux-ci pourront transgresser ou mettre à profit dans d'autres langages, mais en connaissance de cause (et non par ignorance). Par exemple dans la définition des sous-programmes la portée et la direction des informations échangées est très claire. Le passage des arguments pour les procédures est précisé dans le prototypage. (Mentions In, Out, ou In Out). Notons qu'une fonction Ada reste une fonction (elle accepte des paramètres en entrée et fournit un unique résultat en sortie), Pour un novice, ces notions de base apparaissent donc très clairement (ce qui rentre, ce qui sort, ce qui a été modifié, la notion de fonction et de procédure). Les impacts sur la modification des paramètres lors de l'utilisation d'une procédure ou d'une fonction externe sont tout aussi clairs.

De manière connexe on peut insister sur l'incidence de la normalisation et des contrôles à la compilation : l'existence d'outils généraux de manipulation de programme et d'aide au développement en amont et en aval du langage. On notera aussi l'importance d'ASIS pour travailler au niveau des spécifications ou de la validation, et les outils permettant de naviguer conjointement dans le programme et son modèle (programme Ada et réseaux de Petri, spécification et programme, débourrage et navigation dans les sources). On verra enfin avec intérêt le pragma avec assertions.( voir ci-dessous le chapitre des exceptions) qui permet d'imposer aux étudiants dans leur code des contrôles qui sont des éléments de preuve de validité des algorithmes

Voir en annexe un exemple de paquetage

III-4 - Généricité

Le T.A.D., aussi que la classe (voir plus loin), déclarés dans un paquetage (et intimement caractérisés par ce paquetage) peuvent se dériver (on y reviendra), mais aussi peuvent être paramétrés (on paramètre un paquetage comme on paramètre traditionnellement un sous-programme). En Ada ces paramètres (dits de généricité) sont aussi complexes qu'on le souhaite. Les paramètres vont des très traditionnelles (constantes ou variables) en passant par les types, les sous programmes et jusqu'aux paquetages eux-mêmes génériques ! Ce profond degré d'abstraction est absolument remarquable. En Ada la mise en œuvre de la généricité (déclaration, instanciation et utilisation) est très simple et élégante (donc facile à enseigner). Les paquetages génériques en Ada sont compilés d'autorité sans attendre qu'ils soient instanciés ce qui n'est pas le cas des templates C++. Cette compilation autoritaire permet de valider le contrat générique et d'assurer que toute instance respectant le contrat compilera et fonctionnera comme prévu. La généricité permet et facilite la réutilisation et c'est même la technique la plus sûre pour réutiliser de façon fiable.

III-5 - Conception hiérarchique

Avec les paquetages, structure idéale pour construire T.A.D.(vu plus haut) ou les classes (voir plus bas), on peut construire avec Ada95 des familles de paquetages en allant d'une structure la plus simple vers (de proche en proche) la plus complète. Les paquetages fils (nouvellement créés) s'appuient naturellement sur les contrats (spec) des ascendants et permettent d'ajouter des fonctionnalités sans remettre en cause le travail de conception déjà élaboré. Les paquetages « officiels Ada » (fort nombreux et très utiles, voir plus bas) paquetages fournis avec le compilateur possèdent cette structure hiérarchique. Cette manière de travailler (faire modeste et essentiel d'abord, puis, de plus en plus détaillé et complet) est évidemment recommandée dès que les logiciels sont conséquents et cette méthode facilite des développements séparés et/ou en équipes. Et, point remarquable, elle rend inutiles les tests de non-régression sur le paquetage parent qui, lui, n'a pas changé. Cette technique ne nécessite a priori aucune dérivation contrairement aux langages C++ ou Java. Elle permettra aussi de fabriquer des classes (voir plus loin), mais alliée cette fois à la dérivation. Voir en annexe un exemple de paquetage fils.

III-6 - Exceptions

Les exceptions sont présentes dans le langage dès Ada83. Ce concept est évidemment incontournable en programmation et permet de prendre en compte les « anomalies » pendant le déroulement d'une application. La mise en œuvre Ada des exceptions (déclaration, déclenchement et traitement) nous semble d'une grande facilité et donc très agréable à enseigner. Ada95 a considérablement amélioré ce bagage et permet des traitements plus profonds (un peu moins simples cependant à appliquer). Dans le même esprit, nous signalons une propriété didactique d'importance à savoir la possibilité de mettre en place, dans le code, des assertions. Il s'agit d'un pragma (une directive pour le compilateur) que l'on peut activer optionnellement à la génération du code. À l'exécution quand une assertion n'est pas satisfaite une exception système bien identifiée est levée. On peut ainsi imposer aux étudiants dans leur code des contrôles qui sont des éléments de preuve de validité des algorithmes. Cette propriété n'est, à notre avis, pas assez utilisée même si elle reste moins élaborée que dans le langage Eiffel par exemple.

III-7 - Approche modulaire

Ce concept appartient aux techniques de C.O.O. (Conception Orientée Objet) chères à Grady Booch qui l'a bien illustrée avec Ada83. A priori, quand on reste à ce stade de conception (et c'est très souvent suffisant dans de nombreux développements) il n'est pas nécessaire de faire de l'objet « vrai ». C'est-à-dire qu'il est souvent inutile de prévoir des structures susceptibles d'être étendues par dérivation (donc, faire de la conception par analogie). Cependant, si tel est le cas, Ada95 a une réponse pour faire des classes et des objets (avec la déclaration tagged voir plus loin). Si l'on reste au niveau de l'approche modulaire, alors, le paquetage (toujours lui !), déclarant une structure de données (de préférence privée) avec les méthodes (fonctionnalités) associées, rendra tous les services attendus. Comme on l'a vu plus haut on peut étendre les fonctionnalités avec les hiérarchies de paquetages sans dériver (dériver est possible en Ada, mais pas obligatoire). Comme le concept de dérivation (et notamment la liaison dynamique qui va avec) n'est pas aussi facile à assimiler qu'il y parait, Ada permet élégamment de retarder cette difficulté tout en permettant des développements avec tous les préceptes suggérés par le génie logiciel.

III-8 - Classes et objets

Une lecture superficielle du manuel de référence Ada (ah la norme !) pourrait laisser croire qu'Ada ne permet pas les objets puisqu’aucun constructeur syntaxique class n'est présent dans le vocabulaire du langage. La réalité est tout autre : ce constructeur (cher à C++ et Java) n'est pas utile avec Ada95. Pour faire des classes et donc des objets, il suffit de reprendre le concept d'encapsulation, vu avec les paquetages, et de déclarer la première structure de données (racine) avec l'appellation tagged. En clair tout type « taggué (ou plutôt étiqueté) » est susceptible d'être dérivé par extension et cet héritage caractérise la structure de classe. On peut intelligemment mêler cette technique de conception avec l'utilisation des hiérarchies de paquetages vue plus haut permettant, ainsi, encore plus de souplesse et d'élégance dans les développements. Notons cependant que l'héritage multiple n'est pas prévu, en effet les concepteurs du langage n'ont pas jugé utile d'ajouter une construction spécifique pour l'héritage multiple, car trop complexe pour un usage réduit (les bons exemples d'héritage multiples sont très rares). Par contre la conjonction « dérivation et généricité » permet de résoudre les cas de « mixing inheritance » beaucoup moins rares. Voir en annexe un exemple de classe.

III-9 - Programmation d'activités concurrentes

On touche là à un grand moment de bonheur didactique. Avec quelle élégance Ada permet de concevoir et de réaliser facilement la programmation de processus concurrents ! Les tâches (avec Ada83 déjà !) et les objets protégés permettent d'illustrer donc de faire comprendre et assimiler des concepts forts importants et pas toujours évidents en temps réel et en informatique répartie tels que synchronisation et/ou échange d'information entre activités parallèles, exclusion mutuelle, partage de ressources communes, etc. De façon plus détaillée, disons que :

La possibilité de faire apparaître explicitement l'architecture opérationnelle en la « mappant » sur des tâches permet de suivre de façon claire le comportement et le « work flow » d'une application. C'est un aspect très utile pour le temps réel entre autres. Cela n'empêche pas de structurer aussi l'accès à l'information en termes d'objets (objets passifs ou objets pouvant contenir des tâches quand il s'agit d'un objet « serveur »).

Un ensemble de choix judicieux à la conception du langage ont fait de l'objet protégé la meilleure réalisation du concept de moniteur (introduit par C.AR. Hoare et P. Brinch Hansen), loin devant toute autre, même plus récente comme Java. Notons l'évaluation des conditions d'attente avant toute section critique de code, l'évaluation des conditions de réveil à la fin de chaque section critique, la clause « requeue » qui permet de construire des séquences de sections critiques pour une même tâche tout en permettant d'y intercaler les sections critiques d'autres tâches, la priorité donnée aux tâches déjà utilisatrices de l'objet protégé. Tout cela permet de traiter les problèmes de synchronisation à un haut niveau d'abstraction, celui d'automates à files d'attente, et permet de les traiter tous au même niveau (sans introduire des « wait » ou « signal » ou encore « notify », de bas niveau, avec des effets imprévisibles dépendant de l'implantation de l'ordonnanceur du système hôte). Cela facilite aussi la validation des algorithmes concurrents et leur mise au point. On pourra consulter des exemples de programmation concurrente. À notre connaissance, ces exemples ne sont ni dans le manuel de référence ni dans des ouvrages de cours. Ces exemples ont été choisis pour montrer la simplicité et la puissance de l'objet protégé.

La même remarquable simplicité se retrouve dans la programmation distribuée avec Ada où la notion d'activité concurrente est étendue aux machines sur un réseau (voir un exemple élémentaire).

IV - Pour quels apprenants ?

On trouvera dans les paragraphes ci-dessous des témoignages et des contributions d'enseignants utilisant Ada pour leurs enseignements et appréciant ses qualités dans leurs différents cursus (forts variés). La Suisse francophone qui est très active (voire à la pointe de la diffusion d'Ada) présentera ses propositions très intéressantes.

IV-1 - Non-informaticiens

Enseigner l'informatique à l'intention d'étudiants dont la formation essentielle n'est pas l'informatique a été tenté avec satisfaction par Michel Gauthier (en premier cycle à Limoges) et P. Tarroux en DEA Sciences cognitives à Paris-Sud (Orsay). Ce dernier cite une introduction à l'informatique avec Ada et Java comme supports comparés. L'expérience est instructive concernant la légèreté de Java et la rigueur d'Ada.

IV-2 - DEUG

Le langage Ada est utilisé comme support pour les quatre cours d'informatique (sur les quatre semestres) du DEUG MIAS au centre universitaire d'Évreux. Les premiers cours portent sur l'algorithmique classique et l'utilisation des tableaux. Les « facilités » d'Ada (comme l'affectation de partie de tableaux par exemple) sont présentées à la fin des cours, après leur écriture « pour de vrai ». Dès le troisième semestre, on commence l'introduction au module avec le développement de plusieurs corps d'un module sur les listes (ou autres). Enfin un projet dans lequel (cela dépend des années) les groupes se réunissent ET définissent les spécifications (.ads) de tous les modules. Ils doivent ensuite respecter ces spécifications pour pouvoir interchanger des parties du projet.

Des avantages du langage Ada, pour l'enseignement en DEUG, citons : une sémantique très sérieuse,

une égalité des étudiants au départ face aux connaissances antérieures (en effet, certains arrivent en connaissant le C, par exemple),

des concepts de haut niveau faciles pour une sensibilisation au génie logiciel,

l'accent sur le typage et la vérification de type pour l'apprentissage de la rigueur. Côté inconvénients, citons le manque de personnels compétents (vacataires…) !

Dans l'enseignement en DEUG de nombreux enseignements informatiques se font encore avec Pascal (qui reste, certes, un bon outil pédagogique), mais il y aurait tout à gagner à monter d'un cran encore ces avantages avec la transition vers Ada, sans compter qu'il existe un traducteur Pascal vers Ada (voir plus loin) qui peut faciliter la tâche des pédagogues.

IV-3 - IUT (Bac + 2)

Le langage Ada, choisi comme premier langage d'apprentissage par exemple au département informatique de l'I.U.T. d'Aix en Provence (de 1989 à 2003) a permis de résoudre de nombreux problèmes didactiques en fédérant des enseignements modulaires, en permettant l'assimilation de concepts réputés difficiles, mais incontournables au XXIe siècle comme ceux évoqués au chapitre « les concepts bien illustrés ».

Le thème de génie logiciel est présent dans tout le programme pédagogique de la formation IUT. L'accent mis sur la qualité est important dans la phase d'apprentissage d'un premier langage (Ada a été enseigné complètement au premier trimestre sur environ 200 heures). Si Ada, langage réputé complexe (donc ….. difficile à enseigner, mais ceci n'est pas vrai !), est proposé, dans des formations modestes (DUT = Bac + 2) c'est que leur vocation est de former des techniciens supérieurs capables de réaliser des travaux informatiques concrets en étant rapidement opérationnels, efficaces et méthodiques. Un bon langage devrait permettre de faire assimiler, de façon concrète et élégante les concepts fondamentaux qui résisteront au moins à une décennie de professionnalisme. Beau défi pédagogique, mais… résolu avec ce langage ! Ada possède tous les avantages pédagogiques de Pascal (ce que ne permet pas le C++, mais ce dernier, très prisé dans la profession est enseigné ensuite avec Java). Les avantages pédagogiques évoqués sont alliés, avec Ada, avec la rigueur et le professionnalisme des langages dédiés au génie logiciel. Le meilleur hommage que font les étudiants en guise de bilan peut se résumer dans ces propos :« Au début, j'ai été très irrité par le compilateur Ada qui ne laissait rien passer, mais, à la fin, quand mes programmes Ada, assez conséquents, tournaient du premier coup j'ai compris que le compilateur était plutôt mon ami ! ».

Notons tout de même un petit bémol quant aux stages de nos étudiants. Le stage de fin d'études I.U.T. étant très court (dix semaines) il est rare d'en trouver en Ada plutôt proposé sur cinq mois (donc destinés aux ingénieurs !).

IV-4 - Bac + 4

À l'IUP informatique de Brest le langage Ada 95 propose une approche temps réel complète et intéressante dans un contexte d'enseignement. Elle permet de voir comment un langage et ses constructions peuvent offrir les services et fonctions que traditionnellement le programmeur invoque par des appels de bas niveau (librairie de threads par exemple). La présentation du modèle de tâches Ada est suivie de celle de l'annexe temps réel ce qui permet d'aborder les thèmes avancés du temps réel (ordonnancement, inversion de priorité, par exemple). Des TP permettent ensuite d'appliquer les concepts vus en cours.

En licence d'informatique à Limoges Ada est utilisé comme support du cours sur la réutilisation. L'argumentaire est que les autres langages ne fournissent pas un outillage de puissance et de souplesses suffisantes pour la mise en œuvre des concepts sans une excessive technicité de langage. Il est à noter que la réutilisation est vue pour elle même indépendamment du concept d'objet dans la mesure où l'on peut faire des objets sans réutilisation et de la réutilisation d'autre chose que des objets.

IV-5 - Ingénieurs et BAC + 5

Ada est enseigné à Télécom Paris depuis presque la création du langage. Actuellement, Ada 95 nous permet de montrer à des étudiants de deuxième et troisième années (80 élèves) les différences d'approche entre la programmation structurée et celle orientée objet. Par ailleurs, le langage sert de support à des cours de programmation concurrente et de systèmes temps réel (travaux pratiques sur un serveur sporadique notamment). De nombreux projets d'étudiants et de stages de DEA ont été développés en Ada 95. Les vertus du langage ont permis d'intégrer très facilement ces contributions dans des projets désormais bien connus comme GLADE, AdaBroker, PolyORB, autant de logiciels libres développés à l'ENST. Pour terminer, il faut signaler que dans le cadre d'une collaboration ENST (L. Pautet) - LIP6 (F. Kordon), plusieurs thèses se sont déroulées ou se déroulent sur des thématiques d'intergiciels génériques et temps réel, ceux-ci étant développés en Ada 95. En conclusion, l'enseignement d'Ada 95 à l'ENST rencontre un franc succès, les étudiants s'étant intéressés à ce langage contribuant par la suite activement à la promotion d'Ada 95.

À l'Ensieta Brest (http://www.ensieta.fr/). Le programme est très similaire à celui proposé à l'IUP informatique de Brest, mais avec une approche systèmes embarqués. Cet enseignement permet d'aborder ainsi la complémentarité des thèmes temps réel et embarqués.

À l'ENST Bretagne nous présentons l'annexe distribution d'Ada 95 ainsi que les modèles de programmation associés. Ce cours donne un aperçu de la programmation distribuée avec une approche langage et montre comment l'annexe est implantée dans l'environnement GNAT.

IV-6 - Professionnels

Ada étant utilisé dans nombre de projets de grande importance, il y a une demande de la part des industriels pour des formations continues d'introduction au langage. Ces formations ont des contraintes différentes du milieu universitaire :

- elles doivent être courtes (le fait qu'un ingénieur soit indisponible coûte plus cher que la formation elle-même). Par exemple, chez Adalog la formation complète dure six jours. Chez Aonix, elle est fractionnée en deux cours (élémentaire et avancé) de cinq jours chacun ;

- les stagiaires ont, a priori, des connaissances générales de programmation ;

- les industriels veulent que les stagiaires soient directement opérationnels en sortie de formation.

Dans ces conditions, le risque est grand de focaliser le cours sur les aspects syntaxiques ou simplement directement pratiques, en évitant les aspects philosophiques. Bien sûr, les cours dispensés par des entreprises spécialisées en Ada évitent cet écueil, mais il existe des organismes de formation qui ajoutent Ada à leur catalogue sans réelles connaissances du domaine. Adalog a ainsi reçu une fois un groupe de stagiaires qui avaient suivi une formation dans un de ces organismes, et qui, percevant un manque, étaient revenus faire un deuxième stage. Réflexion d'un stagiaire à la fin de la formation: « c'est incroyable, ce n'est pas le même langage ! »

En plus des développeurs, de nombreux stagiaires sont des responsables qualité ou des responsables chargés de recevoir du code développé par des sous-traitants. Leur particularité est qu'ils doivent essentiellement lire du code plutôt que de l'écrire. Ce sont généralement les plus enthousiastes, car les fonctionnalités du langage correspondent très bien à leurs préoccupations.

IV-7 - CNAM

Le Cnam utilise le langage Ada pour :

-- l'apprentissage de la programmation et comme support algorithmique : • en premier cycle dans l'UV « Algorithmique et Programmation A » (APA), dans l'UV « Structures de Données » (où une comparaison est faite entre les langages Ada, Java et C++) ainsi que dans une valeur de projets • en second cycle dans l'UV « Accueil et Reconversion à l'Informatique » (VARI) où une synthèse des cours APA et Structures de Données est faite pour des élèves souhaitant se convertir à l'informatique (à partir de Bac + 2) ;

-- l'apprentissage des mécanismes de synchronisation présents dans les systèmes au second cycle dans la valeur Système Informatique (SIB) ;

-- l'étude de la programmation concurrente en fin de second cycle dans la valeur « Applications Concurrentes, Conception et Outils de Validation » (ACCOV) où les solutions Ada sont comparées aux solutions Java ou Posix ;

-- une initiation à la programmation des systèmes temps réel en fin de second cycle dans la valeur « Systèmes Temps Réels » ou en troisième cycle dans la valeur « Micro Informatique ».

Toutes ces unités de valeur sont également enseignées dans les centres régionaux du réseau Cnam.

IV-8 - Suisse (Bac + 3)

Les HES (Hautes Écoles Spécialisées) comprenant les Écoles d'ingénieurs comme l'EIG (École d'ingénieurs de Genève), l'EIVD (École d'ingénieurs du canton de Vaud) ou encore l'EIAJ (école d'ingénieurs de l'arc jurassien) dispensent un enseignement de niveau universitaire sur trois ans et trois mois (travail de diplôme à plein temps). L'introduction d'un Mastère est prévue à court terme, en relation avec le modèle de Bologne.

Leur « matériau de base » est formé par des étudiants possédant la maturité (« bac ») professionnelle technique obtenue après (ou pendant) un apprentissage fortement orienté vers la pratique professionnelle. Cette maturité est donc différente de la maturité académique (bac) obtenue dans les gymnases suisses (équivalents aux lycées français). À noter que les porteurs de maturité gymnasiale sont soumis à un stage préalable d'une année. Les porteurs de diplôme IUT sont admis en deuxième année d'études.

Les cours totalisent 34 heures de présence obligatoire par semaine sur deux semestres de 17 semaines, plus deux semaines spéciales (séminaires, etc.). Le style d'enseignement se caractérise par un fort encadrement (classes ne dépassant pas 30 étudiants, laboratoires en groupe de 10-15 étudiants et généralement bien équipés).

Le travail de diplôme de trois mois à plein temps est souvent proposé par l'industrie et sa soutenance a lieu en présence d'experts externes provenant des milieux professionnels et académiques. Les filières de formation sont évaluées périodiquement par des commissions d'experts internationaux (Peer Review), mandatées par l'OFFT (Office fédéral de la formation et de la technologie). Sont évaluées les prestations d'enseignement, de Ra&D, de mandats et de contacts avec les industries, la postformation et les relations internationales.

À l'EIVD, Ada constitue la brique de base de la formation en analyse et programmation, et fournit le (ou un) langage d'application dans des matières comme algorithmes et structures de données, programmation concurrente et temps réel ou encore génie logiciel. D'autres langages viennent ensuite se greffer sur les concepts abordés d'abord en Ada. À la fin de leur cursus, les étudiants de la filière d'informatique orientation logiciel auront étudié et pratiqué Ada pendant 600 heures environ, ceux de la filière d'informatique technique environ 200 heures. Soulignons cependant qu'Ada est enseigné comme premier langage à tous les étudiants de première année (environ 180 personnes) du département E+I qui comprend la formation non seulement en informatique, mais aussi en génie électrique, en électronique et en télécommunications. Ce choix pédagogique est accepté dans le département encore aujourd'hui, même si les pressions de tous ordres pour l'utilisation d'un autre langage existent bien évidemment !

À l'EIG, l'approche générale est analogue à celle de l'EIVD. Ada est enseigné dans deux filières: informatique et télécommunications. Tout comme à l'EIVD, le langage est utilisé pour divers enseignements : algorithmique et programmation, machines abstraites, programmation concurrente et temps réel et génie logiciel (env. 600 heures pour la filière informatique et 380 pour la filière télécommunications). À relever l'utilisation dans le cours de systèmes réactifs d'environnements tels que Statemate MAGNUM, SCADE ou encore Rhapsody in Ada (UML), lesquels permettent de générer du code source Ada.

La formation Ada est également appréciée par nos collègues des systèmes numériques qui enseignent notamment le langage VHDL (description comportementale du matériel). La similitude de ces deux formalismes leur permet d'optimiser leur enseignement.

Particularisme de l'EIG, cette dernière abrite également sur son site une EET (École d'enseignement technique) qui prépare, en trois ans, ses étudiants à l'obtention de la maturité technique et à l'entrée à l'EIG. Historiquement, elle était intégrée dans le cursus des études d'ingénieur. Ce qui explique pourquoi un enseignement Ada y est toujours dispensé. Cet aspect est intéressant à plus d'un titre. Il prouve si besoin était qu'un enseignement de l'algorithmique et de la programmation en Ada 95 serait judicieux dans les gymnases et les lycées.

V - Les aides et les outils

V-1 - Les compilateurs

Les compilateurs ont tous la propriété de respecter la norme (tests de validation obligatoires). Pour évidente qu'elle paraisse cette remarque n'est pas toujours valable avec les langages concurrents ! Ada est implémenté sur pratiquement tous les environnements et tous les systèmes. Citons :

GNAT (version commerciale Pro chez Ada Core Technologies) et le gratuit Object Ada (société Aonix) décliné en plusieurs versions, permet des GUI et même des applets Java !

Apex (société Rational Software)

Softech (ex Intermetrics).

et aussi : GreenHills, RR -Software et Irvine

Contrairement aux rumeurs, Ada n'est ni trop lent ni ne génère du code volumineux. Gnat (par exemple) utilise les mêmes environnements que les autres langages du GNU Compiler Collection, communément appelé GCC. Il existe aussi des directives pour obtenir l'effet souhaité (l'option -largs -s par exemple). On notera aussi comme curiosité que les 3/4 du compilateur GNAT est écrit en … Ada !

V-2 - Les environnements de développement (I.D.E.)

Les I.D.E. permettant de développer en Ada sont nombreux et … de qualité. Les gratuits ont ici notre préférence.

AdaGide [ftp://ftp.usafa.af.mil/pub/dfcs/carlisle/adagide] (exclusivement Windows) ainsi que Grasp, Glide, Emacs (disponibles aussi avec Linux), etc.

Une dernière nouveauté (disponible récemment) et à notre avis sans concurrent c'est : G.P.S. (Gnat Programing System) il inclut, entre autres, un débogueur graphique (ex. : GVD déjà disponible) et un navigateur dans les sources ! À voir en priorité (sur tout système) !

Le graphisme et Ada

GtkAda est une bibliothèque de composants graphiques permettant de créer des interfaces utilisateur et de faire du dessin.

GtkAda est un logiciel libre, fondé sur Gtk+, et disponible sur la plupart des plateformes.

La page principale est en http://libre.act-europe.fr/GtkAda

Cette bibliothèque permet de faire aussi bien de simples applications que des interfaces très évoluées, comme par exemple GPS, un environnement de développement écrit en Ada: http://libre.act-europe.fr/gps (voir plus haut rubrique I.D.E.)

GtkAda peut aussi être utilisé avec un générateur d'interfaces (« GUI builder ») appelé « Glade », à ne pas confondre avec l'autre Glade, qui permet de faire du développement d'interfaces distribuées en Ada.

V-3 - Internet et Ada

AWS est un serveur Web, sous forme de bibliothèques, que l'on peut embarquer dans toute application Ada. Ceci permet à l'application de communiquer avec l'extérieur en utilisant le protocole HTTP. Une utilisation courante d'AWS est d'embarquer l'interface graphique de l'application sous forme de pages Web. L'utilisateur peut alors piloter ou récupérer les données de l'application en utilisant un simple navigateur Web. Outre le protocole HTTP, AWS gère aussi HTTPS (HTTP sécurisé), LDAP, SMTP, SOAP, WSDL… En une dizaine de lignes de code, AWS sait aussi devenir un simple serveur de pages Web. La version A.W.S. 1.3 (dernière version distribuée lors de l'écriture de ce document) a été utilisée dans de nombreux projets et dans différentes entreprises. Bien que, lorsque l'on parle de serveur Web, on entend Apache (incontestable leader sur le marché), AWS par son approche « embarquée » a déjà séduit un ensemble d'utilisateurs. Il est aussi intéressant de mentionner que cela permet de faire une application « classique » dont l'interface utilisateur est réalisée par un browser. Exemple: un pilote de processus (ou d'expérience scientifique) que l'on peut contrôler à distance.

S'il est incontestable que Ada est, dans la plupart des esprits, associé au temps réel et aux codes à haute criticité, on voit que AWS est un bon exemple prouvant (si cela était nécessaire) qu'Ada est aussi bien adapté au monde des systèmes d'information.

V-4 - Les Bases de Données et Ada

Voir le projet GNADE (à voir en priorité).

Si GNADE est la référence pour l'embedded SQL, il y a aussi des interfaces directes, notamment via ODBC.

V-5 - Méthodes et Ada

Le choix d'une méthode part, à peu près, du même constat que pour les langages. En ce qui nous concerne ici, une fois le choix d'Ada fait, le développeur qui cherche ce qui le guidera vers une application bien conçue se retrouve à choisir entre du sérieux ou du tentant.

Le tentant c'est ce qui est en vogue. En effet, en faisant ce que fait tout le monde on risque moins d'être vu comme un « extra terrestre » et moins critiqué si l'on a des difficultés. Dans ce cas le choix actuel c'est UML, langage supporté par un rouleau compresseur médiatique (l'université étant loin, d'ailleurs, d'y être étrangère, car la course aux publications pousse à s'intéresser au plus récent). Si nous avons qualifié UML de langage et non de méthode c'est qu'UML ne véhicule pas de démarche à proprement parler. Il faut donc que chacun trouve sa propre démarche pour la formaliser grâce à la notation qui, elle, est formalisée.

Le sérieux (sous ce vocable nous entendons par là : éprouvé, solide, et efficace) c'est HOOD :

éprouvé : car l'industrie aérospatiale a développé suffisamment d'applications de grande envergure avec cette méthode pour que l'on ait un retour d'expérience conséquent (Ariane, Colombus, Eurocopter et bien d'autres),solide : car ce retour d'expérience a démontré la qualité induite par la méthode et la robustesse du logiciel en découlant,efficace : parce que terriblement bien inscrite dans le cycle de vie du logiciel. Méthode idéale pour le partage industriel (la démarche « top down », qui formalise les interfaces avant partage, a prouvé son efficacité) elle est, à l'autre bout de la chaîne idéale pour la maintenance du code. En effet la qualité de la génération de code (facilitée par la parfaite bijection entre les concepts HOOD et les concepts Ada) permet (chose rare) de pouvoir non seulement produire, mais aussi maintenir le code à travers l'outil de conception. Et encore cette totale adéquation est-elle très sous-employée (qui a utilisé de façon opérationnelle le concept de nœuds virtuels de HOOD qui permet la seule vraie répartition reconfigurable ?).

On peut, pour essayer de gagner sur (presque) tous les tableaux, faire du HOOD en notation UML. Si l'on concilie ainsi la rigueur et la « persuasion », on en perd un peu les facilités de génération de code Ada (puisqu’on fera ça avec des outils UML et non pas des outils HOOD).

Le débat entre courant principal et courant d'excellence est difficile à mener dans l'industrie, car il faut souvent plus convaincre politiquement que techniquement. Dans l'enseignement cette question devrait être moins cruciale et il est de la responsabilité des enseignants de donner aux étudiants toutes les armes pour choisir en toute connaissance de cause. Ils seront bien, assez tôt amenés, à choisir en prenant en compte ces contraintes politiques. Un étudiant capable de comprendre une spécification exprimée en UML (sans rivale à ce niveau), de concevoir en HOOD le logiciel qui répondra à ces spécifications et de générer l'application Ada (avec, par exemple, une répartition reconfigurable grâce à l'annexe Ada 95) sera bien armé pour aborder sa première application industrielle.

V-6 - Des bibliothèques

Les bibliothèques Ada sont nombreuses et variées.

Les bibliothèques officielles (obligatoires avec le compilateur du langage) :

1) annexe A (manipulations des caractères, des strings, des numériques, des fonctions mathématiques, des fichiers (textes, directs, séquentiels, flots). ;

2) annexe B (interfaçages) ;

3) gestion du temps, types contrôlés, gestion des exceptions, système, etc.

Les bibliothèques supplémentaires (pratiquement proposées gratuitement avec tous les compilateurs Ada) :

1) programmation système (annexe C) gestions d'interruptions, attribut de tâches ;

2) temps réel (annexe D) gestions des files d'attentes et ordonnancement, délais, priorités, tâches synchrone et asynchrone ;

3) systèmes distribués et applications réparties (annexe E) partitions, appels distants, objet distribué ;

4) numériques (annexe G) nombres complexes, attributs ;

5) systèmes d'information (annexe F) arithmétique décimale, picture à la Cobol ;

6) sûreté et sécurité (annexe H) exigences, pragmas de sécurité.

Les bibliothèques Ada libres sur le Net voir ci-dessous.

V-7 - Interfaçage avec d'autres langages

Ada (et cela peut surprendre) permet, souvent, de faire ce qui est réalisable dans un langage de bas niveau comme le C (on va en voir un exemple). Par contre, le compilateur Ada (pour faire du bas niveau) obligera le programmeur à préciser explicitement les opérations dangereuses (du genre Unchecked). En C le mode dangereux est le mode par défaut totalement antinomique avec Ada qui demande beaucoup plus d'efforts (prix à payer pour du code plus fiable).

Cependant, Ada permet d'utiliser des composants écrits dans d'autres langages : C, Fortran ou Cobol (et même dans les deux sens) grâce à des paquetages (fils de Interfaces) et des pragmas (directive pour le compilateur) assez simples à mettre en œuvre.

Pour interfacer Ada avec un autre langage (normalisé !) il faut que Ada manipule des types qui soient « homogènes » avec leur équivalent dans l'autre langage. Par exemple pour interfacer le C on dispose en Ada du paquetage Interfaces.C qui déclare de nouveaux types Ada, mais possédant une structure compatible avec les types équivalents en C. En effet si en C l'implémentation des types prédéfinis est précisée par la norme en Ada l'implémentation est laissée au compilateur. Pour en savoir plus.

On notera donc que si certains modules écrits en C sont intéressants Ada peut les intégrer. Mais C n'est pas incontournable pour accéder au bas niveau, car rien n'empêche de programmer, en Ada, des applications de bas niveau. À titre d'exemple on peut consulter un petit bout de code Ada permettant de travailler au niveau du bit (sans masque et autre xor), mais surtout en gardant une vision de haut niveau. Ce bout de code montre une manipulation que le C est incapable de réaliser sans un masque xor, démarche peu lisible et sujette à erreur d'interprétation notamment lors d'une maintenance. Dans cet exemple (comme dans d'autres), Ada est mieux adapté à la programmation de bas niveau que le C.

On peut aussi citer les interfaçages avec Tcl/TK, Python, Java, C#, et Lua

V-8 - Traducteurs avec d'autres langages

Acceptent en entrée du codage en C, Pascal ou Fortran pour produire en sortie du code Ada

C : appelé C2Ada voir l'adresse pour en savoir plus

Pascal : appelé P2Ada chez G. de Montmollin

Fortran : appelé F2Ada.

V-9 - Livres, cours, didacticiel

Les livres de cours Ada (en français) sont peu nombreux : Programmer en Ada95 de Barnes (traduit), Programmation séquentielle avec Ada95 ainsi que Programmation concurrente et temps réel avec Ada95 de P. Breguet et L. Zaffalon.

D'autres livres incontournables associant génie logiciel et Ada : Ingénierie du logiciel avec Ada de Grady Booch (antérieur à Ada95) et Méthodes de génie logiciel avec Ada95 de J.P. Rosen (également traducteur du livre précédent). Ce dernier livre (sûrement victime de son succès) est épuisé ! Mais l'auteur se propose, à terme, de le mettre en « libre » sur le Net (Adalog à suivre).

Un cours Ada95 (avec des TD et des TP) au format PDF (1 Mo) de D. Feneuille téléchargeable sur Internet voir l'adresse http://libre.act-europe.fr/french_courses

Un cours complet et détaillé avec exercices format PDF de G. Coray (EPFL) http://lithwww.epfl.ch/teaching/cmp/polycop.html

Des cours et des exercices divers et variés (notamment systèmes et temps réel avec Ada) au CNAM : http://deptinfo.cnam.fr/Enseignement/CycleA/APA/200x/INDEX/APA_HTO.html, http://deptinfo.cnam.fr/Enseignement/CycleA/SD/, http://deptinfo.cnam.fr/Enseignement/CycleProbatoire/Vari/, http://deptinfo.cnam.fr/Enseignement/CycleProbatoire/SRI/Systemes et http://deptinfo.cnam.fr/Enseignement/CycleSpecialisation/ACCOV.

Un article assez récent dans « le monde informatique » (exemples intéressants d'applications industrielles développées en Ada)

Un didacticiel, (le seul !), complet (mais hélas en anglais) de l'E.P.F.L. : Lovelace.

D'autres ouvrages où Ada est bien illustré :

1) Algorithmes et structures de données avec Ada, C++ et Java, A. Guerid, P. Breguet, H.Roethlisberger, PPUR, 2002 ;

2) Structures de données en Java, C++ et Ada 95, Ch. Carrez, InterEditions (1997)3) Initiation et autoformation à la programmation informatique, Canesi et Suc, Ellipses. 4) Vers Ada 95 par l'exemple, Fayard et Rousseau, De Boeck Larcierles titres des deux premiers ouvrages parlent d'eux-mêmes (on verra avec intérêt une comparaison entre les trois langages) ;

3) le troisième est un livre dédié aux vrais débutants en programmation où les premiers concepts sont illustrés avec Ada ;

4) le quatrième permet de découvrir le langage avec des exemples simples pour un premier contact ;

5) Orientation objet, structures de données et algorithmes (Ada95) de Philippe Gabrini. (ISBN : 2-7613-1028-0).

VI - Des adresses d'établissements

Voilà un chapitre présentant des formations qui pourrait demander du temps si l'on souhaite être exhaustif !

- L'École d'ingénieurs du canton de Vaud en Suisse (EIVD)

- Toujours en Suisse l'EIAJ (Jura) et EIG (Genève)

- L'INSA de Toulouse

- Sup Aéro Toulouse et aussi l'ENICA

- L'université Paul Sabatier de Toulouse Licence, I.U.P., DESS

- L'ENSIMAG à Grenoble

- L'ENSMA à Poitiers

- l'UTBM à Belfort

- Paris 12 Licence et MIAGE à Créteil

- DEUG MIAS à Évreux

- Paris 6 Licence

- CUST à Clermont-Ferrand et Licence

- l'INP à Grenoble (télécommunication)

- l'ENST à Paris et DEA

- Licence à Limoges

- l'ENS (ULM)

- Paris Sud DEA

- IUT Valence, Grenoble, Aix, Orsay…

- CNAM (certainement le plus gros contingent avec tous les centres associés de province)

- Orsay FIIFO (ingénieurs en alternance)

- l'ENAC à Toulouse

- IUP GMI Lille

- MIAGE Lille

- UQAM Montréal Québec

VII - Conclusion

Arrivé à ce stade, le lecteur (convaincu de l'intérêt d'Ada ?) se posera certainement quelques questions :

Pourquoi Ada n'est-il pas plus répandu ?

On pensait, au début (vers 1980), que Ada remplacerait rapidement les langages alors principalement utilisés : Fortran, Pascal et, dans une moindre mesure, Cobol. Or si le langage Ada s'est bien introduit dans les domaines sensibles (aéronautique, aviation, domaine militaire, nucléaire, contrôle de processus), sa diffusion est restée modeste dans les domaines de la programmation dite traditionnelle : scientifique, gestion, programmation système, alors qu'on assistait à une montée en force du langage C, et plus tard (depuis 1990) de C++ et maintenant et récemment Java (sans parler des langages de script).

Pourtant, toutes les mesures effectuées sur des projets réels ont montré que les bénéfices que l'on pouvait espérer d'Ada étaient obtenus ou dépassés : meilleure qualité de la conception, réutilisation, augmentation de la productivité des programmeurs, infléchissement de la courbe des coûts en fonction de la taille des logiciels, effondrement du taux d'erreurs résiduelles et des coûts d'intégration, efficacité du code produit ; et ceci, quel que soit le domaine d'application concerné

Mais le choix d'un langage de programmation fait intervenir de nombreux éléments, qui touchent plus à la psychologie qu'à l'économie.

Tout d'abord, l'expansion de C est liée au développement du système UNIX. Il est de tradition de fournir gratuitement le compilateur C avec les outils standard d'UNIX. Pourquoi alors acquérir un autre langage ? D'autant plus que C a pour lui une extrême simplicité (d'aucuns disent même pauvreté) de concepts. Remarquons au passage que ces arguments ne s'appliquent plus à C++ : des compilateurs sont payants, et le langage est devenu bien plus compliqué ! Historiquement, UNIX a été développé par un petit nombre d'individus, sans souci de politique d'ouverture commerciale. Les interfaces ont donc été pensées uniquement en fonction des particularités du C ; c'est ainsi qu'une fonction peut retourner par exemple soit un pointeur, soit la valeur False (c'est-à-dire en fait zéro, qui est également le pointeur nul !). De telles violations de typage sont incompatibles avec la plupart des langages évolués, mais fréquentes avec C. Il en résulte des difficultés à faire des interfaces abstraites, propres, pour beaucoup de fonctionnalités UNIX, ce qui accentue le retard, en particulier au niveau de la standardisation, des autres langages par rapport à C. Et puis la « sagesse populaire » affirme que l'on doit programmer en C sous UNIX, sans plus d'explication. En dehors de ces considérations pratiques, l'attirance du programmeur moyen pour C, et la peur instinctive qu'il éprouve vis-à-vis d'Ada, plongent leurs racines bien plus profondément. Beaucoup de programmeurs encore peut-être en fonction ont été formés à une époque où l'assembleur était roi. Nous avons connu des centres de calcul où les « nobles » (ceux qui étaient capables d'écrire tous leurs programmes en langage machine) regardaient de haut les novices qui n'étaient bons qu'à programmer en Fortran. Avec le temps, la programmation en assembleur tend à disparaître, car les problèmes de fiabilité, de maintenabilité et de portabilité sont vraiment devenus trop importants. Que sont devenus ces programmeurs ? Eh bien ils ont fait et font sûrement encore du C. Ils ont trouvé un langage qui n'est en fait, selon la définition de ses auteurs, qu'un assembleur portable. Comme nous l'avons vu, il permet de faire (presque) tout ce que l'on pouvait faire en langage machine et n'implique aucune remise en cause ni des méthodes ni de la façon de programmer. Inversement, Ada a été spécifiquement conçu pour obliger les programmeurs à modifier leurs habitudes ! En particulier, la programmation Ada s'accompagne d'une description plus abstraite des traitements. Outre que ceci nécessite un effort de réflexion plus important, le programmeur tend à perdre le contact direct avec la machine. Il doit faire confiance au compilateur pour la génération de code efficace, et ne doit plus se préoccuper du parcours exact du programme. Il n'est guère étonnant que l'inertie naturelle ne rende pas Ada très attrayant a priori au programmeur qui n'a pas saisi (car il n'a jamais essayé) les bénéfices d'une approche de plus haut niveau. Mais bien sûr, l'attirance naturelle du programmeur ne saurait être un critère pour un choix technologique dont dépendra tout le projet ! De façon plus imagée :C++ ressemble à un énorme gâteau à la crème, ruisselant de sucreries ; Ada ressemble plus à un poisson poché /pommes vapeur. Les questions intéressantes sont :

1) quel est le meilleur pour votre santé ;

2) qu'est-ce que les gens ont tendance à choisir spontanément ?

Les compilateurs Ada ont eu aussi mauvaise réputation. Les premières années (vers 1985), l'effort des fabricants s'est plus porté sur la conformité à la norme, vérifiée par l'incontournable suite de validations, que sur l'efficacité pour laquelle il n'y avait aucune obligation légale. Des compilateurs sont maintenant disponibles sur quasiment toutes les machines du marché, et des mesures objectives ont montré qu'ils étaient au moins aussi performants et fiables que les compilateurs C. Il n'en reste pas moins que le langage continue à traîner quelques vieilles « casseroles », d'autant qu'il est difficile de supprimer de la littérature les comptes rendus des premières expériences.

Un langage ne peut se répandre que s'il est enseigné.

Force est de reconnaître qu'Ada n'a pas réussi à séduire tous les universitaires. À cela au moins trois raisons.

Au début (1985) le prix des compilateurs, justifié en milieu industriel par le gain de productivité apporté, a été un obstacle certain. C'est ce qui a conduit le DOD, plus tard, à financer GNAT, pour mettre à la disposition de tous un compilateur Ada 95 gratuit [ftp://ftp.cs.nyu.edu/pub/gnat].

En dehors du coup, chercheurs et enseignants ont, aussi, généralement, des contraintes différentes des industriels : leurs logiciels n'ont que rarement à être maintenus, et doivent, souvent, être développés très rapidement. Même si leurs cours d'informatique prônent le génie logiciel et l'importance de favoriser la maintenance au détriment de la facilité de développement, les universitaires appliquent rarement ces bons principes à eux-mêmes ! Inversement, les langages orientés objet sont à la mode et semblent offrir des facilités supplémentaires (mais cet argument n'a plus lieu d'être avec Ada 95 qui les propose aussi).

Enfin, le fait que le DOD ait financé le développement d'Ada lui a créé, sûrement, une certaine antipathie dans les milieux universitaires, traditionnellement antimilitaristes.

Paradoxalement, les promoteurs du langage ont pu lui faire du tort par excès de purisme en exigeant de faire du 100% Ada.

Le langage a tout prévu pour permettre d'écrire des modules en d'autres langages (C, Fortran, Cobol) si c'était plus commode ; il autorise même l'écriture de modules en assembleur. Si ces éléments sont alors non portables, ce n'est pas trop grave dans la mesure où le mécanisme d'empaquetage garantit que ces non-portabilités sont répertoriées, aisément identifiées, et sans effet sur les utilisateurs des paquetages. Il n'y a donc pas de honte à écrire un corps de module en assembleur ou en C si, par exemple, il n'y a pas d'autre moyen d'obtenir les performances requises. En exigeant le « tout Ada » au-delà du raisonnable, on risque de se heurter à des problèmes tout à fait localisés, mais dont la publicité risque de discréditer le langage tout entier.

Enfin, il est remarquable de constater que les plus virulents adversaires d'Ada … ne l'ont jamais vraiment pratiqué !

Peut-être ont ils juste fait quelques essais, sans formation appropriée, en traduisant « littéralement » des bouts de code d'autres langages avec lesquels ils étaient familiers. Au bout de quelques erreurs de compilation, ils ont décidé que le langage était difficile, et n'ont pas poursuivi. Inversement, ce n'est qu'à l'usage que l'on en apprécie tous les avantages : typage fort, exceptions, parallélisme, paquetages hiérarchiques, etc. Il est certain que pour tirer tout le bénéfice d'Ada, il ne suffit pas d'apprendre la syntaxe ; il faut assimiler l'état d'esprit, c'est-à-dire « la façon de faire » qui va avec. Il faut admettre qu'un langage comme Ada joue un rôle différent dans le processus du développement du logiciel et que, comme on l'a vu, le choix du langage de codage n'est pas neutre : précisément parce qu'il peut être beaucoup plus qu'un simple outil de codage. Comme le remarquait G. Booch en 1991 : « Donnez une perceuse électrique à un charpentier qui n'a jamais entendu parler de l'électricité, et il l'utilisera comme marteau. Il tordra des clous et se tapera sur les doigts, car une perceuse fait un très mauvais marteau ».

Si les mauvaises nouvelles courent vite, il est plus difficile de répandre les bonnes.

Ada n'a pas toujours su « faire sa pub ». Qui, en dehors des convaincus, sait qu'Ada a été utilisé (il y a longtemps et avec succès) dans de nombreux programmes de gestion, dont un projet de 2,5 millions de lignes de code qui a pu être développé avec la moitié du coût initialement prévu. Qu'un logiciel qui n'arrivait pas à tenir ses contraintes temps réel a été réécrit en Ada avec des performances satisfaisantes. Qu'une entreprise gère plusieurs projets totalisant un million de lignes de code en n'employant qu'une seule personne à la maintenance. Soyons honnête cependant : au début les utilisateurs d'Ada 83 ont pu rencontrer des difficultés objectives, même si la plupart ont appris à « vivre avec ». On notait en particulier le manque de support des méthodes par classification, de trop nombreuses recompilations dans les grands projets, des difficultés d'interfaçage avec d'autres langages ou des bibliothèques… et le manque de fiabilité des premiers compilateurs et des environnements de programmation, la prolifération des tâches pour de simples synchronisations (car il n'y avait qu'un concept de synchronisation, le rendez-vous). Ces problèmes ont disparu depuis plus de dix ans, ou, ont été résolus par Ada 95 et peuvent être maintenant considérés comme sans objet par exemple grâce à l'excellent choix de l'objet protégé dans les synchronisations. Il faut, quand c'est possible, abandonner le rendez-vous pour traiter la synchronisation et ne le garder que pour les appels conditionnels ou temporisés ou pour initialiser des données propres à une tâche (autrement dit, on utilise le rendez-vous quand on ne peut pas traiter le problème avec un objet protégé).

VIII - Annexes (des exemples et des compléments)

On verra dans cette dernière partie quelques exemples (ou certains développements) illustrant plus finement les arguments développés dans le texte principal. Ces lignes ne sont pas fondamentales pour la compréhension des argumentations, mais devraient permettre de mieux les valider.

VIII-1 - Un exemple de typage

type T_Température is range -20..55;

crée une famille d'objets numériques entiers dont les valeurs sont contraintes entre les bornes -20 et 55. Ada est fortement typé ce qui interdit de façon implicite les mélanges (accidentels ou voulus). Ainsi :

Valeur : Integer;       -- instanciation d'un entier prédéfini
Temp   : T_Température; -- instanciation d'un objet de type déclaré
Temp := Valeur; -- erreur
Temp := -25;    -- erreur

Les deux instructions ci-dessus sont rejetées par le compilateur. Des conversions sont possibles, elles ne sont pas implicites et doivent être précisées.

Temp := T_Température (Valeur); -- conversion

est une instruction correcte syntaxiquement. Mais, à l'exécution, une valeur hors de l'intervalle lèvera une exception.

VIII-2 - Travail au niveau du bit

Le langage C est « proche » de l'implémentation machine ; trop d'ailleurs, comme on l'a vu, si l'on se place dans le domaine du génie logiciel. Malgré cela il est mal commode, en C, de travailler directement au niveau du bit. Il faut en passer par des opérations (masques, xor, etc.) qui font perdre une représentation de haut niveau et nuisent à la lisibilité. Voici un morceau de code Ada permettant de travailler élégamment au niveau du bit tout en gardant un haut niveau d'abstraction. On remarquera l'utilisation de directives spécifiant au compilateur des implémentations particulières (for … use). En gras les mots réservés du langage.

type T_Couleur is (Noir, Bleu, Vert, Cyan, Rouge, Magenta, Marron, Gris, Gris_Sombre,     Bleu_Clair, Vert_Clair, Cyan_Clair, Rouge_Clair, Magenta_Clair, Jaune, Blanc);
for T_Couleur use (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
subtype T_Couleur_Fond is T_Couleur range Noir .. Gris;
type T_Clignotement is (Fixe, Clignotant);
for T_Clignotement use (0, 1); -- on force ainsi l'implémentation
type T_Curseur is record -- déclaration des propriétés d'un curseur
Aspect  : T_Clignotement;
Arrière : T_Couleur_Fond;
Devant  : T_Couleur;
end record;
for T_Curseur use record  -- on impose un octet (bits 0 à 7)
Aspect  at 0 range 0..0; -- bit 0
Arrière at 0 range 1..3; -- bits 1 à 3
Devant  at 0 range 4..7; -- bits 4 à 7
end record;

Une instruction du style : Mon_Curseur.Aspect := Fixe; permet de modifier directement le bit n 0 de l'octet avec l'effet voulu. Ainsi que : Mon_Curseur.Aspect := Clignotement. On travaille ainsi directement sur le bit à l'insu de son plein gré !

VIII-3 - Compléments interfaçage avec C.

L'interfaçage Ada avec C oblige le programmeur Ada à utiliser des nouveaux types, mais compatibles C. Par exemple : int, short, long sont trois identificateurs de nouveaux types entiers Ada « compatibles » C. Le lecteur qui connaît les identificateurs des trois types primitifs C équivalents constatent que les trois identificateurs Ada ont été choisis identiques, en effet, ces identificateurs n'existent pas en Ada (ce sont Integer, Short_Integer et Long_Integer). C'est une bonne idée « mnémonique » ! Pour les types réels en virgule flottante on trouve C_float, double, long_double. Dans cet exemple le premier identificateur ne peut prendre son label C habituel : float pour éviter une confusion avec le type prédéfini Ada Float de même identificateur. Il sera donc nommé C_float. Les deux autres sont identiques à leur homologue C (car en Ada pas de confusion : Long_Float et Long_Long_Float). On notera aussi char et char_array. Respectivement char pour définir des « sortes » de Character et char_array pour définir des tableaux de caractères (String) ; char n'existant pas en Ada est utilisé. On notera aussi dans la bibliothèque (paquetage Interfaces.C) les fonctions To_C et To_Ada (respectivement) qui permettent de convertir des types prédéfinis Ada en leur équivalent Ada compatibles C ou les types Ada compatibles C en types prédéfinis Ada (respectivement).

Un exemple : déclaration d'une procédure Ada réalisée en C (puis utilisation) :

procedure C_System (Chaine : in Char_Array);
pragma Import (C, C_System, »system”);

La procédure Ada C_System est réalisée avec le sous-programme C connu system grâce au pragma Import. Ainsi l'instruction Ada : C_System(To_C(« ls -l »)); permet d'afficher en Ada le répertoire courant Linux. La chaîne de caractères String Ada « ls -l » est convertie (grâce à To_C) en char_array chaîne compatible C et devient paramètre de system qui se substitue à C_System. Notons cependant que cette technique n'est pas celle que l'on utilise traditionnellement en Ada pour lancer une commande, en effet, une bibliothèque existe pour cela. Mais l'exemple est assez concis et significatif pour la compréhension de l'interfaçage (nous semble-t-il !).

VIII-4 - Exemples de lisibilité

Ada est plutôt réputé verbeux (moins que Cobol tout de même !), mais c'est une qualité quand cette verbosité permet de relire facilement du code ou le faire partager. Voici deux exemples l'un très simple le deuxième un peu plus compliqué.

Premier exemple : comparons : une instruction Ada (en première ligne) puis son équivalent en C (deuxième ligne).

Vect(5..7) := Vect(10..12); -- affectation d'une tranche de tableau
memcpy (vect+5,vect+10,3*sizeof(*vect));

Quelle instruction est la plus lisible ? On remarque sur la deuxième ligne (mais c'est bien connu) que le développeur C est très préoccupé par la représentation mémoire alors qu'en Ada c'est le compilateur qui fera l'implémentation, à son idée, sans intervention du programmeur. Voir aussi plus haut l'exemple permettant de travailler au niveau du bit en Ada où l'instruction Mon_Curseur.Aspect := Fixe; est fort lisible et dit significativement ce qu'elle fait !

Deuxième exemple : écriture d'un sous-programme (Ada puis C) qui place les quatre derniers caractères d'une chaîne en tête.

procedure Swap (Str : in out String) is begin
if Str'length < 4 then return; end if; -- contrôle validité
Str := Str (Str'Last-3 .. Str'Last) & Str (Str'First .. Str'Last-4);
end Swap;
voidswap (char *str)
{int len = strlen (str);
char *tmp;
if (len < 4) return;
tmp = (char *) malloc (len + 1);
strncpy (tmp, str+len-4, 4);
strncpy (tmp+4, str, len-4);
tmp[len] = '\0';
strcpy (str, tmp);
free (tmp);
}

À noter que dans ces exemples la version Ada n'utilise pas d'allocation dynamique. La construction de la nouvelle chaîne se fait sur la pile.

VIII-5 - Exemple simple de paquetage

Ce petit exemple, forcément simple, de paquetage (P_Date) sert à illustrer les deux paragraphes : encapsulation ainsi que spécifications et réalisation.

Seules les parties spec (ou contrat) seront mises en évidence, le corps (ou réalisation) n'apportant rien aux concepts à illustrer.

package P_Date is
type T_Mois is (janvier, février, mars, avril, mai, juin, juillet, août, septembre, octobre, novembre, décembre);
subtype T_Numéro is Integer range 1..31;
subtype T_Année  is Integer range 1900..9999;
type T_Date is private;
procedure Saisir (La_Date : out T_Date);
procedure Editer (La_Date : in  T_Date);
fonction  Quantième (La_Date : in T_Date) return Positive;
fonction  Est_Bissextile (La_Date : in T_Date) return boolean;
................... -- ici d'autres méthodes
Private
type T_Date is record
Numéro : T_Numéro;
Mois   : T_Mois;
Année  : T_Année;
end record;
end P_Date;

Commentaires : le type T_Date est le T.A.D. essentiel : c'est un type article ; il est déclaré dans le paquetage P_Date qui sert d'enveloppe logicielle. Ce type est privé c'est-à-dire que les trois champs qui le composent sont inaccessibles par contrat. Des méthodes (non déclarées ici) pourraient permettre ces accès. Seules quatre méthodes (pour faire court) sont déclarées dans le paquetage (deux procédures d'entrée sortie et deux fonctions).

VIII-6 - Exemple de paquetage hiérarchique

Cet exemple reprend le paquetage P_Date précédent en le complétant avec d'autres fonctionnalités dans un paquetage fils P_Date.Suite.

package P_Date.Suite is
fonction "<"  (Date1, Date2 : in T_Date) return boolean;
fonction ">"  (Date1, Date2 : in T_Date) return boolean;
fonction "<=" (Date1, Date2 : in T_Date) return boolean;
fonction ">=" (Date1, Date2 : in T_Date) return boolean;
end P_Date.Suite;

Commentaires : Comme on l'a noté dans l'exemple ci-dessus, le type T_Date déclaré dans le paquetage P_Date manque de fonctionnalités. Elles peuvent être déclarées plus tard en fonction des besoins des développements sans changer les premières spécifications (naturellement héritées sans dérivation). Ici nous montrons la déclaration des quatre opérateurs relationnels traditionnels. L'opérateur d'égalité n'a pas à être déclaré, car il est implicite pour un type article. L'utilisation de ces opérateurs qui sont des surcharges des opérateurs prédéfinis est simple. Exemple :

Aujourd_hui, Demain : T_Date; -- instanciation de deux dates
if Aujourd_hui > Hier then .....

VIII-7 - un exemple de classe

Cet exemple reprend le premier exemple de type abstrait de données T_Date en le déclarant (même identificateur), mais dans une enveloppe de classe le paquetage P_Date_Classe. On notera bien qu'en Ada le constructeur class n'existe pas !

package P_Date_Classe is
type T_Mois is (janvier, février, mars, avril, mai, juin, juillet, août, septembre, octobre, novembre, décembre);
subtype T_Numéro is Integer range 1..31;
subtype T_Année is Integer range 1900..9999;
type T_Date is tagged private; -- racine de la classe
procedure Saisir (La_Date : out T_Date);
procedure Editer (La_Date : in  T_Date);
fonction Quantième (La_Date : in T_Date) return Positive;
fonction  Est_Bissextile (La_Date : in T_Date) return boolean;
................... -- ici d'autres méthodes
Private
type T_Date is tagged record
Numéro : T_Numéro;
Mois   : T_Mois;
Année  : T_Année;
end record;
end P_Date_Classe;

Commentaires : pratiquement aucune différence sauf le mot tagged qui transforme le T.A.D en classe ! Mais bien sûr c'est une autre approche où l'on postule, a priori, une extension possible de la structure de données.

Voyons maintenant le côté extension (par dérivation) de la structure de données de la classe T_Date.

package P_Date_Classe.Suite is
type T_Jour is (Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi, Dimanche);
type T_Date_Complétée is new T_Date with record
Jour : T_Jour; -- dérivation avec extension
end record;
procedure Saisir (La_Date : out T_Date_Complétée);
procedure Editer (La_Date : in  T_Date_Complétée);
fonction Ajuster (La_Date : in T_Date) return T_Date_Complétée;
................... -- ici d'autres méthodes
end P_Date_Classe.Suite;

Commentaires : la nouvelle classe étendue T_Date_Complétée se déclare toujours dans un paquetage (ici un fils). Un seul champ est ajouté celui du nom du jour de la date. Deux nouvelles procédures d'entrée sortie sont déclarées (surcharge) pour la nouvelle classe ainsi qu'une fonction permettant (c'est un exemple) de rendre une date (T_Date) en son équivalente dans l'autre classe (T_Date_Complétée). La réalisation de la fonction Ajuster (faite dans le body) consiste simplement à initialiser le jour d'une date donnée.

Nous ne résistons pas au plaisir d'écrire une réalisation (qui se fait, rappelons-le, dans le body !) :

procedure Saisir (La_Date : out T_Date_Complétée) is
Date_Simple : T_Date;
begin    Saisir (Date_Simple);
La_Date := Ajuster (Date_Simple);
end Saisir;

C'est court et sûr !

La surcharge de Saisir est parfaitement résolue par le compilateur grâce au type (T_Date) du paramètre effectif Date_Simple.

VIII-8 - Deux exemples de programmation concurrente

Voici maintenant deux exemples de programmation concurrente proposés par C. Kaiser. À notre connaissance, ces exemples ne sont, ni dans le manuel de référence, ni dans des ouvrages de cours. Ces exemples ont été choisis pour montrer la simplicité et la puissance de l'objet protégé. Et aussi l'utilisation qu'on peut faire des choix sémantiques pris par le langage. Si on essaie, sans précautions, de transposer ces solutions, élégantes, dans d'autres langages, comme Java, ou de les utiliser avec Posix, on va vers des catastrophes (inter blocage, incohérences). Bien entendu, les solutions présentées sont transposables en Java ou en Posix, mais au prix d'une complication résultant de la programmation défensive qu'il faut mettre en œuvre.

La documentation de ces programmes resterait à développer. Il s'agit d'abord ici de montrer des exemples de style de programmation de concurrence avec les objets protégés. Et combien on peut avoir un plaisir esthétique en programmant la concurrence en Ada. Comme on l'a déjà dit : un vrai bonheur didactique !

UN CLASSIQUE : LE DÎNER DES PHILOSOPHES

Version utilisant des familles d'entrées et du « requeue ». Elle est sans inter blocage grâce à la sémantique de l'objet protégé (dite sémantique de l'œuf), et sans famine.

--  REPAS DES PHILOSOPHES
--  protocole interne et une baguette après l'autre

package General is
N : constant Integer := 5;
type Philo_Id is mod N;
end General;
--
with General; use  General;
package LesBaguettes is
procedure Prendre (PourMoi : in Philo_Id);
procedure Rendre (PourMoi : in Philo_Id);
end LesBaguettes;
package body LesBaguettes is
type Tableau_De_Booleens is array (Philo_Id) of Boolean;

protected Baguettes is
entry Saisir (Philo_Id); -- famille d'entrées
procedure Restituer (X : in Philo_Id);
private
Disponible : Tableau_De_Booleens := (others => True);
entry Completer (Philo_Id); -- autre famille d'entrées
end Baguettes;
protected body Baguettes is
entry Saisir (for I in Philo_Id) when Disponible (I) is
begin
Disponible (I) := False;
requeue Completer (I + 1);
end Saisir;
entry Completer (for I in Philo_Id) when Disponible (I) is
begin
Disponible (I) := False;
end Completer;
procedure Restituer (X : in Philo_Id) is
begin
Disponible (X) := True;
Disponible (X + 1) := True;
end Restituer;
end Baguettes;
procedure Prendre (PourMoi : in Philo_Id) is
begin
Baguettes.Saisir (PourMoi);
end Prendre;
procedure Rendre (PourMoi : in Philo_Id) is
begin
Baguettes.Restituer (PourMoi);
end Rendre;
end LesBaguettes;
--  *************************************************
--  voici de quoi essayer l'algorithme
--  simulation du comportement des philosophes
--  **********************************************
generic
type Resultat is private;
type Etat_Interne is limited private;
with procedure Reset (V : in Etat_Interne);
with function Prochaine_Valeur (V : Etat_Interne) return Resultat;
package Machine_Protegee is
function Prochaine_Valeur_Protegee return Resultat;
end Machine_Protegee;
package body Machine_Protegee is
protected Machine_Interne is
procedure Reset_Interne;
function Prochaine_Valeur_Interne return Resultat;
private
Etat : Etat_Interne;
end Machine_Interne;
protected body Machine_Interne is
function Prochaine_Valeur_Interne return Resultat is
begin
return Prochaine_Valeur (Etat);
end Prochaine_Valeur_Interne;
procedure Reset_Interne is
begin
Reset (Etat);
end Reset_Interne;
end Machine_Interne;
function Prochaine_Valeur_Protegee return Resultat is
begin

return Machine_Interne.Prochaine_Valeur_Interne;
end Prochaine_Valeur_Protegee;
begin
Machine_Interne.Reset_Interne;
end Machine_Protegee;

--====================================
with General; use  General;
package Scene_Philo is
procedure Pense (X : in Philo_Id);
procedure Mange (X : in Philo_Id);
end Scene_Philo;
with Ada.Text_IO, Ada.Numerics.Float_Random;
use  Ada.Text_IO, Ada.Numerics.Float_Random;
with Machine_Protegee;
pragma Elaborate_All (Machine_Protegee);
package body Scene_Philo is
package Protected_Float_Random is new Machine_Protegee
(Resultat         => Float,
Etat_Interne     => Ada.Numerics.Float_Random.Generator,
Reset            => Ada.Numerics.Float_Random.Reset,
Prochaine_Valeur => Ada.Numerics.Float_Random.Random);
use Protected_Float_Random;
procedure Pense (X : in Philo_Id) is
Duree : Float range 0.0 .. 1.0;
begin  --  nouveau tirage aléatoire d'une durée
Duree := Prochaine_Valeur_Protegee;
Put_Line ("le philosophe " & Philo_Id'Image (X) & " pense");
delay (Duration (Duree)); -- x pense un certain temps
Put_Line ("le philosophe " & Philo_Id'Image (X) & " a faim");
end Pense;
procedure Mange (X : in Philo_Id) is
Duree : Float range 0.0 .. 1.0;
begin --  nouveau tirage aléatoire d'une durée
Duree := Prochaine_Valeur_Protegee;
Put_Line ("le philosophe " & Philo_Id'Image (X) & "  mange");
delay (Duration (Duree)); -- x mange un certain temps
Put_Line ("le philosophe " & Philo_Id'Image (X) & "  n'a plus faim");
end Mange;
end Scene_Philo;

--====================================
--  VOICI LE REPAS DES PHILOSOPHES
--====================================
with General, Scene_Philo, LesBaguettes;
use  General, Scene_Philo;
procedure Application is
package Nom is
function Unique return Philo_Id;
end Nom;
package body Nom is
Next_Id : Philo_Id := Philo_Id'Last;
-- protégé par le package
function Unique return Philo_Id is
begin
Next_Id := Next_Id + 1; -- addition modulo N
return Next_Id;
end Unique;
end Nom;
task type Philo (X : Philo_Id  := Nom.Unique);
Philosophes : array (Philo_Id) of Philo;
task body Philo is
begin
for I in 1 .. 65 loop
Pense (X);
LesBaguettes.Prendre (X);
Mange (X);
LesBaguettes.Rendre (X);
end loop;
end Philo;
begin
null;
end Application;
--
=======================================================

--  UN EXEMPLE MOINS CLASSIQUE D'ALLOCATION DE RESSOURCE
--  Ce genre d'exemple nécessite de réévaluer l'état
--  au moment du retour des ressources,
--  et de passer de l'information entre processus.
--  En dehors de Ada, cela donne généralement
--  soit une solution compliquée
--  soit beaucoup de réveils intempestifs de tâches
--  Ici encore Ada permet une solution élégante.
--  Voici deux exemples, l'un très simple, l'autre compliqué
--  comme l'est la réalité des applications concurrentes .
--  =============================================
--  ALLOCATEUR DE RESSOURCES : VARIANTE 1
--  allocation de ressources contrôlée par un objet protégé
--  variante 1 : la première tâche en attente
--  bloque les nouvelles requêtes et alors
--  les nouvelles requêtes doivent attendre que
--  la tâche en attente sur l'entrée Servir soit servie
--  Les requêtes en attente sont servies selon
--  l'ordre de la file de Demander
--  =============================================
package Allocation1 is
Max : constant Integer := 100; -- nombre de ressources
type Des_Ressources is new Integer range -Max .. Max;
subtype Nb_Ressources is Des_Ressources range 1 .. Des_Ressources'Last;
type Liste_Resource is new Integer;
--  type à redéfinir ultérieurement
protected Controleur is
entry Demander
(R : out Liste_Resource; Nombre : Nb_Ressources);
procedure Rendre
(R : in Liste_Resource; Nombre : Nb_Ressources);
Private
entry Servir (R : out Liste_Resource; Nombre : Nb_Ressources);
Disponible : Des_Ressources := Des_Ressources'Last;
end Controleur;
end Allocation1;
--  ===================================================
package body Allocation1 is
protected body Controleur is
entry Demander (R : out Liste_Resource; Nombre : Nb_Ressources)
when Servir'Count = 0 is
begin
Disponible := Disponible - Nombre;
if Disponible < 0 then   --  la demande doit attendre
requeue Servir;
else           --  la demande peut être satisfaite
--  Fournir_Les_Ressources_Dans(R);
null;
end if;
end Demander;
entry Servir (R : out Liste_Resource; Nombre : Nb_Ressources)
when Disponible  >=  0 is
begin
--  Fournir_Les_Ressources_Dans(R);
null;
end Servir;
procedure Rendre
(R : in Liste_Resource; Nombre : Nb_Ressources) is
Begin
--  Recuperer_Les_Ressources_De(R);
Disponible := Disponible + Nombre;
-- enregistre ce nombre de ressources rendues
end Rendre;
end Controleur;
begin
null;
end Allocation1;
--  =============================================
--  ALLOCATEUR DE RESSOURCES : VARIANTE 2
--  allocation de ressources contrôlée par un objet protégé
--  variante 2 : il y a un premier groupe de tâches en attente
--  les tâches de ce groupe reçoivent les ressources une par une
--  et attendent d'avoir toute leur requête avant de continuer
--  les autres tâches en attente, s'il y en a, sont servies après.
--  =============================================
package Allocation2 is
Max : constant Integer := 100; --  nombre de ressources
type Des_Ressources is new Integer range 0 .. Max;
subtype Nb_Ressources is Des_Ressources range 1 .. Des_Ressources'Last;
type Liste_Resource is new Integer; --  à modifier ultérieurement
Taille_Groupe : constant Integer := 10;
--  nombre de tâches distinguées
type Index_Groupe is mod Taille_Groupe;
type Gestion_Groupe is array (Index_Groupe) of Des_Ressources;
protected Controleuse is
entry Demander (R : out Liste_Resource; Nombre : Nb_Ressources);
procedure Rendre (R : in Liste_Resource; Nombre : Nb_Ressources);
private
entry Servir (Index_Groupe)
(R : out Liste_Resource; Nombre : Nb_Ressources);
Disponible : Des_Ressources := Des_Ressources'Last;
Deficit : Gestion_Groupe;
--  tableau des ressources encore attendues
J : Index_Groupe := Index_Groupe'Last; -- témoin de parcours
function Recevable return Boolean;
end Controleuse;
end Allocation2;
package body Allocation2 is
protected body Controleuse is
function Recevable return Boolean is
--  il suffit d une place non utilisée, c'est-à-dire vide
--  la sémantique de l'objet protégé garantit
--  qu'avant ce test
--  toute entrée de Servir qui a sa garde à vrai,
--  parce que la place est vide, a été activée
Begin
for K in Index_Groupe loop
if Deficit (K) = 0 then
return True;
end if;
end loop;
return False;
end Recevable;
entry Demander
(R : out Liste_Resource; Nombre : Nb_Ressources)
when Recevable is
I : Index_Groupe;
function Index_Disponible return Index_Groupe is
begin --  choix selon la politique d'allocation
for K in Index_Groupe loop
if Deficit (K) = 0 then
return K;
end if;
end loop; --  Recevable => on est sûr d'en trouver un
raise Program_Error; --  ne doit pas servir
end Index_Disponible;
begin
if Nombre <= Disponible then
Disponible := Disponible - Nombre; -- effet immédiat
--  Fournir_Les_Ressources_Dans(R);
else         --  la demande ne peut être satisfaite
I := Index_Disponible;
Deficit (I) := Nombre - Disponible;
requeue Servir (I);
end if;
end Demander;
entry Servir (for I in Index_Groupe)
(R : out Liste_Resource; Nombre : Nb_Ressources)
when Deficit (I) = 0 is
begin
--  Fournir_Les_Ressources_Dans(R);
null;
end Servir;
procedure Rendre
(R : in Liste_Resource; Nombre : Nb_Ressources) is
Compte_Des_Nuls : Integer := 0;
Nb : Nb_Ressources := Nombre;
Begin
--  Recuperer_Les_Ressources_De(R) ;
--  attribution de ressource, une par une,
--  à tour de rôle, à chaque requête J en attente,
--  connue parce que Deficit(J) > 0
Loop
J := J + 1; --  Progression de l'indice de parcours
if Deficit (J) > 0 then
Compte_Des_Nuls := 0;
Deficit (J) := Deficit (J) - 1;
Else
Compte_Des_Nuls  := Compte_Des_Nuls  + 1;
end if;
Nb := Nb - 1;
exit when (Nb = 0) or (Compte_Des_Nuls = Taille_Groupe);
end loop;
Disponible := Nb; -- reste = ressources non réparties
end Rendre;
end Controleuse;
begin
null;
end Allocation2;
--   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
--   voici  de quoi essayer les allocateurs
--   chaque générateur de nombres aléatoires est partagé
--   et en accès concurrent; il doit être protégé
--   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
--====================================
Generic
type Resultat is private;
type Etat_Interne is limited private;
with procedure Reset (V: in Etat_Interne);
with function Prochaine_Valeur (V: Etat_Interne) return Resultat;
package Machine_Protegee is
function Prochaine_Valeur_Protegee return Resultat;
end Machine_Protegee;
package body Machine_Protegee is
protected Machine_Interne is
procedure Reset_Interne;
function Prochaine_Valeur_Interne return Resultat;
private
Etat : Etat_Interne;
end Machine_Interne;
protected body Machine_Interne is
function Prochaine_Valeur_Interne return Resultat is
begin
return Prochaine_Valeur(Etat);
end Prochaine_Valeur_Interne;
procedure Reset_Interne is
begin
Reset (Etat);
end Reset_Interne;
end Machine_Interne;
function Prochaine_Valeur_Protegee return Resultat is
begin
return Machine_Interne.Prochaine_Valeur_Interne;
end Prochaine_Valeur_Protegee;
begin
Machine_Interne.Reset_Interne;
end Machine_Protegee;
--  =============================================
--  PROGRAMME PRINCIPAL
--  =============================================
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics.Discrete_Random;
with Ada.Numerics.Float_Random;
with Allocation1; use Allocation1;
with Allocation2; use Allocation2;
with Machine_Protegee;
procedure Exemple_Ressource_Allocation is
package R_Number_Random is new
Ada.Numerics.Discrete_Random (Allocation1.Nb_Ressources);
package Protected_R_Random is new Machine_Protegee
( Resultat         => Allocation1.Nb_Ressources,
Etat_Interne     => R_Number_Random.Generator,
Reset            => R_Number_Random.Reset,
Prochaine_Valeur => R_Number_Random.Random);
package Protected_Float_Random is new Machine_Protegee
( Resultat         => Float,
Etat_Interne     => Ada.Numerics.Float_Random.Generator,
Reset            => Ada.Numerics.Float_Random.Reset,
Prochaine_Valeur => Ada.Numerics.Float_Random.Random);
task type Pilote_Type (X : Allocation1.Nb_Ressources :=
Protected_R_Random.Prochaine_Valeur_Protegee);
task body Pilote_Type is
R : Allocation1.Liste_Resource;
D1: Float := Protected_Float_Random.Prochaine_Valeur_Protegee;
D2: Float := Protected_Float_Random.Prochaine_Valeur_Protegee;
task Copilote;
task body Copilote is
Y : Allocation2.Nb_Ressources := Allocation2.Nb_Ressources (X);
R : Allocation2.Liste_Resource;
begin
delay (Duration (D1)); --  attente aléatoire
Put_Line (" cas 2 : demande de  " & Allocation2.Nb_Ressources'Image (Y));
Controleuse.Demander (R, Y);
delay (Duration (D2)); --  attente aléatoire
Put_Line (" cas 2 : retour de  " & Allocation2.Nb_Ressources'Image (Y));
Controleuse.Rendre (R, Y);
end Copilote;
begin
delay (Duration (D1));  --  attente aléatoire
Put_Line (" cas 1 : demande de  " & Allocation1.Nb_Ressources'Image (X));
Controleur.Demander (R, X);
delay (Duration (D2)); --  attente aléatoire
Put_Line (" cas 1 : retour de  " & Allocation1.Nb_Ressources'Image (X));
Controleur.Rendre (R, X);
end Pilote_Type;
Usagers : array (1..30) of Pilote_Type;
begin  -- maintenant on a soixante et une tâches actives
-- leur exécution va donc imprimer 120 lignes de texte
null;
end Exemple_Ressource_Allocation;
--====================================

VIII-9 - Un exemple de programmation distribuée

La programmation distribuée revient à découper un programme en plusieurs exécutables appelés partitions en Ada) devant s'exécuter sur des machines (voir des OS) différentes. Une grande force de l'approche distribuée d'Ada et de permettre que le même programme puisse être compilé en mode distribué et non distribué. Cette approche simple et pragmatique permet de rapidement construire une application distribuée, atout qui sera apprécié lors de l'enseignement (il n'est pas nécessaire de rentrer dans des détails de bas niveau). Pour illustrer ce point, prenons comme exemple une version distribuée du fameux hello World !

Le serveur:

package Hello_Server is
pragma Remote_Call_Interface;
function Message return String;
end Hello_Server;

package body Hello_Server is
function Message return String is
begin
return "Hello World!";
end Message;
end Hello_Server;

Le client :

with Ada.Text_Io;
with Hello_Server;

procedure Hello_Client is
use Ada;
begin
Text_Io.Put_Line (Hello_Server.Message);
end Hello_Client;

La seule addition par rapport à la version non distribuée est le pragma Remote_Call_Interface. On peut difficilement faire plus simple !

IX - Remerciements

Ont contribué à la rédaction de ce document (par ordre d'apport) : D. Feneuille (IUT), J.P. Rosen (Adalog), S. Tardieu (ENST), S. Rivière (Rochebrune), P. Obry (EDF), D. Minguillon (CNES), P. Valarcher (DEUG), Y. Kermarrec (ENST Bretagne), P. Pignard , A. Charlet (ACT), L. Draghi (Thalès), L. Pautet (ENST), L. Zaffalon (EIG), P. Bréguet (EIVD), G. Canesi (CNAM) C. Kaiser (CNAM), J.F Peyre (CNAM)