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.