[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