[ocaml-ctypes] Variadic Functions

Jeremy Yallop yallop at gmail.com
Mon Oct 14 14:47:54 BST 2013


Hi Florian,

There are a few ways to approach this.  I think my preferred approach
is to use string_opt, which maps OCaml's None to a null pointer.  You
can then create null-terminated arrays pretty easily:

   # Array.of_list string_opt [Some "foo"; Some "bar"; None];;
   - : string option Array.t = { 0x27d8fc0, 0x27d9020, (nil) }

(There's a small pitfall with the string and string_opt types that you
may already be aware of: ctypes always passes a copy of OCaml strings
to C, so if the C code is writing to the strings then you should use
'char ptr' or some similar type instead.)

A slightly more raw approach, if you have an existing array of the
appropriate length, is to simply assign to the last element:

   # a;;
   - : int ptr Array.t = { 0x27d9f30, 0x27d9090 }
   # a.(Array.length a - 1) <- from_voidp int null;;
   - : unit = ()
   # a;;
   - : int ptr Array.t = { 0x27d9f30, (nil) }

Hope this helps,

Jeremy.

On 11 October 2013 17:07, Florian Pichlmeier
<florian.pichlmeier at mytum.de> wrote:
> Hi,
>
> thanks a lot for that quick response, it worked like a charm.
> The only problem now is that the other receive function expects
> the message to be null terminated. How do i get that with ctypes,
> that the last element in the array is NULL?
>
> Thanks again for your work,
>
> Florian
>
> Jeremy Yallop <yallop at gmail.com> wrote:
>
>
>> Hi Florian,
>>
>> The problem is the array argument; things should work better if you
>> change the binding to something like this:
>>
>>   let stub = foreign "zstr_sendx_array"
>>       ptr void @-> ptr string @-> size_t @-> returning int)
>>
>> You'll be able to call stub by passing 'Array.start c_array'.
>>
>> Kind regards,
>>
>> Jeremy.
>>
>> On 11 October 2013 15:46, Florian Pichlmeier
>> <florian.pichlmeier at mytum.de> wrote:
>>
>> > Hello Jeremy,
>> >
>> > i tried the approach with a more wrappable function.
>> >
>> > int
>> > zstr_sendx_array (void *socket, char **string, size_t nmsg)
>> > {
>> >     zmsg_t *msg = zmsg_new ();
>> >     for (size_t i=0;i<nmsg;i++) {
>> >         zmsg_addstr (msg, *string);
>> >         (*string)++;
>> >     }
>> >     return zmsg_send (&msg, socket);
>> > }
>> >
>> > and call it that
>> >
>> > let sendx socket msg_list =
>> >   let c_array : string Ctypes.array = Array.of_list string msg_list
>> >   in
>> >   let stub = foreign "zstr_sendx_array"
>> >     ((ptr void) @-> (array (List.length msg_list) string ) @-> size_t @-> returning int)
>> >   in
>> >   match stub socket c_array (Size_t.of_int(List.length msg_list))with
>> >   | _ -> ()
>> >
>> > But i get this error
>> >
>> > Fatal error: exception Static.Unsupported("Unsupported argument type")
>> >
>> > Do you have an idea what my mistake is?
>> >
>> > Thank you.
>> >
>> > Florian
>> >
>> > Jeremy Yallop <yallop at gmail.com> wrote:
>> >
>> >
>> >
>> > > On 24 September 2013 12:43, Florian Pichlmeier
>> > > <florian.pichlmeier at mytum.de> wrote:
>> > >
>> > >
>> > > > Some of these functions are variadic functions, like this one
>> > > >
>> > > > //  Create new poller
>> > > > CZMQ_EXPORT zpoller_t *
>> > > >     zpoller_new (void *reader, ...);
>> > > >
>> > > > Is there a way with ctypes to emulate this signature?
>> > > >
>> > > >
>> > > >
>> > > >
>> > >
>> > > The short answer, unfortunately, is "no".  This type of function is
>> > > rather tricky to wrap.  This isn't due to a limitation of ctypes; it's
>> > > because there isn't a way to write wrappers for variadic functions in
>> > > standard C [0, 1]
>> > >
>> > > Ideally the C library interface should provide a more wrappable
>> > > interface in addition to the variadic function, e.g.
>> > >
>> > >     zpoller_t *zpoller_new_vec(void **readers);    /* argument is a
>> > > null-terminated array of pointers */
>> > >
>> > > or
>> > >
>> > >     zpoller_t *zpoller_new_vec(void **readers, size_t nreaders);
>> > >
>> > > It might be worth sending a pull request to the czmq maintainers to
>> > > modify the interface.
>> > >
>> > > If you're not too concerned by portability you can use the fact that
>> > > some C implementations (e.g. GCC on Linux) have the same calling
>> > > convention for variadic and regular functions.  Here ctypes offers an
>> > > advantage over handwritten bindings: since C types are exposed as
>> > > OCaml values you can construct a signature dynamically according to
>> > > the number of arguments you want to pass to  the function.  For
>> > > example, you might call the function using any of the signatures
>> > >
>> > >     ptr void @-> returning (ptr (zpoller_t))
>> > >
>> > >     ptr void @-> ptr void @-> returning (ptr (zpoller_t))
>> > >
>> > >     ptr void @-> ptr void @-> ptr void @-> returning (ptr (zpoller_t))
>> > >
>> > > and so on.  In fact, it ought to be possible (but probably not easy)
>> > > to write a function which accepts a list of pointers, constructs a
>> > > suitable signature for zpoller_new and calls the function.
>> > >
>> > > Jeremy.
>> > >
>> > > [0] http://c-faq.com/varargs/handoff.html
>> > > [1] http://c-faq.com/varargs/invvarargs.html
>> > >
>> > >
>> > >
>> > >
>> > >
>> >
>>
>>


More information about the Ctypes mailing list