Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OCaml ctypes idiomatic way to deal with out parameters

Tags:

ocaml

I'm writing OCaml wrappers for a few C functions that use the out-parameter idiom and return an error code. I've been wrapping them by allocating a C array on the OCaml side using Ctypes.allocate_n. And then copying the contents out into an OCaml type.

I feel like I'm hacking around a problem that Ctypes or some other module already solves in another way, here's an example.

gethostname(2) has the following type:

int gethostname(char *name, size_t len);

Here's out_parameter.mli for the wrapped gethostname function.

val gethostname : int -> [> `Ok of string | `Error of int];;

Here's the implementation

open Core.Std;;
let (@->) = Ctypes.(@->);;
let returning = Ctypes.returning;;
open Foreign;;

let gethostname size =
  let size' = Unsigned.Size_t.of_int size in
  let c_gethostname =
    foreign "gethostname" (Ctypes.ptr Ctypes.char @-> Ctypes.size_t @-> returning Ctypes.int) in
  let buf = Ctypes.allocate_n Ctypes.char ~count:size in
  let err = c_gethostname buf size' in
  match err with
  | 0 -> (
    `Ok (Ctypes.string_from_ptr buf ~length:size)
    )
  | _ -> `Error err;;

let main () =
  Printf.printf "%s\n" (match gethostname 1000 with
  | `Ok hostname -> hostname
  | `Error _ -> "error getting hostname");;

let () = main ();;

And for the sake of compeleteness I compiled out_parameter.native with this command.

$ corebuild -pkg ctypes.foreign out_parameter.native

The code does work and returns the hostname, with trailing null bytes stripped.

$ ./out_parameter.native
MY-HOSTNAME

$ ./out_parameter.native | sed -e 's/\x0/@/g'
MY-HOSTNAME
like image 926
Gregory Nisbet Avatar asked Aug 16 '16 22:08

Gregory Nisbet


1 Answers

It looks like your code works and is idiomatic. There are two ways to represent errors:

  • you can throw an exception.
  • you can use a result type. If you do that, I would recommend using the "new" builtin result type (that has been backported for older versions of OCaml in the result package).

Generally speaking, you are responsible for writing code that supports that error convention (the match err part in your code). It is often possible to write a couple combinators to reduce the boilerplate.

However, there are a couple shortcuts provided by ctypes if you use Cstubs (code generation):

  • errno_policy can implement libc's errno convention.
  • concurrency_policy can tune the C code so that it correspond to a given concurrency model (lwt, etc). This is not about error conventions, but is somewhat related.

If you stick to Foreign (which makes your build system simpler than Cstubs), then I'd recommend doing it by hand, or extracting a couple combinators if you have a lot of similar functions.

like image 153
Étienne Millon Avatar answered Oct 29 '22 19:10

Étienne Millon