[ocaml-ctypes] Pointer to pointer and null check

Jeremy Yallop yallop at gmail.com
Thu Jul 21 22:35:02 BST 2016


Dear Andre,

On 21 July 2016 at 21:40, Andre Nathan <andre at digirati.com.br> wrote:
> I'm trying to write bindings to an API that in simplified form looks
> like this, for a given opaque type `foo_t`:
>
>   int foo_bar(foo_t **ret, foo_t *foo);
>
> In C it's used like this:
>
>   foo_t *foo = foo_new();
>   foo_t *ret;
>   int r = foo_bar(&ret, foo);
>
>   if (r == 0) {
>     if (ret != NULL) {
>       /* success */
>     } else {
>       /* error */
>     }
>   } else {
>     /* not important */
>   }
>
> So if the return is 0, one has to check whether ret is NULL to determine
> failure or success.
>
> I have defined in the OCaml side:
>
>   type foo = unit ptr
>   let foo : foo typ = ptr void
>
>   let foo_bar = foreign "foo_bar" (ptr foo @-> foo @-> returning int)
>
>   let foo_bar x =
>     let ret = allocate foo x in
>     let r = foo_bar ret x in
>     (r, ret)
>
> Given that `ret` was allocated before the call, it will never be NULL
> after the call, so determining if there was an error becomes impossible.

Your code above is correct.  I think the apparent difficulty lies in
the different roles of 'ret' in the C and OCaml fragments.

In C, your ret is a pointer to foo_t:

>   foo_t *ret;

You pass its address to foo_bar, which uses the address to modify ret.
(You're passing '&ret', so foo_bar modifies '*&ret'.)

In OCaml, your ret is a pointer to pointer to foo:

>   let foo : foo typ = ptr void
[...]
>     let ret = allocate foo x in

and so, correctly, you pass ret (rather than its address) to foo_bar,
which can consequently only modify *ret, not ret.  (You're passing
'ret', so foo_bar modifies '*ret'.)

So to check for an error you should compare *ret, not ret, with NULL, like this:

   ptr_compare !@ret null = 0

> but of course I can't call `addr` on a `Ctypes_static.pointer`. Also,
> apparently the way to check for NULL is to use `ptr_opt`,

Indeed, that's an option, and perhaps better than explicit null
comparisons.  Here's how you adapt your code to use ptr_opt:

  type foo_opt = unit ptr option
  let foo_opt : foo_opt typ = ptr_opt void

  let foo_bar = foreign "foo_bar" (ptr foo_opt @-> foo_opt @-> returning int)

  let foo_bar x =
    let ret = allocate foo_opt (Some x) in
    let r = foo_bar ret (Some x) in
    (r, !@ret)

Now foo_bar returns a pair

    int * unit ptr option

where the second element is 'None' if *ret was NULL after the call to
foo_bar and 'Some p' for some p otherwise.

I hope that helps,

Jeremy.


More information about the Ctypes mailing list