dbus glib bindings: deriving from G_TYPE_BOXED, parameterized types
Colin Walters
walters at verbum.org
Fri May 13 10:01:05 PDT 2005
Hi,
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:
http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-signatures
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:
http://web.verbum.org/files/dbus-tutorial.html#glib-client
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
values.
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
consequences:
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
DBUS_G_TYPE_STR_STR_HASHTABLE and DBUS_G_TYPE_UINT_ARRAY.
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>"))
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: This is a digitally signed message part
Url : http://lists.freedesktop.org/archives/dbus/attachments/20050513/e3956ffb/attachment.pgp
More information about the dbus
mailing list