Interfaçage entre Ada et le C

L'un des défauts du langage Ada est, il faut bien le reconnaître, son manque de popularité malgré ses grands atouts. Un langage comme le C a connu un grand succès malgré ses défauts et ses lacunes, lacunes que le C++ ou le Java ont tenté de rectifier par la suite. Cette très forte popularité du langage C et de ses descendants a conduit au développement de très nombreuses bibliothèques tierces (on parlerait de packages en Ada) : pour jouer des vidéos, afficher des images, des boutons, jouer des morceaux de musique… et j'en passe. Et si certaines bibliothèques existent aussi en Ada, on ne peut pas en dire autant de toutes.

Tu veux dire que l'on aurait mieux fait d'apprendre un autre langage ?

Non bien sûr ! Ada est un excellent langage ! :D Mais il faut lui reconnaître ce défaut. Car c'est en reconnaissant ce défaut que des développeurs ont eu l'idée ingénieuse de permettre aux développeurs Ada d'utiliser des bibliothèques écrites dans d'autres langages : C, C++, Fortran, Cobol. Dès lors, plus besoin de réapprendre un nouveau langage pour utiliser une bibliothèque intéressante : il suffit de la porter en Ada. On parle alors d'interfaçage ou de portage, ou encore de binding. Nous allons donc découvrir comment porter une bibliothèque C en Ada.

Quelques préparatifs

Avant de nous lancer dans l'interfaçage d'une quelconque librairie écrite en C, nous devons nous préparer, tout d'abord en disposant des logiciels nécessaires, ensuite en apprenant quelques rudiments sur le langage C et enfin en se penchant sur les packages qui seront nécessaires.

Logiciels nécessaires

Pas besoin de télécharger des dizaines de logiciels, nous ne comptons pas devenir des développeurs en C, mais nous aurons toutefois besoin d'un compilateur qui connaisse le C. Or, notre compilateur actuel s'appelle GNAT, c'est-à-dire : GNU NYU Ada Translator (Traducteur GNU pour Ada du NYU [New York University]). GNAT ne compile que de l'Ada et rien d'autre. Sauf que GNAT fait partie d'un projet plus grand : GNU Compiler Collection, un ensemble de compilateurs libres.

Et alors ? Il va pas compiler du C pour autant ? :colere2:

Non, mais jetez un œil au répertoire où est installé GNAT (par défaut C:\GNAT sous Windows) et plus précisément, jetez un œil au répertoire …\GNAT\2011\bin. Ce répertoire contient tout plein de programmes comme GNAT lui-même, GPS (l'IDE fourni par Adacore) mais aussi g++ le compilateur pour le C++ et surtout gcc le compilateur pour le C ! Bref nous avons déjà tout ce qu'il faut.

Hey ! Quand je clique dessus, une fenêtre s'ouvre et se ferme ! Il ne se passe rien ! :colere:

C'est bien normal, le compilateur n'est utilisable que de deux façons : soit par un IDE, soit en ligne de commande. Nous allons opter pour la seconde solution (si certains souhaitent tout de même disposer d'un IDE prévu pour le C, je les renvoie vers l'excellent cours «Apprenez à programmer en C» de Mathieu Nebra). Son utilisation est des plus simples, sous Windows allez dans Menu Démarrer > Tous les programmes > Accessoires > Invite de commandes (ou cmd). Sous Linux, cela dépend de votre distribution… sous Ubuntu, le programme s'appelle «Terminal», sous Kubuntu c'est la «Konsole». Ensuite, les commandes seront les mêmes, il vous suffira de taper :

1
cd chemin/du/repertoire/de/votre/fichier/c

Cela «ouvira» le répertoire pour la console. Puis la compilation s'effectuera de la manière suivante :

1
gcc -c nom_du_fichier.c

Ainsi, si je dispose d'un fichier appelée MonProgramme.c enregistré sur le bureau, je devrais entrer les commandes suivantes :

1
2
cd C:\Users\Proprietaire\Bureau
gcc -c MonProgramme.c

Je vous invite à relire cette partie lorsque vous aurez besoin de compiler votre premier fichier en C.

Quelques rudiments de C

Encore une fois, je ne vais pas faire de vous des programmeurs-C (le cours de Mathieu Nebra le fait très bien). Mais de même que pour lire un package, vous n'avez pas besoin de décrypter tout son code source, pour connaître les fonctionnalités fournies par un programme en C, vous n'avez pas besoin de connaître toutes ses subtilités. Nous n'allons donc voir que quelques correspondances entre l'Ada et le C afin que vous puissiez interfacer correctement vos futures bibliothèques.

Des variables, de leur type et de leur déclaration

En Ada, vous avez désormais l'habitude d'utiliser les types Integer, Float et Character. Ils ont bien évidemment leur pendant en C et s'appellent respectivement int, float et char. Le type boolean n'a pas d'équivalent en C (même s'il en a un en C++), le programmeur C utilise donc généralement un integer pour pallier à cela en prenant 1 pour true et 0 pour false.

Qui plus est, le programmeur C utilise régulièrement le type double plutôt que float. Ce nouveau type correspond en fait au type Long_Float en Ada. Et le langage C dispose également d'un deuxième type d'Integer appelé long. Quelle différence avec les int ? Plus aucune aujourd'hui. Enfin, les variables ne se déclarent pas de la même manière. Rapide comparatif :

Version Ada

1
2
3
Variable1 : Integer ; 
Variable2, Variable3 : Float ; 
Variable4 : Character := 'A' ;

Version C

1
2
3
int Variable1 ; 
float Variable2, Variable3 ;
char Variable4 = 'A' ;

Vous aurez remarqué les différences : le type est indiqué au tout début et le(s) nom(s) de(s) variable(s) après. Il n'y a pas de symbole pour séparer le type de sa variable. Le symbole d'affectation de l'Ada (:=) est un simple signe égal (=) en C.

Il y a des similitudes également : chaque déclaration se termine bien par un point virgule et les variables sont séparées par des virgules. Les caractères sont symbolisés de la même manière : entre deux apostrophes.

Des programmes

Le langage C dispose bien entendu de fonctions comme le langage Ada, mais il ne dispose pas de procédures. Ou plutôt, disons que les procédures en C sont des fonctions ne retournant aucun résultat. Un exemple :

Version Ada

1
2
3
4
5
6
procedure hello ; 
procedure plusieursHello(n : integer) ;
function Addition(a : integer ; b : integer) return integer is
begin 
   return a + b ; 
end Addition;

Version C

1
2
3
4
5
6
void hello() ; 
void plusieursHello(int n) ;
int Addition(int a, int b)
   {
   return a + b ; 
   }

Comme vous pouvez le voir, le C n'utilise pas les mots FUNCTION ou PROCEDURE. Une fonction se déclare à la manière d'une variable : le type (celui du résultat attendu) suivi du nom de la fonction, suivi à son tour de parenthèses quand bien même la fonction ne prendrait aucun paramètre. Du coup, pas besoin d'indiquer le type du résultat avec une instruction RETURN qui ne sert qu'à transmettre le résultat.

Une procédure, en C, est une fonction de type void. Enfin, pas la peine de chercher un BEGIN et un END, ils sont symbolisés par les accolades : { et }. Ainsi, ce que l'Ada gagne en lisibilité par rapport au C, il le perd en compacité.

En C, il n'y a pas de mode OUT ou IN OUT. Tous les paramètres sont en lecture seule !

Des tableaux, des strings et des types personnalisés

Comparons encore une fois l'Ada et le C :

Version Ada

1
2
Tab : array(1..5) of integer ;
txt : string(3..7) ;

Version C

1
2
int Tab[5] ;
char txt[5] ;

La construction d'un tableau se fait simplement en utilisant les crochets et en indiquant le nombre de valeurs contenues dans ce tableau. Le type au début indique bien sûr le type des valeurs contenues dans le tableau. Et il n'y a pas de type String en C ! Ce sont seulement des tableaux de caractères.

Les tableaux sont toujours indexés à partir de 0 en C ! ! !

Enfin, les types énumérés et structurés se déclarent de la manière suivante :

1
2
3
4
5
6
enum Jour{Lundi,Mardi,Mercredi,Jeudi,Vendredi,Samedi,Dimanche} ; 
struct Horaire
{
   int H ;
   int min ; 
};

Des pointeurs

Nous arrivons à la fin de cette initiation express au C, mais nous ne pouvons commencer l'interfaçage sans évoquer les pointeurs. Comme nous l'avons vu, les pointeurs sont peu utilisés en Ada, voire même bannis dans certains projets pour des raisons de sécurité. Mais en C, c'est tout le contraire ! Le développeur-C a tout intérêt à avoir compris le fonctionnement des pointeurs car ils sont partout. Cet usage si important est notamment du au fait que les paramètres des fonctions ne peuvent être qu'en lecture seule. Mais les pointeurs permettent de lever cette restriction. Un pointeur, en C, se déclare ainsi :

1
int *Ptr;

Un simple astérisque suffit à faire d'une variable un dangereux pointeur. Du coup, Ptr est un pointeur et *Ptr est la valeur pointée par Ptr (l'équivalent de Ptr.all en Ada).

Packages nécessaires

Ada ne connaît pas du tout les types utilisés en C, ni leur encodage, et a besoin d'outils pour effectuer quelques conversions. Nous aurons donc besoin du package Interfaces.c (attention, ce package ne commence pas par le mot « ada »). Si vous souhaitez le consulter (et je vous y engage), il se nomme i-c.ads. Vous y retrouverez les types double, int, char, C_Float (pour ne pas confondre avec le type Float en Ada)… dont nous avons parlé plus tôt. Ce package donne les équivalences entre les types Ada et les types C.

Il fournit également de nombreuses fonctions de conversion pour passer d'un langage à l'autre. Vous remarquerez également que ce package fournit un type char_array qui correspondra aux types string pour le C. Mais comme les tableaux en C utilisent les pointeurs, le type char_array n'est pas un sous-type de string et le package fournit donc des procédures et fonctions To_Ada() et To_C() afin d'effectuer les conversions nécessaires.

Un second package vous sera parfois utile, il s'agit du package Interfaces.c.extensions, dont le fichier se nomme i-cexten.ads. Celui fournit simplement de nouveaux types entiers ou modulaires, il n'a donc pas de corps.

Hello World : du C à l'Ada

Notre programme en C

Nous allons commencer par créer notre (premier ?) programme écrit en C. Pour cela, ouvrez un éditeur de texte et faites un copier-coller du code ci-dessous :

1
2
3
4
5
6
#include <stdio.h>

void HelloWorld() 
   {
   printf("Hello World ! \n") ;
   }

Enregistrez-le dans un répertoire spécifique et sous le nom « HelloWorld.c » et effectuez la compilation comme indiqué dans la première partie. Comme GNAT, le compilateur gcc crée divers fichiers, dont notamment des fichiers objets (les fameux fichiers .o qui encombrent vos répertoires). Ce sont ces fameux fichiers .o que le langage Ada pourra utiliser pour effectuer l'interfaçage.

Euh… c'est bizarre, gcc n'a pas créé de fichier exécutable. Ça n'a pas marché ?

Rassurez-vous, tout va bien : pour qu'un programme en C génère un exécutable, il doit toujours porter le nom « Main » (principal). Sans cela, le compilateur le voit comme une sorte de package. Pour information, ce programme HelloWorld() affichera la fameuse phrase "Hello World !" puis retournera à la ligne (c'est là le rôle du \n).

Notre programme Ada avec les normes 95 ou 2005

Faisons comme si nous n'étions pas capable de réaliser un programme similaire en Ada et créons un programme appelé Programme_Ada.adb dans le même répertoire que « HelloWorld.c ». Voici ce que nous souhaiterions faire :

1
2
3
4
PROCEDURE Programme_Ada IS
BEGIN
   HelloWorld ;     --Appel de la fonction c Helloworld()
END Programme_Ada ;

Lier notre programme au fichier C

Il n'y a aucun package utilisé ? C'est étrange.

Nous n'avons pas besoin de package pour ce programme mais en revanche, il faut lui indiquer dans quel fichier .o il pourra trouver la fonction C appelée HelloWorld(). Cela ne se fait pas à l'aide des clauses de contexte WITH ou USE mais en utilisant une directive de compilateur dans la partie déclarative : PRAGMA Linker_Options. Ce qui nous donne ceci :

1
2
3
4
5
PROCEDURE Programme_Ada IS
   PRAGMA Linker_Options("HelloWorld.o") ; 
BEGIN
   HelloWorld ;     --Appel de la fonction c Helloworld()
END Programme_Ada ;

Cette directive indique au compilateur dans quel fichier objet il devra puiser les informations manquantes.

Importation de la fonction C

Bah ! o_O Quand j'essaye de compiler, GNAT ne voit toujours pas la fonction HelloWorld() ! Il m'indique « programme_ada.adb:4:04: "HelloWorld" is undefined ».

Attention, la directive de compilateur et le fichier objet ne jouent pas le rôle d'une clause de contexte ou d'un package. Ce n'est pas aussi simple. GNAT sait maintenant dans quel fichier il doit piocher, mais il ne sait pas ce qu'il doit y trouver, ni comment ce qu'il y trouvera a été écrit (est-ce du C, du C++ ou autre chose encore ?). Pour tout vous dire, il est même impossible d'employer directement la fonction HelloWorld() dans notre programme Ada ; nous devons préalablement lui créer un support, un sous-programme Ada auquel on attribuera le code de HelloWorld().

Pour cela, vous devrez écrire la spécification d'une procédure (l'équivalent d'une fonction void) puis importer dans cette procédure le code de HelloWorld() grâce à un second PRAGMA, le « PRAGMA Import »:

1
2
3
4
PROCEDURE Hello_World_Ada ;
PRAGMA Import(Convention    => C, 
              Entity        => Hello_World_Ada, 
              External_Name => "HelloWorld") ;

Le PRAGMA Import prend trois paramètres:

  • Convention, correspond au langage utilisé ;
  • Entity correspond au nom de l'entité Ada servant de support, ici il s'agit de notre procédure Hello_World_Ada() ;
  • Enfin, le paramètre External_Name est un String correspond au nom de la fonction C correspondante.

Attention, le C est sensible à la casse, vous ne devez pas écrire une minuscule à la place d'une majuscule ! Voici donc notre code complet :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
PROCEDURE Programme_Ada IS
   PRAGMA Linker_Options("HelloWorld.o") ; 

   PROCEDURE Hello_World_Ada ;
   PRAGMA Import(Convention    => C, 
                 Entity        => Hello_World_Ada, 
                 External_Name => "HelloWorld") ;
BEGIN
   Hello_World_Ada ;     --Appel de la fonction c Helloworld()
END Programme_Ada ;

Un PRAGMA Export existe également. Il fonctionne de la même façon que le PRAGMA Import mais permet d'exporter des variables ou des sous-programmes vers des programmes C.

Notre programme Ada avec la norme 2012

Encore une fois, la nouvelle norme Ada2012 apporte son lot de nouveautés et avec elles quelques obsolescences. Ainsi, ce bon vieux « PRAGMA Import » est remplacé par des contrats. Au lieu d'écrire le code ci-dessus, vous n'aurez qu'à écrire :

1
2
3
4
5
6
7
8
PROCEDURE Programme_Ada IS
   PRAGMA Linker_Options("HelloWorld.o") ; 

   PROCEDURE Hello_World_Ada 
      WITH Import => TRUE, Convention => C, External_Name => "HelloWorld" ;
BEGIN
   Hello_World_Ada ;     --Appel de la fonction c Helloworld()
END Programme_Ada ;

Il est même possible de simplifier ce code en n'écrivant que ceci :

1
2
PROCEDURE Hello_World_Ada 
   WITH Import, Convention => C, External_Name => "HelloWorld" ;

Supposons que vous disposiez d'une fonction void en C et d'une procédure en Ada portant le même nom, par exemple hello(), votre travail sera encore simplifié :

1
2
PROCEDURE Hello 
   WITH Import, Convention => C ;

Quelques menus problèmes

Maintenant que vous avez interfacé votre premier programme du C vers l'Ada, je vous propose de modifier notre programme en C. Mon objectif sera de mettre en évidence quelques soucis ou difficultés auxquels vous pourriez être confrontés.

Nous utiliserons pour la suite de ce chapitre les techniques des normes Ada95 et Ada2005.

Procédure avec paramètres

Code C

Première modification : nous allons améliorer le code du programme C afin qu'il répète plusieurs fois la phrase « Bonjour ### », où les dièses seront remplacés par une chaîne de caractère quelconque (un nom de préférence) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>

void HelloWorld(int n, char nom[]) 
   {
   int i ;
   for(i=0 ; i< n ; i++)
      {
      printf("Bonjour %s ! \n", nom) ;
      }
   }

Code Ada

Vous ne devriez pas avoir beaucoup de difficultés à interfacer ce nouveau programme. Pensez seulement à faire appel aux types que nous avons vus dans le package Interfaces.c, et notamment au type Char_array. En effet, s'il ne devrait pas y avoir de soucis à utiliser le type Integer au lieu de Int, il n'en sera pas de même si vous utilisez le type String au lieu de Char_array. Mais je ne vous en dis pas davantage pour l'instant. Voici le code en Ada et le résultat obtenu :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
WITH Interfaces.C ;       USE Interfaces.C ; 


PROCEDURE Programme_Ada IS
   PRAGMA Linker_Options("HelloWorld.o") ; 

   PROCEDURE Hello_World_Ada(n : int ; nom : char_array) ;
   PRAGMA Import(Convention    => C, 
                 Entity        => Hello_World_Ada, 
                 External_Name => "HelloWorld") ;
BEGIN
   Hello_world_ada(4, To_C("Bobby")) ; 
END Programme_Ada ;
1
2
3
4
Bonjour Bobby !
Bonjour Bobby !
Bonjour Bobby !
Bonjour Bobby !

Si vous essayez de retirer la fonction de conversion To_C(), votre programme devrait lever une exception CONSTRAINT_ERROR. Pour mieux comprendre l'intérêt de ce programme, nous allons le modifier à la marge. Au lieu d'afficher "Bobby", nous allons afficher une variable de type Char_array qui vaudra "Bobby". La nuance est infime et pourtant. Voici notre nouveau code :

1
2
3
4
5
6
...
   Texte : Char_Array(1..5) ; 
BEGIN
   Texte := To_C("Bobby") ;
   Hello_world_ada(4, Texte) ; 
END Programme_Ada ;

Mais là encore, notre programme se retrouve face à une exception CONSTRAINT_ERROR. Et selon lui, ce serait du à un problème de longueur de chaîne.

Mais pourquoi cette erreur ? Il y a bien 5 caractères dans Bobby.

En Ada, oui. Mais en C, la gestion des chaînes de caractères est plus complexe. Tout d'abord, en C, chaque case du tableau est un pointeur sur un caractère. Ensuite, chaque chaîne de caractère comporte un caractère supplémentaire indiquant la fin de la chaîne. Il faut donc une chaîne de longueur 6 pour enregistrer 5 caractères en C. Pour régler ce problème facilement, il vous suffit d'indexer votre Char_Array de 0 à 5 au lieu de l'indexer de 1 à 5.

Avec un type structuré

Code C initial

Au lieu d'utiliser deux paramètres, nous souhaiterions n'en utiliser qu'un seul de type structuré. Voici ce que donnerait notre code C :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <stdio.h>

struct t_type{
   long n ;
   char nom[5] ;
   } ;

void HelloWorld(struct t_type x) 
   {
   int i ;
   for(i=0 ; i< (x.n) ; i++)
      {
      printf("Bonjour %s ! \n", x.nom) ;
      }
   }

Code Ada

Nous devons commencer par créer l'équivalent d'un type struct en Ada et indiquer au compilateur à l'aide d'un nouveau PRAGMA que ce type est conforme aux exigences du langage C :

1
2
3
4
5
TYPE T_Type IS RECORD
   N : Int ; 
   Nom : Char_Array(0..5) ; 
END RECORD ; 
PRAGMA Convention(C,T_Type) ;

Voici enfin ce que donnerait notre code complet :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
WITH Interfaces.C ;       USE Interfaces.C ; 


PROCEDURE Programme_Ada IS
   PRAGMA Linker_Options("HelloWorld.o") ; 

   TYPE T_Type IS RECORD
      N : Int ; 
      Nom : Char_Array(0..5) ; 
   END RECORD ; 
   PRAGMA Convention(C,T_Type) ; 

   PROCEDURE Hello_World_Ada(X : t_type) ;
   PRAGMA Import(Convention    => C, 
                 Entity        => Hello_World_Ada, 
                 External_Name => "HelloWorld") ;

   X : T_Type ; 
BEGIN
   X.N := 4 ; 
   X.Nom := To_C("Bobby") ; 
   Hello_World_Ada(x) ; 
END Programme_Ada ;

Mais ? o_O Comment se fait-il que le message ne cesse de s'afficher en boucle ? Et d'ailleurs, ce n'est même pas le bon message !

Je vous propose cet exemple pour attirer votre attention sur un problème d'interfaçage. Les types struct du C correspondent aux types RECORD de l'Ada mais le langage requiert que lorsqu'un type RECORD est passé en paramètre d'une fonction ou d'un programme, il soit transmis via son adresse, c'est-à-dire grâce à un pointeur.

Résoudre notre problème

Une première solution, un peu longue, est de revoir complètement nos codes. Nous devons d'abord revoir le code en C :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>

struct t_type{
   long n ;
   char nom[5] ;
   } ;

void HelloWorld(struct t_type *x) 
   {
   int i ;
   for(i=0 ; i< (x->n) ; i++){
      printf("Bonjour %s ! \n", x->nom) ;
      }
   }

Notez bien l'usage des symboles * et -> ! Nous utilisons désormais un paramètre X de type pointeur. Venons-en maintenant à notre code Ada :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
WITH Interfaces.C ;       USE Interfaces.C ; 


PROCEDURE Programme_Ada IS
   PRAGMA Linker_Options("HelloWorld.o") ; 

   TYPE T_Type IS RECORD
      N : Int ; 
      Nom : Char_Array(0..5) ; 
   END RECORD ; 
   PRAGMA Convention(C,T_Type) ; 

   PROCEDURE Hello_World_Ada(X : access t_type) ;
   PRAGMA Import(Convention    => C, 
                 Entity        => Hello_World_Ada, 
                 External_Name => "HelloWorld") ;

   X : ALIASED T_Type ; 
BEGIN
   X.N := 4 ; 
   X.nom := to_c("Bobby") ; 
   Hello_World_Ada(X'access) ;
END Programme_Ada ;

Notre problème est alors réglé. Le programme retrouve son fonctionnement normal mais un détail me chiffonne : en règle générale, vous serez amenés à interfacer des bibliothèques importantes et vous ne pourrez pas modifier le code source de chacune des fonctions C afin qu'il corresponde à vos envies. Ce serait un travail long, fastidieux et surtout risqué. L'idéal serait de ne pas toucher au code source écrit en C. Heureusement, Ada a tout prévu ! Pour cela, revenez au code C fourni au début de cette sous-partie (celui ne faisant pas appel aux pointeurs, c'est-à-dire sans les symboles * et ->). Nous allons simplement indiquer que notre type répond à un PRAGMA Convention légèrement différent :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
WITH Interfaces.C ;       USE Interfaces.C ; 


PROCEDURE Programme_Ada IS
   PRAGMA Linker_Options("HelloWorld.o") ; 

   TYPE T_Type IS RECORD
      N : Int ; 
      Nom : Char_Array(0..5) ; 
   END RECORD ; 
   PRAGMA Convention(C_pass_by_copy,T_Type) ; 

   PROCEDURE Hello_World_Ada(X : t_type) ;
   PRAGMA Import(Convention    => C, 
                 Entity        => Hello_World_Ada, 
                 External_Name => "HelloWorld") ;

   X : T_Type ; 
BEGIN
   X.N := 4 ; 
   X.nom := to_c("Bobby") ; 
   Hello_World_Ada(X) ;
END Programme_Ada ;

Comme vous pouvez le voir à la ligne 11, au lieu d'indiquer que T_Type répond aux conventions du langage C, nous indiquons avec C_Pass_By_Copy qu'il devra également être copié avant d'être transmis à un sous-programme. Plus besoin de pointeurs, de variables ALIASED ou de modification du code source en C. Notre problème est réglé vite fait bien fait.


En résumé :

  • Avant d'utiliser du code C dans votre programme Ada, vous devez compiler votre fichier « .c » afin de générer un fichier « .o ».
  • Pour indiquer le fichier objet utilisé, faites appel au PRAGMA Linker_Options. Attention, seul le fichier objet peut être lu !
  • Les variables ou sous-programmes peuvent être importé grâce au PRAGMA Import.
  • Quelques différences de fond existent entre le C et l'Ada : une chaîne de caractère, en C, contient toujours un caractère supplémentaire pour indiquer la fin de la chaîne.
  • Les types structurés ne peuvent être passés en paramètre d'une fonction C que via un pointeur.