IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Appel de commandes C/C++ dans OCaml

The Caml Language

Cette page résume différents moyens de mélanger du ocaml et du C/C++. Pour plus d'information, voir directement le manuel ocaml. ♪

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Introduction

Les exemples fournis ici permettent de voir comment :

  • appeler des fonctions C/C++ depuis ocaml ;
  • intégrer ces nouvelles commandes dans l'interpréteur ;
  • appeler des commandes ocaml depuis du C/C++.

I. Des commandes C/C++ dans l'interpréteur OCaml

I-A. Côté OCaml

Dans un fichier lescmds.ml, écrire :

 
Sélectionnez
external ma_cmd : string -> int = "CamlMaCmd"

On définit ainsi la commande caml ma_cmd qui fait appel à la fonction C/C++ CamlMaCmd. Ici, la fonction prend une chaîne de caractères en entrée, et rend un entier, mais on peut bien sûr avoir la signature que l'on veut.

I-B. Côté C/C++

Dans un fichier cmds.cc, écrire :

 
Sélectionnez
extern "C" value CamlMaCmd (value v_str) {
  CAMLparam1 (v_str);
  CAMLlocal1 (v_res);

  std::string str = String_val (v_str);
  std::cout << "Bonjour " << str << " !\n";

  int l = str.size();
  v_res = Val_int (l);

  CAMLreturn (v_res);
}

Tout d'abord, comme les fonctions appelées par ocaml sont des fonctions C, il ne faut pas oublier de le préciser si on compile (comme moi) en C++.
Les macros CAMLparam1, CAMLlocal1, CAMLreturn sont utilisées par le ramasse-miettes (garbage collector) de ocaml. Il est TRÈS important de ne pas les oublier pour tout ce qui est de type value sous peine de bugs graves et introuvables… Si la fonction ne renvoie pas de résultat, elle se termine par :

 
Sélectionnez
CAMLreturn (Val_unit);

Les macros String_val et Val_int permettent de traduire des objets de type value de et vers des types C. Les plus courantes sont fournies, mais on peut aussi écrire ses propres fonctions si on a des types plus compliqués.
Avant de pouvoir compiler, il faut bien sûr ajouter les fichiers d'entête C++ :

 
Sélectionnez
#include <iostream>
#include <string>

et les fichiers d'entête OCaml :

 
Sélectionnez
extern "C" {
#include <memory.h>
#include <mlvalues.h>
} // FIN extern "C"

I-C. Côté compilation

Une compilation C++ standard (en précisant tout de même le chemin pour les entêtes ocaml) :

 
Sélectionnez
g++ -o cmds.o -I/usr/local/lib/ocaml/caml/ -c cmds.c

La compilation ocaml pour fabriquer lescmds.cmi et lescmds.cmo :

 
Sélectionnez
ocamlc -c lescmds.ml

et enfin, l'édition de liens :

 
Sélectionnez
ocamlmktop -custom -cc "g++" -o test lescmds.cmo cmds.o

Lors de cette dernière étape, on a choisi ici de faire l'édition de liens avec l'interpréteur de commande (toplevel system). On verra plus loin qu'on peut aussi produire un simple exécutable.

I-D. Côté exécution

On peut maintenant lancer :

 
Sélectionnez
./test

On se retrouve dans l'interpréteur de commande, et on peut lancer :

 
Sélectionnez
Lescmds.ma_cmd "Anne";;

Maintenant, on aimerait mieux avoir un accueil plus personnalisé. Pour cela, on ajoute une petite fonction dans lescmds.ml :

 
Sélectionnez
let intro () =
   Printf.printf "\n  Bienvenue !\n\n" ;
   Printf.printf "Pour tester  l'outil, tapez : ma_cmd \"votre nom\";;\n" ;
   Printf.printf "Pour quitter l'outil, tapez : #quit;; (avec le #)\n\n"

On veut qu'elle soit lancée automatiquement, et on en profite aussi pour ouvrir le module Lescmds afin de pouvoir taper directement ma_cmd au lieu de Lescmds.ma_cmd. Pour cela, on crée un fichier .ocamlinit qui contient :

 
Sélectionnez
Lescmds.intro();;
open Lescmds;;

Et voilà ! ça marche :-)

II. Produire un exécutable contenant du C/C++ et du ocamlc

Maintenant, on ne veut plus interpréter des commandes, mais simplement produire un exécutable.

II-A. Côté ocamlc

Pour produire un exécutable, il faut avoir quelque chose à exécuter. On va donc commencer par ajouter quelque chose à exécuter dans lescmds.ml, par exemple :

 
Sélectionnez
ma_cmd "depuis lescmds.ml";;

II-B. Côté compilation

Pour changer, on décide d'utiliser ocamlopt (compilateur en code natif) plutôt que ocamlc qui produit du bytecode. On va donc construire lescmds.cmx à l'aide de la commande :

 
Sélectionnez
ocamlopt -cc "g++" -c lescmds.ml

II-C. Côté édition de liens

On utilise le même fichier C/C++ que précédemment

 
Sélectionnez
ocamlopt -cc "g++" -o test2 lescmds.cmx cmds.o

II-D. Côté exécution

On obtient bien :

 
Sélectionnez
$ ./test2
Bonjour depuis lescmds.ml !

Et comme on en veut toujours plus, on décide maintenant d'ajouter un main au programme C/C++

II-E. Côté C/C++

On ajoute une fonction main à cmds.c :

 
Sélectionnez
int main (int argc, char * argv[]) {
  caml_main (argv);
  std::cout << "Lancement du main...\n";
  return 0;
}

Il faut noter qu'on doit forcement commencer par un appel à caml_main.

Rien à changer du côté de l'édition de liens…

II-F. Côté exécution (bis)

On obtient bien :

 
Sélectionnez
Bonjour depuis lescmds.ml !
Lancement du main...

On voit qu'on a commencé par initialiser le module OCaml, puis lancé de main !

III. Appeler des commandes ocamlc depuis du C/C++

Pour l'instant, on a appelé une fonction C/C++ depuis OCaml, voyons maintenant comment faire le contraire, c'est-à-dire appeler une commande OCaml depuis une fonction C/C++.

III-A. Côté OCaml

Il faut connaitre les commandes que l'on va vouloir appeler, car il faut leur ajouter une callback. On pense par exemple qu'il est beaucoup trop difficile de multiplier par 2 en C/C++ ;-) aussi décide-t-on de faire une petite commande ocaml :

 
Sélectionnez
let fois2 x = 2*x
let _ = Callback.register "caml_fois2" fois2 ;;

La seconde ligne sert à enregister la commande fois2, et à indiquer le nom qu'on souhaite lui donner côté C/C++ (caml_fois2 ici). Ce nom peut bien sûr être le même que le nom de la commande si on le souhaite.

III-B. Côté C/C++

Voici la fonction qui sert à appeler la commande :

 
Sélectionnez
int Fois2 (int x) {
  CAMLlocal2 (v_x, v_res);
  char * cmd = "caml_fois2";
  v_x =  Val_int (x);
  v_res = callback (*caml_named_value (cmd), v_x);
  int res = Int_val (v_res);
  std::cout << cmd << " (" << x << ") = " << res << "\n";
  return res;
}

On y retrouve les fonctions de traduction des types C/C++ vers le type value de ocaml, et l'appel à la fonction (callback) que l'on a retrouvée à partir de son nom (caml_named_value)

Exercice :

  • ajouter un appel à cette merveilleuse fonction dans le main ;
  • compiler/faire l'édition de liens ;
  • exécuter ;
  • et… ça marche !

Conclusion

J'espère que cette page a pu vous aider. Si vous avez noté des erreurs ou des compléments à apporter, merci de me les signaler…

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Anne Pacalet. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.