[ocaml-ctypes] Defines, and Structs with platform-specific fields

Jeremy Yallop yallop at gmail.com
Mon Oct 20 15:10:42 BST 2014

On 20 October 2014 09:25, Jeremy Yallop <yallop at gmail.com> wrote:
> On 18 October 2014 12:49, Trevor Smith <trevorsummerssmith at gmail.com> wrote:
>> Your suggestion makes sense. If I were to do that -- any suggestions of how
>> to deal with freeing that memory? Ideally, without adding a lifecycle "free"
>> call to the user library. My thoughts would be to store the cstruct
>> reference (malloced by C) in a ref in OCaml, then attach a finalizer to the
>> OCaml ref that would call the corresponding C free.
> That approach should work, I think.
> Another possibility to allocate the struct using Ctypes.allocate or
> Ctypes.allocate_n

Yet another possibility: you can avoid the need to write C altogether
by defining the public interface as a module type, defining the
platform-specific struct definitions as implementations, and using
first-class modules (or the build system, if you prefer) to pick an
appropriate definition for the platform.

For example, you might define the public interface of the uv_loop_s
structure as follows:

  module type UV_LOOP =
    type t
    val t : t structure typ

    (* Define the public fields of the struct here *)
    val data : (unit ptr, t structure) field
    val active_handles : (uint, t structure) field
    (* ... other public fields ... *)

This exposes the fact that the underlying type is a structure, and
that data, active_handles etc. are fields, so you can use the various
ctypes functions that deal with structs (make, getf, setf, etc.).

You can then give a platform-specific definition of the struct that
matches the UV_LOOP interface, and includes (but does not expose) the
private fields:

  module Uv_loop_windows : Uv_loop =
    type t
    let t = structure "uv_loop_s"
    let data = field t "data" (ptr void)
    let active_handles = field t "active_handles" uint
    (* ... other public and private fields ... *)
    let time = field t "time" uint64_t
    let () = seal t

Finally, first-class modules make it possible to pick an appropriate
implementation for the platform:

  module Uv_loop =
   (val if Sys.os_type = "win32"
         then (module Uv_loop_windows : UV_LOOP)
        else if Sys.os_type = "unix"
         then (module Uv_loop_unix : UV_LOOP)
        else failwith "Unsupported platform")

If you're not keen on first-class modules, an alternative approach is
to simply use an interface file uv.mli and an implementation file
uv.ml which is selected from various platform-specific alternatives
(uv_windows.ml, uv_unix.ml etc.) by the build system.

More information about the Ctypes mailing list