glib2/gdbus - arrays and g_variant_get() quirks

Simon McVittie smcv at collabora.com
Tue Oct 10 15:53:17 UTC 2017


On Mon, 09 Oct 2017 at 23:35:38 +0200, David Sommerseth wrote:
> I'm getting quite puzzled when trying to extract an array using
> g_variant_get() ... and I don't quite follow why this happens.

This is a question about the GVariant API, not about D-Bus, so you
might get better results on a GLib/GNOME mailing list.

> Lets first look at this little and simple snippet:
> 
> ---------------------------------------------------------------------
>      GVariant *res = some_function_returning_int_array_via_dbus();
>      printf("Format (res): %s\n", g_variant_get_type_string(res));
> 
>      GVariant *array = NULL;
>      g_variant_get(res, "(*)", &array);
>      printf("Format (array): %s\n", g_variant_get_type_string(array));
> ---------------------------------------------------------------------
> 
> When running this little snippet the output is:
> 
>      Format (res): (au)
>      Format (array): au

Right: if we use a pseudo-C++ notation, res is a
GVariant<tuple<array<uint32>>>, and array is a GVariant<array<uint32>>
representing the first (and only) element of that tuple.

> But I'm not really happy about the '(*)'.  So I change that to say:
> 
>      g_variant_get(res, "(au)", &array);

You can't do this unless you change the type of 'array'. The format
string "(*)" expects a GVariant * argument, but the format string
"(au)" (or in fact "(aX)" for any X) expects a GVariantIter **.

See: https://developer.gnome.org/glib/stable/gvariant-format-strings.html

If you want both a GVariant * and type assertions, use "(@au)".

In general, g_variant_get() and g_variant_new() are designed for
convenience, rather than maximum possible consistency - the thought is
"if you have something that you know to be an array, clearly you are
going to want to iterate over it".

g_variant_get() with format string "(*)" or "(@au)" is effectively a
short-cut for calling g_variant_get_child_value() (and an assertion
about the type, in the case of "(@au)".

g_variant_get() with format string "(au)" is effectively a short-cut
for calling g_variant_get_child_value(), g_variant_iter_new() and
g_variant_iter_init().

> I do know that results from D-Bus are inside a "container", so the ()
> encapsulation is needed.

This is a true fact about the GDBus API for D-Bus, but it is not a
general fact about D-Bus. In generic D-Bus the hierarchy is:

    message
        header
            message type
            object path
            interface
            etc.
        body, consisting of 0-many times:
            parameter

In the GDBus API, the representation for a message body happens to be a
GVariant of type tuple, because that's a convenient way to encapsulate
0 or more GVariants.

In other D-Bus APIs (like libdbus, and I think also sd-bus) there is no
representation for a message body as separate from a message.

"Container" is a jargon term in the D-Bus and GVariant data-type models -
it means an array, a D-Bus struct or GVariant tuple (they're the same
thing), a variant, a dict-entry or a GVariant "maybe" type - so it's
best to avoid using that word with its generic meaning.

Regards,
    smcv


More information about the dbus mailing list