I've summarised how the GObject bindings work below, which should
hopefully help reviewing the generator/generated code.
There are a couple of points in here I'm still not 100% happy with.
Specifically the handling of FBuffer and the Cancellable flag. Both are
explained below. I'm interested in suggestions.
Return values:
**************
All functions which can return an error have as their final argument a
GError **. GI maps this to the appropriate error mechanism for each
target language.
RErr returns a gboolean, true = success, false = failure.
The following types return the value from libguestfs unchanged:
RInt
RInt64
RBool
RConstString (transfer none): gobject doesn't manage returned memory
RConstOptString (transfer none). Methods don't return an error (no
GError**)
RString
RStringList
RHashtable is converted to a GHashTable. The keys and values from
libguestfs are inserted directly into the GHashTable without copying.
The top level array returned by libguestfs is freed.
RStruct is converted to GObject boxed type for the target struct. It
returns a copy of the struct, which is copied field by field. Memory for
the returned struct is allocated using glib's slice allocator. The
original struct is freed with guestfs_free_<struct>().
RStructList is converted as an array of structs, which are individually
converted the same way as RStruct, i.e. everything is copied. The
returned array is a newly allocated NULL-terminated array. The
originally returned value is freed with guestfs_free_<struct>_list().
RBufferOut is returned as a guint8 array, whose length is specified by a
size_r field. GObject uses both these fields to construct a single array
object where language bindings allow. The value returned by RBufferOut
is not copied.
Structs:
********
A parallel struct is created for each libguestfs struct type. A boxed
type is created for each parallel struct. It's field types are all
mapped trivially to gobject basic types (e.g. gchar *, gint32) except
FBuffer. FBuffer is mapped as:
guint8 *<field>
guint32 <field>_size
Unfortunately I can see no way to attach annotation to these fields, so
the bindings do not appreciate that these fields are related. I'm not
happy with this at all, so I may try manual tweaking of the .gir to see
if we can turn these into a single returned array where possible.
Required arguments:
*******************
Required arguments to methods are all mapped trivially to gobject basic
types. They are all passed directly to libguestfs. StringList and
DeviceList both remain null-terminated arrays. BufferIn has separate
array and length components, which are combined into a single array
using annotations where language bindings permit.
Optional arguments:
*******************
Optional arguments are implemented as a separate gobject whose name is
is Guestfs<camel api name>. This gobject has defined properties for each
optional argument accepted by the api. All properties are initially
undefined. Property types are mapped as:
OBool -> GuestfsTristate
OInt, OInt64 -> gint/gint64. -1 is used internally as a magic value
to represent undefined.
OString -> gchar *. NULL represents undefined.
GuestfsTristate is a new boxed enum type with values FALSE, TRUE and
UNSET. In the javascript bindings at least, passing in 'true' or 'false'
here works as expected. To set the UNSET value, you have to explicitly
use GuestfsTristate.UNSET.
OInt and OInt64 have a similar problem to boolean, in that there's no
way to represent unset in the fundamental type. The -1 = undefined
scheme is an internal detail and need not be part of the API. I did
create a patch to make the undefined number explicit for each optional
argument which required it. However I decided to hold off on it until we
actually create an api where this would be a problem, as it makes the
api definition in the generator slightly uglier and more complex.
Where no optional arguments are required, the user can pass in the
binding language's equivalent of NULL. GObject has a placeholder for
default values, but there are not yet implemented. When they are, we can
define the default value of optargs to be null, meaning they can be
entirely omitted when not required.
Cancellation:
*************
Certain apis are cancellable. These all take a GCancellable as the final
argument before GError **. This can be passed NULL if cancellation is
not required. While I have written cancellation, I have not yet tested
it *at all* other than it compiles and works correctly when NULL is
passed in.
We recently made Cancellable an explicit flag whereas before it was
implicit if the api had a FileIn or FileOut argument. This means it is
now possible to break the GObject api without breaking the C api with
the addition of a Cancellable flag. What potential solutions are there
to this problem? I can see:
• Live with breaking the GObject api if it ever comes up.
• Never add Cancellable to an existing api.
• Automatically add a GCancellable argument to all GObject apis, just in
case.
Events API
**********
The libguestfs events API is not yet bound. I'll add this at a later stage.
Matt
--
Matthew Booth, RHCA, RHCSS
Red Hat Engineering, Virtualisation Team
GPG ID: D33C3490
GPG FPR: 3733 612D 2D05 5458 8A8A 1600 3441 EA19 D33C 3490