8.4 Utilisation de la commande link

Pour terminer, il convient de parler d'un cas (encore) plus simple où on peut charger des fonctions dynamiquement dans Scilab sans avoir à écrire une interface. On utilise pour cela les fonctions Scilab link et call. Une contrainte est imposée, on doit modifier la fonction C pour que sa liste d'appel ne contienne que des pointeurs.


#include <stdlib.h>
int geom1(int *m, int *n, double *p, int y[])
{
  int i;
  if ( *p >= 1.0 ) 
    {
      cerro("p doit-etre < 1 "); return 1;
    }
  for (i = 0 ; i < (*m) * (*n) ; i++) 
    {
      double z = drand48();
      y[i] = 1;
      while ( z < 1.0 - *p ) { z = drand48(); y[i] ++; }
    }
  return 0;
}

Comme précédemment il faut créer une librairie partagée et la charger cette fois dans Scilab avec la commande link. Il existe donc deux façons de charger une librairie partagée dans Scilab mais qui correspondent à deux usages différents : (addinter rajoute des primitives Scilab et nécessite l'écriture d'interfaces alors que scifunlink ne fait que rajouter des fonctions dans la table des fonctions accessibles par les primitives non linéaires ode, optim,... et la primitive call.

 
-->names=['geom1'] // les points d'entrées à rajouter 
                   // ici un seul. 
-->files = 'geom1.o' // les fichiers objets. 
-->flag = 'c'     
-->ilib_for_link(names,files,[],"c");
   generate a loader file
   generate a Makefile: Makelib
   running the makefile

Dans ce qui précède flag='c' permet de préciser que la fonction geom1 est écrite en C. Cette précision est nécessaire car sur certains systèmes le compilateur Fortran ajoute un ``blanc souligné'' (underscore) au début ou à la fin des noms des points d'entrée ce que ne fait pas le C.

On charge alors dynamiquement cette fonction dans Scilab en exécutant le fichier loader.sce.

 
-->exec loader.sce  // chargement de la librairie
-->link(geom1_path+'/libgeom1.so',['geom1'],'c');
Loading shared executable ./libgeom1.so
shared archive loaded
Linking geom1 (in fact geom1)
Link done

On peut alors appeler la fonction C geom1 depuis Scilab et obtenir sa sortie comme une variable Scilab. On doit envoyer à geom1 les paramètres d'entrée m,n et p et récupérer la matrice de sortie y. C'est la commande call qui permet de faire cet appel. La syntaxe est un peu longue car on doit fournir les valeurs des variables d'entrée, mais aussi leur type C et leur position dans la liste d'appel de geom1. C'est en effet la fonction call qui doit faire le travail de conversion que nous avions précédemment codé dans un interface. Cela donne par exemple :

-->m=3;n=4;p=0.3;

-->y=call("geom1",m,1,"i",n,2,"i",p,3,"d","out",[m,n],4,"i");

Dans cette instruction, on indique en premier argument le nom de la fonction C appelée (c'est aussi le point d'entrée passé à link). Les arguments suivants, jusqu'à la chaîne out, donnent l'information relative aux variables d'entrée. Les arguments situés après la chaîne out donnent, pour leur part, l'information relative aux variables de sortie.

À chaque variable est associé un triplet de paramètres. On interprète par exemple les trois paramètres m,1,"i" comme : passer la valeur m comme premier argument de geom de type int. En sortie, on aura : y (premier argument à gauche du signe égal) est une matrice de dimension [m,n], quatrième paramètre de geom, de type int.

Évidemment, on peut encapsuler l'instruction call(...) dans une fonction Scilab y=geom(m,n,p) et en profiter pour faire des tests (par exemple de dimensions) sur les arguments passés à notre fonction C.


Une autre utilisation de la commande link se fait dans le contexte suivant: certaines primitives Scilab comme par exemple les fonctions ode, fsolve ou optim admettent des arguments de type fonction que l'on appellera fonctions externes. Par exemple pour chercher les zéros de la fonction , on peut construire une fonction Scilab :

 
function y=f(x) 
y=cos(x)*x^2-1

et utiliser ensuite cette fonction f comme argument de la fonction fsolve :

-->y0=10; y=fsolve(y0,f) 
 y  =
 
    11.003833 
-->f(y)
 ans  =
 
    3.819D-14

On peut parfois avoir envie, pour des raisons d'efficacité, de coder ce genre de fonctions dans un langage compilé (C, C++ ou Fortran). Dans ce cas, il ne faut pas vraiment écrire une interface, car la fonction fsolve a déjà été prévue pour fonctionner avec des fonctions f externes. fsolve nous impose par contre une contrainte sur la liste d'appel de la fonction f qu'elle peut reconnaître (voir le help de fsolve). L'exemple précédent pourrait être codé en C sous la forme :


#include <math.h>
void f(int *n,double *x,double *fval,int *iflag) 
{
  *fval = cos(*x)*(*x)*(*x) - 1;
}

On procède comme précédemment pour compiler et charger la fonction C f dans Scilab :

-->ilib_for_link('f','f.o',[],'c');
   generate a loader file
   generate a Makefile: Makelib
   running the makefile
 
-->exec loader.sce

et on indique à fsolve que le problème à résoudre concerne la fonction f écrite en C en lui passant en argument la chaîne "f" :

 

-->y0=10; y=fsolve(y0,"f")
 y  =
 
    11.003833

Cela est valable pour la plupart des primitives Scilab admettant des argument fonctionnels.