Posted by: Airtower | 2010-07-20

Using GVariant tuples

A GVariant stores data and type information for that data. The data can be simple, like numeric values or strings, or more complex like arrays of simple types or tuples of other valid types. The type(s) of data contained in a GVariant is (are) described by a format string. This format string can be a short "s" or "i" for a string or 32 bit integer, respectively, "as" for a string array or something longer like "(asii)" for a tuple consisting of a string array and two integers. There are a lot more possibilities, but "(asii)" is what I needed today and also what this post will be about.

So what did I want to do? I have a function that should return multiple values of different kinds, but of course in C functions can only return a single variable. There are a number of ways to solve this problem, but I am going to use a GVariant container, because that’s what GIO‘s D-Bus API needs and my function is supposed to be called via D-Bus.

Building the GVariant object

GLib has a function g_variant_new() that will construct a GVariant from an arbitrary format string and a variable number of values, but also typed functions like g_variant_new_int32() (which I prefer). The first GVariant I need is a string array. There is a typed function for this:

GStrv x;
/* fill x with data */
GVariant *x_variant = g_variant_new_strv((const gchar * const *) x, -1);
g_strfreev(x);

g_variant_new_strv() takes the array as first argument and the number of elements in the array as second. Here I used “-1”, which tells the function to expect a NULL terminated array. I don’t need the GStrv afterwards, so I free it.

Building my "(asii)" type GVariant is only slightly more complex: I will use g_variant_new_tuple() to create a tuple GVariant from an array of GVariant *.

GStrv x;
int a, b;
/* fill a, b and x with data */
GVariant **t = g_new(GVariant *, 3);
t[0] = g_variant_new_strv((const gchar * const *) x, -1);
g_strfreev(x);
t[1] = g_variant_new_int32(a);
t[2] = g_variant_new_int32(b);
GVariant *tuple_variant = g_variant_new_tuple(t, 3);
g_free(t);

Note that the array is freed, but not the component GVariants. Only the references are copied to the tuple GVariant, so freeing the components would cause trouble. g_variant_get_type_string() may be useful if you’re trying to build a complex GVariant and it doesn’t seem to have the structure you wanted.

Disassembly

I use the GVariant described above as a return value with the caller responsible for freeing the allocated memory. There are many ways to read data from GVariants (even an iterator for containers like tuples), but I’m just going to write about the method I used.

GVariant *v = my_function_call(/* parameters */);
g_assert(v != NULL);
g_assert(g_variant_n_children(v) == 3);

Ok, I get a GVariant * from my function. The first thing to check is that it isn’t NULL (you should replace this with error handling). The second assertion is to prevent mistakes: I have control over both caller and callee, but you can’t be too careful. As you might have guessed, g_variant_n_children() returns the number of elements (“children”) in a container GVariant, and that should really be three in this case. 😉 Using g_variant_get_child_value(), I get the child GVariants from the tuple. The string array is first:

GVariant *tmp = g_variant_get_child_value(v, 0);
GStrv y = g_variant_dup_strv(tmp, NULL);
g_variant_unref(tmp);

There are two functions to get a string array from a GVariant containing one: g_variant_get_strv() and g_variant_dup_strv(). I use the latter because it creates a deep copy of the array stored inside the variant object, so I can do whatever I want with the result. The GVariant is no longer needed, so it is unref‘d. I don’t think the following needs more explanation:

tmp = g_variant_get_child_value(v, 1);
gint32 c  = g_variant_get_int32(tmp);
g_variant_unref(tmp);

tmp = g_variant_get_child_value(v, 2);
gint32 d = g_variant_get_int32(tmp);
g_variant_unref(tmp);

The only thing left to do is cleaning up the container:

g_variant_unref(v);

That’s it!

Advertisements

Responses

  1. Hello, thanks for the write up (almost 6 years old, but still useful:)!

    In “Building the GVariant object” line 9, did you really mean `g_variant_new_tuple(tuple, 3)`? Or is it `g_variant_new_tuple(t, 3)`?

    • You’re right. I’ve compared the example with the code I wrote back then and fixed it. Thanks for the hint! 🙂


Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: