[ocaml-ctypes] Inverted stubs
Jeremy Yallop
yallop at gmail.com
Wed Oct 1 10:24:00 BST 2014
On 30 September 2014 11:09, Thomas Braibant <thomas.braibant at gmail.com> wrote:
> First, I would like to report that my first attempt to make bindings
> using Ctypes for a sizable library was a success, and I would like to
> congratulate the people behind Ctypes for making such a wonderful
> library.
Thanks! We're very glad to hear of your success.
> Now, I would like to generate inverted stubs that match a third party
> C header file (the same one that I used to make my bindings in the
> first place). Generally speaking, this should be possible I suppose.
> However, one of the functions from this interface returns a struct
> that contains function pointers to all the other functions exposed in
> the interface. I am reasonably convinced that it is out of the scope
> of what Ctypes can do, but I wanted to be sure about that.
I think this is something that ctypes can handle. The trickiest part
is likely to be the memory management: when you pass an OCaml object
to C you need to be careful to ensure that the GC can still see it.
The inverted stubs support is still at an early stage, and in time I
think we'll develop techniques to make memory management easier.
On to the example. If I understand correctly you'd like to build an
interface that looks something like this:
struct callbacks {
int (*arith)(int, int);
int (*print)(char *);
};
struct callbacks *build(int id);
Here's how to build a library with the above interface using ctypes.
First, a definition for the struct:
(* bindings.ml *)
open Ctypes
let callbacks : [`callback] structure typ = structure "callbacks"
let arith = field callbacks "arith"
(Foreign.funptr (int @-> int @-> returning int))
let print = field callbacks "print"
(Foreign.funptr (string @-> returning void))
let () = seal callbacks
For this example I'll use the simplest possible memory management
strategy: a cache which holds references to OCaml values returned from
the 'build' function:
type state = { arith: int -> int -> int; print: string -> unit }
let cache : ([`callback] structure * state) list ref = ref []
The build function itself allocates and initializes a struct, adds it
to the cache, and returns it:
let build id =
let state = { arith = (+);
print = Printf.printf "id: %d; msg: %s\n%!" id } in
let c = make callbacks in
begin
setf c arith state.arith;
setf c print state.print;
cache := (c, state) :: !cache;
addr c
end
The external interface is described with a functor in the usual way.
We expose a single function, "build", by passing name, type, and
implementation to the "internal" function.
module Bindings(I: Cstubs_inverted.INTERNAL) =
struct
let _ = I.internal "build" (int @-> returning (ptr callbacks)) build
end
I won't post all the build details here, but I've put them in a gist
so that you can compile and run the example.
https://gist.github.com/yallop/d196fee1607883493876
It's worth just showing a use of the library here. Here's a little test client:
#include "callback_stubs.h"
#include <caml/callback.h>
#include <stdio.h>
int main(int argc, char **argv)
{
/* Initialize the OCaml runtime before calling the library. */
char *caml_argv[1] = { NULL };
caml_startup(caml_argv);
struct callbacks *c0 = build(0);
struct callbacks *c1 = build(1);
printf("c0->arith(10, 20) => %d\n", c0->arith(10, 20));
printf("c1->arith(30, 40) => %d\n", c0->arith(30, 40));
c0->print("printing via c0");
c1->print("printing via c1");
return 0;
}
The output is as you'd expect:
c0->arith(10, 20) => 30
c1->arith(30, 40) => 70
id: 0; msg: printing via c0
id: 1; msg: printing via c1
I hope that helps,
Jeremy.
More information about the Ctypes
mailing list