dbus glib bindings: deriving from G_TYPE_BOXED, parameterized types


It's rather timely that Owen mentioned the topic of deriving from
G_TYPE_BOXED in the Cairo discussion.

I've been working on the GLib bindings for the D-BUS messaging system
(http://www.freedesktop.org/Software/dbus).  The core problem I am
running into is that D-BUS has a fully recursive type system which is
difficult to map cleanly into GType (in its current form).  You can read
about the D-BUS type system here:


Here is a copy of the DBus tutorial which I've patched to include a
discussion of how my current patch to the D-BUS GLib bindings works:


The core approach that I've been taking now, as you can see from the
above, is to map common D-BUS type signatures to some manually-defined
GTypes, and punt the complex and weird ones into a special DBusGValue
variant type.  This ends up *massively* simplifying the code, both the
API and the internal marshalling/demarshalling.

However, hand-defining various types such as GArray<guint> is mildly
painful; it's even worse for the GHashTable cartesian product.  Although
I imagine for GHashTable not all of those will be in use; e.g. everyone
seems to be using string keys only, and not using e.g. the *int64 as

Anyways though as you can see in the discussion of arrays in the
tutorial, what I essentially want is twofold:

1) A GType for GArray and GHashTable
2) "parameterized" subtypes of these

We need the parameterized types because the marshalling code needs to
know what type an array contains in order to marshal, and the
demarshalling code needs to verify that the demarshalled values match
what a client was expecting.

I talked with Matthias very briefly about this, and he mentioned that he
thought this issue hadn't come up before because in most normal APIs,
when you pass down e.g. a GHashTable, the library expects that it maps
e.g. string -> uint; it's only with remoting that one needs to
manipulate values generically.

At first I was hand-defining a GType for each variant of
GArray<subtype>, and I wrote a GType for
GHashTable<gchararray,gchararray>.  However, I decided to try to make
this more generic by defining a GType for GArray, and then deriving a
"parameterized" subtype from it, using a generic function that looks
like this:

dbus_g_type_get ("GArray<guint>")
dbus_g_type_get ("GHashTable<string,gboolean>")

There was really nothing D-BUS specific about this though.  It knew
about GArray and GHashTable specially, and would register types for them
which derived from G_TYPE_BOXED.

It would then build a GType for the parameterized version, which derived
from the GArray or GHashTable type.  However I ran into two problems:

1) Type names can't contain < or >
2) We can't "deep" derive from G_TYPE_BOXED

Now, I could give up on defining GTypes for these, but it has two major

1) Makes the dbus_g_proxy_invoke API significantly more weird... you'd
   need to e.g. pass DBUS_G_ARRAY and G_TYPE_UINT32, but how do you
   distinguish? DBUS_G_ARRAY from normal GType? Or perhaps we make it a
   string-based API, so you say:

   GArray *arr = g_array_new (FALSE, FALSE, sizeof (guint32));
   g_array_append_val (arr, 10);
   g_array_append_val (arr, 42);
   guint32 ret;

   dbus_g_proxy_invoke (proxy, "MyMethod", &error, "gboolean", TRUE,
                        "GArray<guint32>", arr, "", &ret, "");

   That'd be a method that took a boolean and an array of uint32 as 
   arguments, and returned a uint32.

   But...ugly...it would invoke a lot of g_type_from_name () lookups
   for various builtin types.

2) D-BUS has the idea of a "variant", so a client could get as a return
   value a GValue which happens to contain an array of uint32.  Normally
   with GValue, you'd test like this:

   if (G_VALUE_HOLDS_STRING (&value))
   else if (G_VALUE_HOLDS_UINT (&value))

   etc.  But you can't do that if we can't map GArray<uint32> to a
   its own GType (because how do you know what the array contains
   then?)  We'd have to map all variants to a special DBusGValue type
   which you have to manipulate using special functions.

   And now if glib had an API for manipulating "parameterized" types:
   else if (g_type_is_a (G_VALUE_TYPE (&value), G_TYPE_ARRAY)) {
     if (g_type_is_a (g_type_container_parameter (G_VALUE_TYPE (&value)), G_TYPE_UINT)) {
       GArray *arr;
       guint i;
       arr = g_value_get_boxed (&value);
       for (i = 0; i < arr->len; i++)
         printf ("%d\n", g_array_index (arr, guint, i));
     } else {
        /* Ignore */ 
   } else if (g_type_is_a (G_VALUE_TYPE (&value), G_TYPE_HASHTABLE)) {
     GHashTable *table;
     const GType *parameters;

     table = g_value_get_boxed (&value);
     parameters = g_type_map_parameters (G_VALUE_TYPE (&value));
     if (parameters[0] == G_TYPE_STRING && parameters[1] == G_TYPE_STRING) {
        /* We know know it's a hashtable from string -> string */
        const char *val;
        if ((val = g_hash_table_lookup (table, "foo")) != NULL) {
          printf ("foo: %s\n", val);
     } else {
        /* Ignore other kinds of hash tables */
   g_value_unset (&value);  /* Note this actually knows how to free the array or
                               hashtable or whatever, including g_strdup() keys
                               etc for the hashtable */
Anyways, what I am tempted to do for now is to continue to manually
define GTypes for the common types in use not already defined in GLib
(e.g. GArray<guchar> and GHashTable<string,string>).  Clients will have
to access those types using the existing macros such as

If GLib ever gets support for boxed subtypes and adding parameterization
metadata to types, then I can change those macros to look like this:

#define DBUS_G_TYPE_STR_STR_HASHTABLE (g_type_build_parameterized ("GHashTable<gchararray,gchararray>"))

Attachment: signature.asc
Description: This is a digitally signed message part

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]