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
"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.
GVariant is only slightly more complex: I will use
g_variant_new_tuple() to create a tuple
GVariant from an array of
GStrv x; int a, b; /* fill a, b and x with data */ GVariant **t = g_new(GVariant *, 3); t = g_variant_new_strv((const gchar * const *) x, -1); g_strfreev(x); t = g_variant_new_int32(a); t = 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.
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_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: