Author: Christian.Queinnec@lip6.fr

## Créer et modifier des valeurs Objective CAML en C

L'appel d'une fonction C à partir d'Objective CAML peut modifier ses arguments ou retourner une nouvelle valeur. Cette valeur doit correspondre à un type Objective CAML. Pour les types simples, plusieurs macros C sont prédéfinies pour faciliter les conversions. Pour une valeur d'un type structuré, elle doit être allouée de la bonne taille et ses champs remplis par des valeurs du bon type. Il est en effet facile de faire n'importe quoi en C, ce qui risque de provoquer un arrêt de l'exécution du programme Objective CAML appelant ce morceau de C.
Il est important que les nouvelles valeurs Objective CAML créées en C soient connues du GC d'Objective CAML. Ces valeurs, le temps de leur portée, doivent être considérées comme de nouvelles racines pour le GC. Pour cela plusieurs macros sont définies pour ajouter des racines à conserver par le GC.
De plus, il est possible d'allouer à partir de C des valeurs Objective CAML mais aussi C, dans le tas d'Objective CAML. Cela permet de profiter du mécanisme de récupération automatique de mémoire d'Objective CAML dans les programmes C. Pour des valeurs dont le type n'est connu qu'en C, il est nécessaire d'indiquer au GC leur fonction de finalisation pour la libération d'un bloc de ce type.

### Modification des valeurs Objective CAML

#### Valeurs immédiates

Les macros suivantes permettent de créer des valeurs immédiates de type `value` à partir de valeur C ainsi que d'en modifier:

> `Val_long(l)` | à partir d'un `long int`
> `Val_int(i)` | à partir d'un `int`
> `Val_bool(x)` | à partir d'un `int` (`false` si `x=0`, `true` sinon)
> `Val_true` |
> `Val_false` |
> `Val_unit` |
> `Store_field(b,n,v)` | met la valeur `v` dans le champ `n` d'un bloc `b`
> `Store_double_field(b,n,d)` | idem pour un tableau de flottants

> Figure 12.10 : Création et modification de valeurs immédiates

Il est en outre possible d'utiliser l'une des macros `Field`, `Byte` et `Byte_u` pour affecter les champs d'un bloc : `Store_field(b,n,v)` ÜÞ `Field(b,n) = v`.
Voici un exemple pour inverser une chaîne de caractères :

    #include <caml/mlvalues.h>
    value swap_char(value v, int i, int j)
      { char c=Byte(v,i); Byte(v,i)=Byte(v,j); Byte(v,j)=c; }
    value swap_string (value v)
    {
      int i,j,t = string_length(v) ;
      for (i=0,j=t-1; i<t/2; i++,j--)  swap_char(v,i,j) ;
      return v ;
    }

    # external miroir : string -> string = "swap_string" ;;
    external miroir : string -> string = "swap_string"
    # miroir "abcdefg" ;;
    - : string = "gfedcba"

### Allocation de nouveaux blocs

Il est important d'utiliser les macros suivantes pour allouer des valeurs dans le tas d'Objective CAML, sans cela, les blocs n'auraient pas les informations nécessaires au GC pour les manipuler. La figure 12.11 liste les différentes fonctions d'allocation de blocs dans le tas Objective CAML.

> `alloc(n, t)` | retourne un nouveau bloc de taille `n` et de Tag `t`
> `alloc_tuple(n)` | idem mais avec le Tag 0
> `alloc_string(n)` | retourne une chaîne (non initialisée) de longueur `n`
> `copy_string(s)` | retourne un bloc à partir d'une chaîne C
> `copy_double(d)` | retourne un bloc à partir d'un `double`
> `alloc_array(f, a)` | retourne un bloc tableau à partir d'une fonction `f`
> | de conversion et d'un tableau de pointeurs
> `copy_string_array(p)` | retourne un tableau de chaîne à partir d'un
> | `(char**)`, le tableau p doit se finir par un
> | pointeur nul

> Figure 12.11 : Fonctions d'allocation de blocs

La macro `alloc_array` prend un tableau de pointeurs (`a`) se terminant par le pointeur nul et une fonction `f` qui prend un pointeur et rend une valeur. Elle rend un tableau de valeur obtenu en appliquant `f` à chacun des pointeurs de `a`. La fonction `make_str_array` de l'exemple ci-dessous utilise la macro `alloc_array` pour créer des copies de chacune des chaînes d'un tableau :

    #include <caml/mlvalues.h>
    value make_str (char *s) { return copy_string(s); }
    value make_str_array (char **p) { return alloc_array(make_str,p) ; }

Il est aussi possible de définir des blocs de taille 0, c'est par exemple le cas d'un tableau Objective CAML vide. Un tel bloc est appelé un atome.

    # inspecte [| |] ;;
    ....bloc : taille=0  -  tableau de valeurs (Tag=0) :
    - : '_a array = [||]

Il est possible de l'invoquer en C par la macro `Atom(t)` où `t` est le tag du bloc créé. En fait, de tels blocs sont constants et ne sont pas alloués dans la partie dynamique du tas Objective CAML.

### Des valeurs C dans le tas Objective CAML

On peut souhaiter conserver dans le tas Objective CAML des valeurs qui ne proviennent pas de ce langage et qui ne sont donc pas soumis aux contraintes imposées par Objective CAML pour le GC. Dans ce cas, on utilisera les blocs abstraits.
L'exemple le plus naturel est de manipuler avec Objective CAML des entiers C utilisant 32 (ou 64) bits. Une telle valeur va être conservée dans un bloc de taille 1 (la taille d'une valeur est celle d'un entier long) de tag `Abstract_tag`.

    #include <caml/mlvalues.h>
    #include <stdio.h>

    value Cint_of_OCAMLint (value v)
    {
      value res = alloc(1,Abstract_tag) ;
      Field(res,0) = Long_val(v) ;
      return res ;
    }

    value OCAMLint_of_Cint (value v)  { return Val_long(Field(v,0)) ; }

    value Cplus (value v1,value v2)
    {
      value res = alloc(1,Abstract_tag) ;
      Field(res,0) = Field(v1,0) + Field(v2,0) ;
      return res ;
    }

    value printCint (value v)
    {
      printf ("%d",(long) Field(v,0)) ; fflush(stdout) ;
      return Val_unit ;
    }

    # type cint
     external cint_of_int : int -> cint = "Cint_of_OCAMLint"
     external int_of_cint : cint -> int = "OCAMLint_of_Cint"
     external plus_cint : cint -> cint -> cint = "Cplus"
     external print_cint : cint -> unit = "printCint" ;;

Nous pouvons maintenant utiliser des entiers sans << bit de tags >>, donc dans le format courant en C tout en conservant le GC d'Objective CAML. Par contre, ce ne sont plus des valeurs immédiates ce qui sera plus coûteux pour les opérations arithmétiques simples.

    # let a = 1000000000 ;;
    val a : int = 1000000000
    # a+a ;;
    - : int = -147483648
    # let c = let b = cint_of_int a in plus_cint b b ;;
    val c : cint = <abstr>
    # print_cint c ; print_newline () ;;
    2000000000
    - : unit = ()
    # int_of_cint c ;;
    - : int = -147483648

#### Fonction de finalisation

Il est aussi possible de stocker dans un bloc abstrait des pointeurs vers des blocs alloués hors du tas Objective CAML. Nous savons qu'une valeur Objective CAML qui n'est plus utilisée par le programme est susceptible d'être récupérée par le GC. Mais que devient alors un bloc du tas C référencé par une valeur qui a disparue ? Pour éviter cette fuite de mémoire, nous pouvons associer une fonction dite de finalisation qui est exécutée par le GC avant de récupérer le bloc abstrait.
Pour ce faire nous utiliserons un bloc marqué par le tag `Final_tag`. Un tel bloc contient en plus des valeurs souhaitées, une fonction prenant comme argument le bloc lui-même et qui est exécutée avant la récupération de la valeur.
L'allocation de tels blocs se fait en utilisant `alloc_final (n, f, used, max)` :

- `n` est la taille du bloc. Attention, la première case du bloc est utilisée pour stocker la fonction de finalisation.
- `f` est la fonction de finalisation : `void f (value);`
- `used` : place mémoire (hors du tas Objective CAML) supposée utilisée par la valeur.
- `max` : place mémoire maximale que l'on accepte de ne pas récupérer systématiquement.

Pour être plus efficace, le GC ne récupère pas un bloc dès qu'il devient inutilisé. Le rapport `used/max` permet de spécifier le nombre de blocs abstraits que le GC peut laisser alloués alors qu'ils ne sont plus utilisables.
Le programme suivant manipule des tableaux d'entiers C alloués dans le tas C par appel à `malloc`. Pour que ces tableaux puissent être bien récupérés lors d'un GC d'Objective CAML, la fonction `create` crée un bloc contenant le pointeur vers le tableau d'entiers (type `IntTab`) et la fonction de finalisation `finalize_it`.

    #include <malloc.h>
    #include <stdio.h>
    #include <caml/mlvalues.h>

    typedef struct {
            int size ;
            long * tab ; } IntTab ;

    IntTab *alloc_it (int s)
    {
      IntTab *res = malloc(sizeof(IntTab)) ;
      res->size = s ;
      res->tab = (long *) malloc(sizeof(long)*s) ;
      return res ;
    }
    void free_it (IntTab *p)  { free(p->tab) ; free(p) ; }
    void put_it (int n,long q,IntTab *p)  { p->tab[n] = q ; }
    long get_it (int n,IntTab *p)  { return p->tab[n]; }

    void finalize_it (value v)
    {
      IntTab *p = (IntTab *) Field(v,1) ;
      int i;
      printf("recuperation d'un IntTab par finalisation [") ;
      for (i=0;i<p->size;i++) printf("%d ",p->tab[i]) ;
      printf("]\n"); fflush(stdout) ;
      free_it ((IntTab *) Field(v,1)) ;
    }
    value create (value s)
    {
      value bloc ;
      bloc = alloc_final (2, finalize_it,Int_val(s)*sizeof(IntTab),1000) ;
      Field(bloc,1) = (value) alloc_it(Int_val(s)) ;
      return bloc ;
    }
    value put (value n,value q,value t)
    {
      put_it (Int_val(n), Long_val(q), (IntTab *) Field(t,1)) ;
      return Val_unit ;
    }
    value get (value n,value t)
    {
      long res = get_it (Int_val(n), (IntTab *) Field(t,1)) ;
      return Val_long(res) ;
    }

Les fonctions visibles à partir d'Objective CAML seront : `create`, `put` et `get`.

    # type c_int_array
     external cia_create : int -> c_int_array = "create"
     external cia_get : int -> c_int_array -> int = "get"
     external cia_put : int-> int -> c_int_array -> unit = "put" ;;

On peut désormais manipuler notre nouvelle structure de données :

    # let tab = cia_create 10 and tab2 = cia_create 10
     in for i=0 to 9 do cia_put i (i*2) tab done ;
        for i=0 to 9 do print_int (cia_get i tab) ; print_string " " done ;
        print_newline () ;
        for i=0 to 9 do cia_put (9-i) (cia_get i tab) tab2 done ;
        for i=0 to 9 do print_int (cia_get i tab2) ; print_string " " done ;;
    0 2 4 6 8 10 12 14 16 18
    18 16 14 12 10 8 6 4 2 0 - : unit = ()

On force un GC pour provoquer l'appel à la fonction de finalisation :

    # Gc.full_major () ;;
    recuperation d'un IntTab par finalisation [18 16 14 12 10 8 6 4 2 0 ]
    recuperation d'un IntTab par finalisation [0 2 4 6 8 10 12 14 16 18 ]
    - : unit = ()

La fonction de finalisation peut aussi être utilisée pour fermer un fichier, terminer un processus, etc.

### Paramètres, variables locales des fonctions C et GC

Il est possible d'invoquer le GC depuis une fonction C, les primitives pour le faire sont définies dans le fichier `memory.h`. La fonction `void Garbage_collection_function ()` est la primitive C d'invocation du GC. Selon que le programme est compilé en code-octet ou en code natif le GC mineur ou majeur est invoqué.
Examinons l'exemple suivant et voyons quelle erreur a pu se produire.

    #include <caml/mlvalues.h>
    #include <caml/memory.h>

    value identite (value x)
    {
      Garbage_collection_function() ;
      return x;
    }

    # external id : 'a -> 'a = "identite" ;;
    external id : 'a -> 'a = "identite"
    # id [1;2;3;4;5] ;;
    - : int list = [539709628; 539709622; 539709616; 539709610; 539709604]

La liste passée en paramètre de `id`, donc de la fonction C `identite`, est susceptible d'être déplacée ou récupérée par le GC. Nous avons, dans notre exemple, forcé l'appel au GC, mais toute allocation d'un bloc aurait pu en faire tout autant. La liste, anonyme, passée en argument à `id` a été modifiée par le passage du GC car elle n'est pas connue dans l'ensemble des racines. Pour éviter celà, il faut utiliser une déclaration spécifiques des paramètres et des variables locales, qui sont des `value`, utilisés par les primitives C pour qu'ils soient correctement traités par le GC en utilisant les macro suivantes :

`CAMLparam1(v)` | : | pour une valeur,
`CAMLparam2(v1,v2)` | : | pour deux valeurs,
... |  | ...
`CAMLparam5(v1,``...``,v5)` | : | pour cinq valeurs,
`CAMLparam0` ; | : | obligatoire quand il n'y a pas de valeurs.

Si il y a plus de cinq paramètres de type `value`, les cinq premiers sont déclarés avec la macro `CAMLparam5` et les suivants en utilisant le nombre de fois nécessaires les macros `CAMLxparam1`, ..., `CAMLxparam5`.

`CAMLparam5(v1,``...``,v5);`
`CAMLxparam5(v6,``...``,v10);`
`CAMLxparam2(v11,v12);` | : | pour douze valeurs

Les variables locales de type `value` doivent être elles-aussi déclarées auprès du GC en utilisant les macros `CAMLlocal1`, ..., `CAMLlocal5`. La déclaration d'un tableau de valeurs se fait avec la macro `CAMLlocalN(tab,n)` où `n` est la taille du tableau `tab`. Enfin, si la primitive retourne une `value`, nous utiliserons la macro `CAMLreturn`.
Voici donc la version correcte de l'exemple précédent :

    #include <caml/mlvalues.h>
    #include <caml/memory.h>
    value identite2 (value x)
    {
      CAMLparam1(x) ;
      Garbage_collection_function() ;
      CAMLreturn x;
    }

    # external id : 'a -> 'a = "identite2" ;;
    external id : 'a -> 'a = "identite2"
    # let a = id [1;2;3;4;5] ;;
    val a : int list = [1; 2; 3; 4; 5]

Cette fois nous obtenons le résultat attendu.

### Appel d'une fermeture Objective CAML depuis C

Pour, dans une fonction C, appliquer une fermeture (i.e. une valeur fonctionnelle Objective CAML) à un ou plusieurs arguments, nous utiliserons les macros définies dans le fichier `callback.h`.

`callback(f,v)` | : | une fermeture `f` appliquée à un argument `v`,
`callback2(f,v1,v2)` | : | à deux arguments,
`callback3(f,v1,v2,v3)` | : | à trois arguments,
`callbackN(f,n,tab)` | : | à `n` arguments stockés dans le tableau `tab`.

Toutes ces macros retournent une `value` résultat de l'application.

#### Déclarations de fonctions Objective CAML

L'utilisation des macros `callback` nécessite d'avoir la fonction à appliquer sous la forme d'une fermeture, c'est-à-dire d'une valeur qu'il aura fallu passer en argument de la primitive. Il est aussi possible d'enregistrer une fermeture depuis un programme Objective CAML en lui associant un nom et de la récupérer dans un programme C grâce à son nom.
La fonction `register` du module `Callback` associe un nom (`string`) à une fermeture (de type quelconque donc 'a). Cette fermeture peut-être retrouvée depuis C grâce à la fonction C `caml_named_value` qui prend une chaîne de caractères et rend un pointeur vers la fermeture associée au nom, si elle existe, ou le pointeur nul, sinon.
Voyons cela sur un petit exemple :

    # let plus x y = x + y ;;
    val plus : int -> int -> int = <fun>
    # Callback.register "plus3_ocaml" (plus 3);;
    - : unit = ()

    #include <caml/mlvalues.h>
    #include <caml/memory.h>
    #include <caml/callback.h>

    value plus3_C (value v)
    {
      CAMLparam1(v);
      CAMLlocal1(f);
      f = * caml_named_value("plus3_ocaml") ;
      CAMLreturn callback(f,v) ;
    }

    # external plusC : int -> int = "plus3_C" ;;
    external plusC : int -> int = "plus3_C"
    # plusC 1 ;;
    - : int = 4
    # Callback.register "plus3_ocaml" (plus 5);;
    - : unit = ()
    # plusC 1 ;;
    - : int = 6

Il n'y a pas exactement correspondance entre la déclaration d'une primitive C par external et l'enregistrement d'une fermeture Objective CAML par la fonction `register`. Dans le premier cas, il s'agit d'une déclaration statique, le lien entre les deux noms se fait à l'édition de lien. Alors que dans l'autre cas, il s'agit d'une liaison dynamique qui intervient durant l'exécution. En particulier, rien n'interdit de remplacer la liaison nom--fermeture en remplaçant la fermeture et en modifiant ainsi le comportement des fonctions C utilisant ce nom.