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)); |
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);
} |
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 |
#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)); |
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.