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-oth... 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