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.
>
Maybe I did figure it out after all, although I'm still not sure
this is the best interface. Applying this on top of the original
patch lets me use 'string option' instead of 'string' as the second
member of the export record.
https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html#s%3Ac-ocaml-dataty...
didn't directly answer my question, but my understanding is that
since 'string option' is the same as:
type 'a t = a' option =
| None (* Is_block is false, value is Val_int(0) *)
| Some of 'a (* Is_block is true, value is block with tag 0 *)
then checking Is_block tells me whether I have None or Some string,
at which point another Field() deref gets me to the string contained
in that block.
All ‘value’ things in OCaml are just 64 bit integers. The only
difference is whether the bottom bit is 0, in which case it's a
pointer to an OCaml block, or 1 in which case it's an integer-like
thing shifted left by one bit. Is_block() simply checks the bottom
bit.
Val_int(0) turns C int 0 into an OCaml int. Which is done by shifting
it left one place and setting the bottom bit. IOW it returns C 1.
Also None is represented as OCaml 0 (so C 1):
https://rwmj.wordpress.com/2009/08/04/ocaml-internals/
OCaml blocks are lists of values allocated on the OCaml heap:
https://rwmj.wordpress.com/2009/08/05/ocaml-internals-part-2-strings-and-...
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.
+++ 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?
Rich.
--
Richard Jones, Virtualization Group, Red Hat
http://people.redhat.com/~rjones
Read my programming and virtualization blog:
http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html