Ada, c’est bien connu, est un langage portable. Ceci ne signifie pas pour autant que tout programme Ada se comporte exactement de la même façon sur toute machine ; heureusement même, car cela signifierait qu’il ne serait pas possible de profiter des particularités d’une machine donnée. Plus modestement, cela signifie qu’il est possible d’écrire des programmes portables, moyennant quelques précautions que nous allons détailler maintenant.
Notons tout d’abord qu’il existe plusieurs formes de portabilités : en particulier on peut souhaiter une portabilité absolue (même résultat sur tous les systèmes), ou une portabilité relative (ajustement automatique du programme pour utiliser au mieux les possibilités de la machine). La première chose à faire pour écrire « portable » est donc de définir quelle forme de portabilité est souhaitée…
Notons que l’on peut considérer qu’unprogramme est portable si l’effort de portage est négligeable devant l’effort de développement initial ; en pratique ce coût est rarement nul. Si l’on doit dépendre par endroits des spécificités de l’implémentation, on veillera à accéder aux fonctionnalités concernées via des paquetages spécialisés dont il suffira d’ajuster le corps en cas de portage.
Les types prédéfinis sont ceux définis dans le
paquetage Standard.
Si Boolean ne crée pas de problème, les
types numériques (Integer,
Float,
Duration)
ont des intervalles de valeurs définis par
l’implémentation. La
première règle
est donc de ne pas utiliser les types prédéfinis
et de toujours
définir
ses propres types de données, à partir des
spécifications du problème
bien
entendu.
Comme toute règle, celle-ci a ses
exceptions : par
exemple, Integer doit être
utilisé pour indexer les String (c’est à
peu près
sa seule utilisation systématique). Noter que rigoureusement, il
peut
être non
portable d’utiliser des chaînes de plus de 32K
de
long (la norme exige qu’Integer soit sur au moins 16
bits); en pratique le risque est limité,
pratiquement tous
les compilateurs ayant Integer sur (au moins) 32 bits
aujourd’hui.
Le cas de Float
est un peu différent : on peut l’utiliser à
condition d’accepter
que la
précision des calculs soit la précision
« naturelle » de la
machine.
On obtiendra donc des résultats de précision variable
selon
l’implémentation,
mais ceci peut être acceptable dans certaines applications. Le
tout est
de
définir quelle sorte de portabilité on souhaite !
Le paquetage System
est l’outil
de base de la portabilité relative : il définit en
effet les
caractéristiques
de la machine sur laquelle on tourne. Par exemple, si l’on souhaite
simplement
disposer du plus grand type entier disponible sur la machine, il suffit
de
déclarer :
type Big is range Min_Int .. Max_Int;
Les enfants de System
sont des
paquetages de services de bas niveau pouvant poser des problèmes
de
portabilité : Address_To_Access_Conversion,
Storage_Elements,
Storage_Pools…
et bien entendu Machine_Code.
Leur utilisation doit donc être encapsulée dans des
paquetages d’accès.
L’ordre d’élaboration des unités de
compilation est
(partiellement) déterminé par le compilateur. Si une
unité utilise lors
de son
élaboration des services fournis par une autre unité, le
corps de cette
dernière
doit avoir été élaboré avant, sous peine de
Constraint_Error
(cf. "L'ordre d'élaboration"). Du coup,
un
changement de
compilateur fait souvent apparaître une dépendance abusive
à l’ordre,
révélant
que le programme marchait par hasard. Une utilisation soigneuse du pragma
Elaborate_All
est indispensable. Noter que Gnat dispose
d’une
option (option –p de gnatbind) qui génère
le plus
mauvais ordre d’élaboration possible. Si le programme
démarre
correctement
avec cette option, alors il est probable qu’aucun problème
d’élaboration
n’existe !
Mis à part le cas des accès directs à la
mémoire (dont
la portabilité est intrinsèquement faible), l’ordre des
octets dans un
mot n’a
pas de raison de créer de problèmes de portabilité
en Ada. En revanche,
le sens
de numérotation des bits (à savoir, si le bit de poids
fort porte le
numéro 0
ou 31) peut créer des difficultés au niveau des clauses
de
représentation.
C’est pourquoi Ada95 a rajouté l’attribut Bit_Order
qui permet de savoir, et même
de forcer, le sens de numérotation des bits. Attention, cet
attribut
n’a
d’effet que sur l’interprétation des numéros de bits dans
les clauses
de
représentation, il ne change en rien la représentation
mémoire.
Copyright © 2000 Adalog / J-P. Rosen