[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