making my C more like Python

Asynchronous programming in C can be such a pain. You’re always creating little structs in order to pass around user_data. So often I find myself wishing it were like Python, and I could just create an anonymous tuple.

Then I had this crazy idea.

DBus is always passing around tuples. dbus-glib represents these as GValueArrays. They’re so ubiquitous that telepathy-glib added utility functions to deal with them: tp_value_array_build() and tp_value_array_unpack().

We can use them like this:

my_request_async (obj, my_callback,
  tp_value_array_build (2,
    G_TYPE_POINTER, my_pointer,
    G_TYPE_UINT, my_uint,
    G_TYPE_INVALID));

With the callback:

static void
my_callback (GObject *obj,
    GAsyncResult *result,
    gpointer user_data)
{
  gpointer my_pointer;
  guint my_uint;
 
  tp_value_array_unpack (user_data, 2,
      &my_pointer,
      &my_uint);
 
  ...
 
finally:
  g_value_array_free (user_data);
}

In my specific use case, I was making requests for a property, and then needed the generic callback to know which property the request was for. I could have copied and pasted the request multiple times, or get increasingly meta:

#define MY_REQUEST_ASYNC(prop) \
  my_request_async (obj, prop, \
      my_callback, tp_value_array_build (2, \
        G_TYPE_POINTER, my_pointer, \
        G_TYPE_UINT, prop, \
        G_TYPE_INVALID));
 
  MY_REQUEST_ASYNC (PROP_1);
  MY_REQUEST_ASYNC (PROP_2);
 
#undef MY_REQUEST_ASYNC

Where this gets really useful is when you’re having to copy or reference your members: strings, objects, hash tables, etc.. You no longer need to write new() and free() functions for each structure. GValue already knows how to take care of it.

my_request_async (obj, my_callback,
  tp_value_array_build (3,
    G_TYPE_OBJECT, my_obj,
    G_TYPE_STRING, "escher",
    G_TYPE_BOOLEAN, TRUE,
    G_TYPE_INVALID));

Everything will be released when you call g_value_array_free().

You could go further. For optional numbers of arguments you could use an a{sv} map (telepathy-glib also has utility methods to manipulate these, i.e. tp_asv_new()).

Unfortunately not everyone has telepathy-glib in their stack. I bet you could also achieve the same result using GVariant with g_variant_new(), g_variant_get() and g_variant_unref(). Store your pointers as type ‘t’ and remember to cast them to (guint64) for correct var-args alignment. Unfortunately GVariant can’t do your ref-counting.

taking over

dbus-send --session --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames | grep string | grep -v '":' | wc -l
57
dbus-send --session --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames | grep string | grep 'Telepathy\|Empathy' | wc -l
13

With Empathy using GtkApplication, somewhere around 23% of the well-known names on my D-Bus session bus belong to Telepathy or Empathy.

It’s slowly taking over.

Creative Commons Attribution-ShareAlike 2.5 Australia
This work by Danielle Madeley is licensed under a Creative Commons Attribution-ShareAlike 2.5 Australia.