On 01/06/22 15:58, Richard W.M. Jones wrote:
> 
> 
> On Thu, Jan 06, 2022 at 03:09:07PM +0100, Laszlo Ersek wrote:
>> +value
>> +v2v_osinfo_os_get_all_devices (value osv)
>> +{
>> +  CAMLparam1 (osv);
>> +  CAMLlocal3 (retval, link, props);
> 
> It's a matter of style but I would tend to name variables which are
> OCaml values (C type: value) as "<something>v", just to remind myself
> that they have to follow the rules; see for example:
> 
> https://github.com/libguestfs/virt-v2v/blob/f9d5448d2efec106ab452e7b219ca179752983c3/convert/libosinfo-c.c#L108

So that's the explanation behind these "v" suffixes. :)

OK, I can do that, but I still very much want the *prefixes* to make sense. 
What about:

- retval_v, link_v, props_v;
- or: retvalv, linkv, propsv?

I obviously based my function on v2v_osinfo_os_get_device_drivers(), and this 
line:

  CAMLlocal3 (rv, v, vi);

could not have been more obscure -- it almost felt like intentional obfuscation 
:)

> 
>> +  g_autoptr (OsinfoDeviceList) dev_list = NULL;
>> +  OsinfoList *ent_list;
>> +  gint ent_nr;
>> +
>> +  retval = Val_emptylist;
>> +  dev_list = osinfo_os_get_all_devices (OsinfoOs_t_val (osv), NULL);
>> +  ent_list = OSINFO_LIST (dev_list);
>> +  ent_nr = osinfo_list_get_length (ent_list);
>> +
>> +  while (ent_nr > 0) {
>> +    OsinfoEntity *ent;
>> +    size_t prop_nr;
>> +
>> +    --ent_nr;
>> +    ent = osinfo_list_get_nth (ent_list, ent_nr);
>> +
>> +    props = caml_alloc (NUM_DEVICE_PROPS, 0);
>> +    for (prop_nr = 0; prop_nr < NUM_DEVICE_PROPS; ++prop_nr) {
>> +      const gchar *prop_val;
>> +
>> +      prop_val = osinfo_entity_get_param_value (ent, device_prop[prop_nr]);
>> +      if (prop_val == NULL)
>> +        prop_val = "";
>> +      Store_field (props, prop_nr, caml_copy_string (prop_val));
> 
> This works because the struct only contains strings:
> 
> type osinfo_device = {
>   id : string;
>   vendor : string;
>   vendor_id : string;
>   product : string;
>   product_id : string;
>   name : string;
>   class_ : string;
>   bus_type : string;
>   subsystem : string;
> }
> 
> which is OK,

Yes, the code intentionally relies on this. See the comment on "device_prop":

+ * All of the above properties have string values. Thus, for uniformity, access
+ * all these properties by their names at the OsinfoEntity level (i.e., forego
+ * the class- and property-specific, dedicated property getter functions).

> but doesn't libosinfo let us convert them to more natural
> types?

Wait, I understand your question better now. Here's the thing: the *natural 
type* for each of these properties is *genuinely* string. It's not like I'm 
accessing things that are actually integers and booleans through their internal 
(low-level) string representation -- they are *genuinely* strings.

I defined the "osinfo_device" OCaml type with purely string fields *because* 
all of the OsInfoDevice properties are strings in the first place!

I wish I could provide you a URL with http(s) scheme; however, the only place 
where I managed to find libosinfo documentation is my hard disk, from the 
"libosinfo-devel" package. So please open this URL;

  file:///usr/share/gtk-doc/html/Libosinfo/OsinfoDevice.html

and you'll see that all the properties are strings.

In fact, the first version of my array here did not contain strings (property 
names), but pointers to the following getter functions (which is "all of the 
existent getters"):

osinfo_device_get_vendor ()
osinfo_device_get_vendor_id ()
osinfo_device_get_product ()
osinfo_device_get_product_id ()
osinfo_device_get_bus_type ()
osinfo_device_get_class ()
osinfo_device_get_name ()
osinfo_device_get_subsystem () 

This was possible because all of them return "const gchar *".

My first version worked fine, but then I noticed that I missed the most 
important property: the ID.

That property is defined by the abstract base class OsinfoEntity however:

  const gchar * osinfo_entity_get_id ()

  file:///usr/share/gtk-doc/html/Libosinfo/OsinfoEntity.html

So that was a complication: I would have to add some kind of dynamic casting, 
because osinfo_entity_get_id () would act at a different level than the other 
getters.

Here's the simplification: if I work solely at the OsinfoEntity level, and use 
osinfo_entity_get_param_value() exclusively, I can access all the properties 
uniformly, by name, regardless of whether they come from the base class or from 
the derived class. The property names themselves are public API; there are no 
tricks being pulled.

And given the actual properties at hand, the string representation returned by 
osinfo_entity_get_id() matches the *actual* property types -- because all those 
types are genuinely strings.

> 
> Another thing which is not actually a bug but is potentially
> dangerous is:
> 
>   Store_field (props, prop_nr, caml_copy_string (prop_val));
>                                ^^^^
> 
> A temporary variable is allocated to store the result of
> caml_copy_string, so it's equivalent to:
> 
>   value tv = caml_copy_string (prop_val);
>   Store_field (props, prop_nr, tv);
> 
> Now it turns out that Store_field (which calls caml_modify) is
> documented to never call the garbage collector, so the block pointed
> to by "tv" could never move, so we're OK!
> 
> But the code is nevertheless a bit tricky and you might want to make
> the temporary explicit (and include it in the CAMLlocal expression at
> the top of the function so it is a global root).

The

  Store_field (..., caml_copy_string (prop_val))

pattern was copied verbatim from the existent function 
v2v_osinfo_os_get_device_drivers():

    str = osinfo_device_driver_get_architecture (driver);
    Store_field (vi, 0, caml_copy_string (str));
    str = osinfo_device_driver_get_location (driver);
    Store_field (vi, 1, caml_copy_string (str));

In fact, when writing my function, I meticulously reviewed the transfer 
(ownership) annotations of all the other (prior) libosinfo APIs used in this C 
source file. Thankfully, I found no bugs. I initially suspected that we had a 
leak here, in v2v_osinfo_os_get_device_drivers():

  OsinfoDeviceDriverList *list;
  [...]
  list = osinfo_os_get_device_drivers (OsinfoOs_t_val (osv));

I thought that it should have been wrapped into g_autoptr().

However, surprisingly, osinfo_os_get_device_drivers() is annotated with 
"transfer none" (so the code is correct!):

  
file:///usr/share/gtk-doc/html/Libosinfo/OsinfoOs.html#osinfo-os-get-device-drivers

while the function I needed myself is annotated with "transfer full":

  
file:///usr/share/gtk-doc/html/Libosinfo/OsinfoOs.html#osinfo-os-get-all-devices

> 
>> +    }
>> +
>> +    link = caml_alloc (2, 0);
>> +    Store_field (link, 0, props);
>> +    Store_field (link, 1, retval);
>> +    retval = link;
>> +  }
>> +
>> +  CAMLreturn (retval);
> 
> So the code is fine as it turns out, but:
> 
> (a) Might consider using <something>v for value names

Sure, I'll do that (please advise me on the "v" vs. "_v" suffix)

> 
> (b) It'd be nice if libosinfo gave us real types

It does -- but the string representations I use here already matches the actual 
types in question! (That's why I went with this loop-based approach in the 
first place, rather than the explicit property-wise copying seen in 
v2v_osinfo_os_get_device_drivers().)

> 
> (c) Better IMHO to make the temporary variable explicit

Hmmm, I'm not attracted to this; it will create a discrepancy between 
v2v_osinfo_os_get_device_drivers() and the new function. I took

  Store_field (..., caml_copy_string (prop_val))

as a known-safe, canned pattern.

The pattern has three *prior* uses in this source file, plus we have one

  CAMLreturn (caml_copy_string (id));

as well.

If the pattern is obscure (or risky), we should eliminate all uses of it.

Please comment :)

Thanks!
Laszlo

_______________________________________________
Libguestfs mailing list
[email protected]
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to