[Libguestfs] [nbdkit PATCH 2/2] ocaml: Implement .list_exports and friends

Eric Blake eblake at redhat.com
Tue Sep 1 14:27:39 UTC 2020


On 9/1/20 9:13 AM, Richard W.M. Jones wrote:
> On Tue, Sep 01, 2020 at 08:41:40AM -0500, Eric Blake wrote:
>> On 9/1/20 8:25 AM, Eric Blake wrote:
>>> Fairly straightforward. I'd love for type export to be a bit more
>>> flexible to make description optional, but could not figure out how to
>>> decode that from the C side of things, so for now this just requires
>>> the caller to supply a description for all exports during
>>> .list_exports.
>>>
>>

> OCaml blocks are lists of values allocated on the OCaml heap:
> https://rwmj.wordpress.com/2009/08/05/ocaml-internals-part-2-strings-and-other-types/
> and those values may be ints or pointers.
> 
> Therefore the easiest way to tell if something is None from C is
> to compare it with Val_int(0), ie:
> 
>    value v = /* something which has type ‘string option’ */;
> 
>    if (v == Val_int(0)) /* It's None */
>      printf ("None\n");
>    else {
>      /* It's Some string, set sv to the string value */
>      value sv = Field (v, 0);
>      printf ("Some %s\n", String_val (sv));
>    }
> 
> The weird casting macros have the form: To_from()
> 
> Also that String_val is only valid for a short period of time,
> basically until the next OCaml allocation in the current thread.

That's helpful, as well.

> 
>> +++ w/plugins/ocaml/ocaml.c
>> @@ -332,11 +332,12 @@ list_exports_wrapper (int readonly, int
>> is_tls, struct nbdkit_exports *exports)
>>
>>     /* Convert exports list into calls to nbdkit_add_export. */
>>     while (rv != Val_int (0)) {
>> -    const char *name, *desc;
>> +    const char *name, *desc = NULL;
>>
>>       v = Field (rv, 0);          /* export struct */
>>       name = String_val (Field (v, 0));
>> -    desc = String_val (Field (v, 1));
>> +    if (Is_block (Field (v, 1)))
>> +      desc = String_val (Field (Field (v, 1), 0));
>>       if (nbdkit_add_export (exports, name, desc) == -1) {
>>         caml_enter_blocking_section ();
>>         CAMLreturnT (int, -1);
> 
> Seems OK.  I'm guessing this crashes?

Nope, it worked.  So compared to your explanation above, my test for 
Some was Is_block, rather than Int_val(0).  And I repeated Field(v,1) 
for getting to the 'Some "string"' rather than an intermediate variable, 
but matched your code in the Field(x, 0) for accessing the string 
portion of it.

I guess my remaining questions are whether there is a better approach than:

func : export list =
   [ { "name1"; None }; { "name2"; Some "desc" } ]

In python, I was able to use:

   return [ "name1", ( "name2", "desc" ) ]

where the alternation was on a string vs. a 2-tuple of strings, rather 
than all list members being a record but where the record had an 
optional member.  But I'm not sure how to represent that in the ocaml 
interface.  Maybe I'm thinking of something like:

type export =
   | Name of string
   | NameDesc of string * string

but I'm not quite sure how to write a list that initializes that.  On 
the other hand, I think I understand the C binding for such a type: 
either val is Val_int(0) (C 1) for the Name branch (Field(v, 0) is a 
string), or val is Val_int(1) (C 2) for the NameDesc branch (Field(v, 0) 
is name, Field(v, 1) is desc).

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org




More information about the Libguestfs mailing list