• Aucun résultat trouvé

Conventions Prolog pour la communication des données

Dans le document Apprendre et enseigner Prolog pdf (Page 195-200)

Table de descripteurs

7.7. Ajout de fonctions externes à appel direct

7.7.2. Conventions Prolog pour la communication des données

Il s'agit ici de pouvoir exprimer en Prolog toutes les informations nécessaires pour pouvoir faire l'appel d'une fonction C, concernant ses arguments et sa valeur de retour. On s'intéressera donc :

- au mode de passage des arguments,

- au type des arguments et de la valeur de retour de la fonction, - à la valeur initiale des arguments,

- à la valeur résultat des arguments et de la fonction, - à d'autres informations techniques liées au type de l'objet.

Plus qu'au mode de passage d'une donnée, on s'intéressera au rôle de la donnée, à savoir : donnée d'entrée et/ou de sortie.

En effet en Prolog, une donnée se voyant affecter une valeur, ne pourra plus en changer (sauf par backtracking, mais c'est assimilable en comparant avec C, à une autre exécution). Une procédure Prolog qui attendrait une valeur en entrée et un résultat en sortie, devrait utiliser deux données. C'est là la différence essentielle entre Prolog et C, il est donc important de connaître si une donnée (initialisée ou pas) doit changer de valeur au cours de l'exécution de la fonction C. Dans la suite de ce chapitre, on fera donc la distinction entre argument avec valeur de retour et argument sans valeur de retour. Le mode de passage par adresse ou par valeur en sera déduit automatiquement.

Il est possible de passer en paramètre:

1. un entier long avec ou sans valeur de retour,

2. un réel double précision avec ou sans valeur de retour, 3. une chaîne de caractères avec ou sans valeur de retour,

4. un tableau d'entiers, de chaînes de caractères ou de réels double précision, avec ou sans valeur de retour.

Il est possible d'attendre en résultat de la fonction: 1. un entier long,

2. un réel double précision.

Voyons à présent, quelles sont les conventions. Pour les illustrer sur des exemples, on supposera qu'un lien a été déclaré entre le prédicat relaiProlog et la fonction fonctionC.

7.7.2.1. Convention pour des paramètres sans valeur de retour

Dans ce cas, il n'est pas utile de spécifier le type de la donnée, Prolog le connaît puisque la donnée a déjà une valeur.

Les données de type entier, réel ou chaîne sont représentées par la donnée Prolog elle même.

Les données de type tableau homogène d'entiers, de réels ou de chaînes sont représentées par une liste Prolog (terminée éventuellement par nil), d'entiers de réels ou de chaînes.

Par exemple:

:relaiProlog(180, 3.14e0, "pi", 2.3.5.7.11.13.17.nil, "b"."a"."ba".nil)

est équivalent aux instructions C suivantes:

{ long arg1 = 180L; double arg2 = 3.14; char arg3[] = {'p','i','\0'};

long arg4[] = {2,3,5,7,11,11,13,17}; char *arg5[] = {"b","a","ba"};

fonctionC(arg1,arg2,arg3,arg4,arg5); }

Les chaînes et les tableaux sont toujours passés par adresse selon la convention habituelle en C. Prolog effectue de toute façon une copie des arguments dans une zone intermédiaire dont la taille peut être paramétrée sur la ligne de commande (voir le chapitre 2 du Manuel d'Utilisation).

7.7.2.2. Convention pour le retour de la fonction

Il est nécessaire de connaître le type du retour de la fonction, et de disposer d'un terme Prolog qui sera unifié avec sa valeur. La convention est la suivante:

le terme qui représente le résultat doit être un doublet : < type_résultat, variable_résultat >

- dont le premier élément type_résultat est une chaîne qui indique le type: "I" pour un retour entier, "R" ou "X" pour un retour réel.

- dont le deuxième élément variable_résultat sera unifié avec la valeur retournée par la fonction (entier ou réel).

7.7.2.3. Convention pour des paramètres avec valeur de retour

Pour une donnée C avec valeur de retour attendue, il est nécessaire d'avoir deux données Prolog: une première qui doit être connue au moment de l'appel et qui sert à transmettre la valeur initiale à la fonction C, une deuxième qui sera unifiée avec la nouvelle valeur obtenue de la fonction C.

On distingue parmi les types de données : des types simples et des types qui nécessitent une allocation de zone mémoire.

Note : Certaines conventions avec valeur(s) de retour, spécifient des tailles de zones mémoire à allouer pour ranger le(s) résultat(s) des fonctions C appelées. L'utilisateur doit donc impérativement s'assurer que les zones sont suffisantes pour les résultats attendus. Il s'assurera également que les types des arguments sont cohérents avec les déclarations faites en C.

La convention pour les données de type simple, c'est à dire les entiers et les réels, est la suivante :

l'argument effectif du prédicat Prolog est un doublet : < valeur_initiale, variable_résultat >

- dont le premier élément valeur_initiale est la valeur initiale de la zone dont l'adresse est passée en paramètre à la fonction C. Si la valeur est quelconque on peut mettre comme valeur initiale la marque "I" (pour indiquer un entier) ou "R" (pour un réel).

- dont le deuxième élément variable_résultat sera unifié avec la nouvelle valeur de l'argument rendue par la fonction (entier ou réel).

Par exemple:

:relaiProlog(<180,x>, <3.14e0,y>)

est équivalent aux instructions C suivantes:

{ long arg1 = 180L; double arg2 = 3.14; fonctionC(&arg1,&arg2); }

suivies des affectations des variables x et y avec les nouvelles valeurs de arg1 et arg2.

La convention pour les données qui nécessitent une zone mémoire dépend du type de la donnée :

Pour une chaîne de caractère, il est nécessaire d'avoir une zone de caractères, dont on doit définir la taille, susceptible de contenir dans un premier temps la chaîne initiale puis la chaîne résultat:

l'argument effectif du prédicat Prolog est un triplet:

< valeur_initiale, variable_résultat, taille_max_résultat >

- dont l'élément taille_max_résultat est un entier spécifiant la taille de la zone à allouer pour l'opération, dont l'adresse est transmise à la fonction C.

- dont l'élément valeur_initiale est la valeur initiale copiée dans la zone allouée pour l'opération.

- dont l'élément variable_résultat sera unifié avec le résultat (chaîne Prolog). Par exemple:

:relaiProlog(<"pi",x,100>)

est équivalent aux instructions C suivantes:

{ char arg1[100]; strcpy(arg1,"pi"); fonctionC(arg1); }

Pour un tableau d'entiers ou de réels, il est nécessaire d'avoir une zone d'entiers ou de réels qui tient lieu de tableau, dont on définit le nombre d'éléments, et susceptible de contenir dans un premier temps les éléments du tableau en entrée puis les éléments du tableau en sortie:

l'argument effectif du prédicat Prolog est un quadruplet:

< valeur_initiale, variable_résultat, taille_résultat, taille_tableau >

- dont l'élément taille_tableau est un entier spécifiant le nombre d'éléments de la zone à allouer pour l'opération, dont l'adresse est transmise à la fonction C. - dont l'élément taille_résultat indique le nombre d'éléments valides du tableau à prendre en compte pour le retour.

- dont l'élément valeur_initiale est la liste Prolog terminée par nil, des valeurs initiales des éléments du tableau. Le type de ces éléments détermine le type du paramètre.

- dont l'élément variable_résultat sera unifié avec le résultat (liste Prolog terminée par nil, d'entiers ou de réels).

Par exemple:

:relaiProlog(<2.4.6.8.12,x,1,5>, <0e0.nil,y,5,5>)

est équivalent aux instructions C suivantes:

{ long arg1[]={2,4,5,6,8,12}; double arg2[5]; arg2[0] = 0.0;

fonctionC(arg1,arg2); }

suivies des affectations des variables x et y avec les nouvelles valeurs de arg1 et arg2.

Pour un tableau de chaînes de caractères, il est nécessaire d'avoir une zone de pointeurs pour représenter le tableau, dont on doit définir le nombre d'éléments, et éventuellement une zone de caractères pour stocker tous les caractères de toutes les chaînes du tableau, dont on doit également définir la taille.

l'argument effectif du prédicat Prolog est un quadruplet:

< valeur_initiale, variable_résultat, nbre_max_de_caractères, taille_tableau >

- dont l'argument taille_tableau est un entier spécifiant le nombre d'éléments de la zone à allouer pour l'opération, dont l'adresse est transmise à la fonction C. La fin du tableau doit être indiquée par le pointeur NULL après le dernier élément. - dont l'argument valeur_initiale est une liste Prolog terminée par nil de chaînes pointées par le tableau en entrée ("".nil au minimum).

- dont l'argument est un entier, il offre la possibilité de laisser à Prolog la gestion (allocation et libération) du buffer de caractères pour les chaînes en sortie. Si l'utilisateur gère le(s) espace(s) des chaînes en sortie, nbre_max_de_caractères doit valoir 0. Si l'utilisateur laisse à Prolog le soin d'allouer et par la suite libérer un buffer de caractères, nbre_max_de_caractères doit être non nul, il indique alors le nombre de caractères total maximal nécessaire pour stocker tous les caractères de toutes les chaînes du tableau. La valeur du premier élément du tableau (pointeur de chaîne de caractères) sera dans ce cas l'adresse de cet espace (où est copiée la première chaîne en entrée).

- dont l'élément variable_résultat sera unifié avec le résultat, une liste Prolog terminée par nil, de chaînes.

Par exemple:

:relaiProlog(<"il"."était"."une"."fois",x,100,5>)

est équivalent aux instructions C suivantes:

{ char * arg1[5]; char buffer[100]; strcpy(buffer,"il"); arg1[0] = buffer;

arg1[1] = "était"; arg1[2] = "une"; arg1[3] = "fois"; fonctionC(arg1); }

suivies de l'affectation de la variable x avec les nouvelles valeurs de arg1. 7.7.3. Exemple : méthode simple pour appeler une fonction C

Nous montrons ici des exemples avec divers types d'arguments, utilisant des fonctions déclarées par l'utilisateur, et une fonction système.

#include <stdio.h> #include <string.h> #include "proext.h" #define REAL double #define INTEGER long

static REAL complete_example(ii1,io1,ri1,ro1,si1,so1,tii1, tri1,tio1,tro1,tsi1,tso1,reserved) /*---*/ INTEGER ii1,*io1; REAL ri1,*ro1; char *si1; char *so1;

REAL *tri1; REAL *tro1; INTEGER *tio1,*tii1; char **tsi1,**tso1; int reserved; { int i;

printf("ii1= %d\n",ii1); /*integer in input*/ printf("io1= %d\n",*io1); /*integer in output*/ printf("ri1= %g\n",ri1); /*real in input*/ printf("ro1= %g\n",*ro1); /*real in output*/ printf("si1= %s\n",si1); /*string in input*/ printf("so1= %s\n",so1); /*string in output*/

for (i=0;i<2;i++) /*integer array*/

printf("tii1[%d]= %d\n",i,tii1[i]); /*in input*/

for (i=0;i<2;i++) /*real array*/

printf("tri1[%d]= %g\n",i,tri1[i]); /*in input*/

for (i=0;i<3;i++) /*integer array*/

printf("tio1[%d]= %d\n",i,tio1[i]); /*in output*/

for (i=0;tsi1[i];i++) /*string array*/

printf("tsi1[%d]= %s\n",i,tsi1[i]); /*in input*/

for (i=0;i<3;i++) /*real array*/

printf("tro1[%d]= %g\n",i,tro1[i]); /*in output*/

*io1 = 3333; /*integer in output*/

*ro1 = - 8888.; /*real in output*/

strcpy(so1,"bonjour"); /*string in output*/

for (i=0;i<10;i++) /*integer array*/

tio1[i] = i+200; /*in output*/

for (i=0;i<10;i++) /*real array*/

tro1[i] = (float) i +200.; /*in output*/

for (i=0;tso1[i];i++) /*string array*/

printf("strin(%d)=%s\n",i,tso1[i]);/*in output*/ if(reserved) /*space for strings reserved by prolog */

{

strcpy(tso1[0],"Zone1"); /*first pointer(tso1[0])*/ /*initialized by Prolog */ tso1[1] = tso1[0]+6; /*others pointers initialized*/

/*by the user */ strcpy(tso1[1],"Zone2");

tso1[2] = NULL; /*end of array */

}

else /*space for strings reserved by the user */ {

tso1[0] = "Zone1";

tso1[1] = (char *) malloc(10); strcpy(tso1[1],"Zone2");

tso1[2] = NULL; }

return 1234.; /*returned value */ }

static REAL average(tab,n)/* ---*/ INTEGER *tab,n;

Dans le document Apprendre et enseigner Prolog pdf (Page 195-200)