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.
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++,
Des commandes C/C++ dans l'interpréteur ocaml
- Côté ocaml
-
Dans un fichier lescmds.ml, écrire :
|
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ère en entrée,
et rend un entier, mais on peut bien sûr avoir la signature que l'on veut.
- Côté C/C++
-
Dans un fichier cmds.cc, écrire :
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 TRES important de ne pas les oublier pour tout ce qui est de
type value sous peine de bugs grave et introuvable...
Si la fonction ne renvoie pas de résultat, elle se termine par :
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++ :
#include <iostream>
#include <string>
|
et les fichiers d'entête ocaml :
extern "C" {
#include <memory.h>
#include <mlvalues.h>
} // FIN extern "C"
|
- Côté compilation
-
Une compilation C++ standard (en précisant tout de même le chemin pour les
entête ocaml) :
g++ -o cmds.o -I/usr/local/lib/ocaml/caml/ -c cmds.c
|
La compilation ocaml pour fabriquer lescmds.cmi et
lescmds.cmo :
et enfin, l'édition de liens :
ocamlmktop -custom -cc "g++" -o test lescmds.cmo cmds.o
|
Lors de cette dernière étape, on a choisit 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.
- Côté exécution
-
On peut maintenant lancer :
On se retrouve dans l'interpréteur de commande, et on peut lancer :
Maintenant, on aimerait mieux avoir un accueil plus personalisé. Pour cela,
on ajoute une petite fonction dans lescmds.ml :
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é 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 :
Lescmds.intro();;
open Lescmds;;
|
- Et voilà !
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.
- 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 :
ma_cmd "depuis lescmds.ml";;
|
- Côté compilation
-
Pour changer, on décide d'utiliser ocamlopt
(Native-code compiler) plutôt que ocamlc qui produit du
bytecode. On va donc construire lescmds.cmx
à l'aide de la commande :
ocamlopt -cc "g++" -c lescmds.ml
|
- Côté édition de liens
-
On utilise le même fichier C/C++ que précédemment
ocamlopt -cc "g++" -o test2 lescmds.cmx cmds.o
|
- Côté exécution
-
On obtient bien :
Bonjour depuis lescmds.ml !
|
Youpi !
Et comme on en veut toujours plus, on décide maintenant d'ajouter un
main au programme C/C++
- Côté C/C++
-
On ajoute une fonction main à cmds.c :
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.
- Côté compilation/édition de liens
- Rien de changé...
- Côté exécution
-
On obtient bien :
Bonjour depuis lescmds.ml !
Lancement du main...
|
On voit qu'on a commencé par initialiser le module ocaml,
puis lancé de main !
Appeler des commandes ocamlc depuis du C/C++
Pour l'instant, on 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++.
- Côté ocaml
-
Il faut connaître 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 :
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.
- Côté C/C++
-
Voici la fonction qui sert à appeler la commande :
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 !
Voilà. 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...
Liens
-
une
autre page avec beaucoup plus d'informations sur l'échange de différents types de données.
Utilisation
Copyright © 2005-2008 Anne Pacalet
Vous pouvez utiliser ce document selon les termes de la
licence GNU/FDL
ce qui signifie que la reproduction exacte et la distribution intégrale de cet
article est permise sur n'importe quel support d'archivage, pourvu que cette
notice soit préservée.
You can use this document according to the terms of the
GNU/FDL licence
which means that verbatim copying and distribution of this entire article is
permitted in any medium, provided this notice is preserved.