Here’s a working example that shows how to expose simple variant types as C enums.
Ingredients
First, here’s the OCaml module that we’ll expose, with a “day” type and a function that cycles round days (day.ml
):
type t = Sun | Mon | Tue | Wed | Thu | Fri | Sat [@@deriving enum]
let of_int64 i = let Some d = of_enum (Int64.to_int i) in d
let to_int64 d = Int64.of_int (to_enum d)
let tomorrow : t -> t = function
| Sun -> Mon | Mon -> Tue | Tue -> Wed | Wed -> Thu
| Thu -> Fri | Fri -> Sat | Sat -> Sun
Here’s the ctypes
code that builds a view over int64_t
to represent days, and exposes an enum
and a C function that correspond to the OCaml type and function above (bindings.ml
):
let day_t = Ctypes.(typedef (view int64_t ~read:Day.of_int64 ~write:Day.to_int64) "enum day_enum")
module Stubs(I: Cstubs_inverted.INTERNAL) = struct
I.enum ["Sun", 0L; "Mon", 1L; "Tue", 2L; "Wed", 3L;
"Thu", 4L; "Fri", 5L; "Sat", 6L] day_t;;
let tomorrow = I.internal "tomorrow" Ctypes.(day_t @-> returning day_t) Day.tomorrow
end
We also need a stub generator module that’s essentially the same as generate.ml
in the example repository (gen.ml
). The code in the example repository generates a binding for xmlm
; we’ll need to change the names so that it builds days.h
, days.c
and days_bindings.ml
.
We’ll need a one-line module that applies the ctypes bindings functor above to the generated code (link.ml
):
include Bindings.Stubs(Days_bindings)
Finally, we’ll need to initialize the OCaml runtime somehow. The easiest way is to include the init.c
code from the example repository.
Build instructions
Here are the steps to build the example. First, build the code generator from the OCaml code, the ctypes
binding description and the generator module:
ocamlfind opt -g -package ctypes.stubs,ppx_deriving.enum day.ml bindings.ml gen.ml -linkpkg -o gen.exe
Next, run the generator to produce days.h
, days.c
and days_bindings.ml
:
./gen.exe
Finally, link the generated code together with the pieces described above to build a shared library:
ocamlfind opt -g -ccopt -fPIC -output-obj -runtime-variant _pic -package ctypes.stubs days.c days_bindings.ml day.cmx bindings.cmx link.ml init.c -linkpkg -o libdays.so
Example
Here’s a piece of C code that uses the library built above (daytest.c
)
#include "days.h"
#include <stdio.h>
int daydiff(enum day_enum from, enum day_enum to)
{
int i = 0;
while (from != to) {
from = tomorrow(from);
i++;
}
return i;
}
int main()
{
printf("from Mon to Fri: %d days\n", daydiff(Mon, Fri));
printf("from Fri to Mon: %d days\n", daydiff(Fri, Mon));
}
The code can be built, linked against the library, and run in the usual way:
gcc -L . -ldays daytest.c -o daytest.exe
LD_LIBRARY_PATH=. ./daytest.exe
and the output is as expected:
from Mon to Fri: 4 days
from Fri to Mon: 3 days