[ocaml-ctypes] Creating a C wrapper for an OCaml value
Jeremy Yallop
yallop at gmail.com
Fri Sep 11 18:46:48 BST 2015
On 11/09/2015, EMMA R TURETSKY <eturetsky at wisc.edu> wrote:
> So I guess the meat of my question is how do I pass a value in OCaml to C
> and just create a wrapper around it in a way that I can pass the same value
> back when I call OCaml from C using ctypes? I feel like this should be
> simple and obvious from the manual, but my OCaml understanding is very
> weak.
Passing OCaml values to C is actually a slightly unusual use case,
since most uses of ctypes involve passing C values to OCaml, so it's
probably not especially well documented. It is possible, though, at
least with the master (unreleased) version of ctypes, which you can
install as follows:
opam pin add ctypes git at github.com:ocamllabs/ocaml-ctypes.git
The main thing to be aware of is that the OCaml garbage collector can
run in between the time that you pass the OCaml value to C and the
time that you pass it back to OCaml. The GC can move values around,
changing their addresses, so simply passing the address of a value
directly to C isn't safe. The way to avoid problems is to register
values as roots, then pass the addresses of those roots to C, rather
than the values themseles. Ctypes provides a module Ctypes.Root with
a few functions for root management:
https://github.com/ocamllabs/ocaml-ctypes/blob/758e620d/src/ctypes/ctypes.mli#L417-L432
https://github.com/ocamllabs/ocaml-ctypes/pull/318
The Root API is rather low-level, so I recommend wrapping calls to it
using signatures with stronger typing. Here's a full example of
passing an OCaml module value to C, retrieving it via a separate C
call and invoking some of the module functions. First, some C
functions which store and retrieve OCaml values using 'void *':
/* c_bit.c */
#include <stddef.h>
#include <assert.h>
static void *ocaml_modules[] = {NULL, NULL};
void store_module(int i, void *m)
{
assert (i == 0 || i == 1);
ocaml_modules[i] = m;
}
void *retrieve_module(int i)
{
assert(i == 0 || i == 1);
assert(ocaml_modules[i] != NULL);
return ocaml_modules[i];
}
Next, an OCaml module with low-level interfaces to 'store_module' and
'retrieve_module' and a higher-level wrapper module 'HL' which
enforces the use of roots and the correct types:
(* ocaml_bit.ml *)
module LL =
struct
(* Low-level functions for passing OCaml values to and from C *)
let store_module = Foreign.foreign "store_module"
Ctypes.(int @-> ptr void @-> returning void)
and retrieve_module = Foreign.foreign "retrieve_module"
Ctypes.(int @-> returning (ptr void))
end
module type S =
sig
val say_hello : unit -> unit
val add : int -> int -> int
end
module HL :
sig
type t = (module S)
type handle
val register : t -> handle
val release : handle -> unit
val store : int -> handle -> unit
val retrieve : int -> t
end =
struct
type t = (module S) and handle = unit Ctypes.ptr
let register = Ctypes.Root.create
and release = Ctypes.Root.release
and store = LL.store_module
and retrieve i = Ctypes.Root.get (LL.retrieve_module i)
end
Findlib provides a convenient way to build the example:
ocamlfind c c_bit.c
ocamlfind mklib -o example -package ctypes.foreign c_bit.o ocaml_bit.ml
Finally, here's everything working in the top level:
# #use "topfind";;
- : unit = ()
(* ... *)
# #require "ctypes.foreign";;
(* ... *)
# #load "example.cma";;
# open Ocaml_bit;;
# let handle = HL.register
(module struct
let say_hello () = print_endline "hello"
and add = (+)
end);;
val handle : Ocaml_bit.HL.handle = <abstr>
# HL.store 0 handle;;
- : unit = ()
# let handle2 = HL.register
(module struct
let say_hello () = print_endline "hi"
and add = (-)
end);;
val handle2 : Ocaml_bit.HL.handle = <abstr>
# HL.store 1 handle2;;
- : unit = ()
# module M1 = (val HL.retrieve 0);;
module M1 : Ocaml_bit.S
# module M2 = (val HL.retrieve 1);;
module M2 : Ocaml_bit.S
# M1.say_hello ();;
hello
- : unit = ()
# M2.say_hello ();;
hi
- : unit = ()
I hope that helps a bit! Feel free to follow-up, of course, if
anything's not clear (e.g. how to call back into OCaml from C).
Kind regards,
Jeremy.
More information about the Ctypes
mailing list