Ctypes enum, how to make it work

The function constant and enum are part of the cstub interface of Ctypes and they are not available in the dynamic mode of Ctypes.

To bind enums in the dynamic mode, the easiest (if unsafe) way is to write an integer view:

type abc = A | B | C
let of_int = function 0 -> A | 1 -> B | 2 -> C | _ -> raise (Invalid_argument "Unexpected C enum")
let to_int = function A -> 0 | B -> 1 | C -> 2
let abc = Ctypes.view ~read:of_int ~write:to_int Ctypes.int

Then the view abc can be used like any other Ctype's view. For instance, binding a function f:abc -> unit can be done with

let f = Foreign.foreign "f" Ctypes.( abc @-> returning void)

If you want to use Ctypes cstubs API, the process is a little more involved.
First, you need to write an ocaml-side type binding generator:

type abc = A | B | C

module Types(T:Cstubs.Types.TYPE) = struct
        let a = T.constant "A" T.int64_t
        let b = T.constant "B" T.int64_t
        let c = T.constant "C" T.int64_t

        let abc = T.enum "letter" [A, a; B, b; C, c]
            ~unexpected:(fun x -> assert false)
end

let () = (* generate the c-side type bindings : *)
  let f = Format.formatter_of_out_channel @@ open_out "types_gen.c" in
  Format.fprintf f {|#include "enum.h"@.|};
  Cstubs.Types.write_c f (module Types)

Executing this ocaml-side generator will generate a C-side type binding generaror (here types_gen.c). This C-side generator can then be executed to generate ocaml bindings to the C type definitions and constants.
At this point, it is possible to use this type bindings to write ctype cstub generator. For instance,
if the generated type bindings was mapped to types_with_abc:

module Tb = Type_bindings
module T = Tb.Types(Types_with_abc)

module P(F:Cstubs.FOREIGN) = struct
  open F
  let f = foreign "f" T.(abc @-> returning Ctypes.void)
end

let () = (* the generation itself is done here: *)
  let cstub = Format.formatter_of_out_channel @@  open_out "cstub_abc.c" in
  let bindings = Format.formatter_of_out_channel @@ open_out "abc_bindings.ml" in
  Cstubs.write_c cstub "abc" (module P);
  Cstubs.write_ml bindings "abc" (module P)

Once executed, this generator will create both cstubs and a new foreign module that can be used to instance the bindings P on the OCaml side:

module Abc = Binding.P(Abc_bindings)
let () =
  Abc.f A
5 Likes