Re: GVariant for prez!



On Mon, 2009-05-04 at 19:12 +0100, Simon McVittie wrote:
> You're clearly not happy with the representations chosen by dbus-glib
> (and I agree that they're not so good - they rarely match an existing
> API, and they don't match the real D-Bus data model either).
> 
> I hope I'm misunderstanding your position somewhere: despite that, you
> seem to be advocating having the object-mapping output such types, in
> the absence of any other indication of what to return?

No, there are no GValue anywhere in the user API of the current GDBus
code - e.g. a{si} is mapped to a GHashTable with keys being gchar* and
values stored in pointers (e.g. GPOINTER_TO_INT). Ditto a{sd} is a hash
from gchar* to gdouble* - e.g. the "natural" way a GHashTable is used.

> If there *does* need to be custom code to map between D-Bus types and GLib
> types, it seems sensible for something like GVariant to be the "D-Bus types"
> end of that pipeline, because its data model *is* the D-Bus data model, so
> no transformation is involved; as a side benefit, anyone who is happy with
> GVariant as an representation for their variants could use it without
> any automatic conversion.

Actually, the idea with eggdbus was actually to let the user provide the
complete type expressed as a tree of GTypes (it's in the source tree as
src/eggdbus/completetype.c but not finished). For this to work you need
a bit more information than just D-Bus introspection XML, e.g. consider
the following pseudo-IDL

 struct Pair
 {
   int32 x;
   int32 y;
 };

 struct Point
 {
   int32 first;
   int32 second;
 };

 # In standard D-Bus introspection XML, this is ambiguous
 # because structs are anonymous
 method GetStuff (out array<Pair> pairs, out array<Point> points);

and a natural mapping would be this

 FooPair *foo_pair_new (gint x, gint y);
 gint     foo_pair_get_x (FooPair *pair);
 gint     foo_pair_get_y (FooPair *pair);

 // ditto for FooPoint

 void foo_invoke_get_stuff_sync (Proxy *proxy,
                                 GPtrArray **out_pairs,
                                 GPtrArray **out_points);

with FooPair and FooPoint being generated subclasses that implements the
GDBusStructure interface (and g_ptr_array_get_element_type() on these
would be FOO_TYPE_PAIR resp. FOO_TYPE_POINT, should bug 581106 be
resolved).

The idea is that the implementation (which is generated by a code
generator from the IDL) is something like this:

 gboolean foo_invoke_get_stuff_sync (Proxy *proxy,
                                    GPtrArray **out_pairs,
                                    GPtrArray **out_points)
 {
    proxy_invoke_method_sync (PROXY (proxy),
                           G_TYPE_PTR_ARRAY, "a%FooPair%", &out_pairs,
                           G_TYPE_PTR_ARRAY, "a%FooPoint%", &out_pairs);
 }

with standard async/error handling omitted for brevity. The point,
really, is that we extend the D-Bus signature format to include
information about the desired GType we want constructed / want passed.

(It may be that a string "a%FooPair%" (to be parsed every time) isn't
the best format, it's entirely possible to pass a pointer to a
CompleteType object or something that already has the complete type
information in form of a tree (and GType instead of type name) and we
can run with that.)

You can extend this if you want - presumably you can require that any
type you pass in would need to implement the GDBusSerializable interface
that could look like this:

 gboolean (*new_from_wire) (DBusMessage     *message,
                            DBusMessageIter *iter,
                            const gchar     *interface_name,
                            const gchar     *signature,
                            GValue          *out_value,
                            GError         **error);
 // keep a ref to message to avoid copying data

 gboolean (*to_wire)       (DBusMessage     *message,
                            DBusMessageIter *append_iter,
                            const gchar     *interface_name,
                            const gchar     *signature,
                            GValue          *value,
                            GError         **error);

and the generated FooPair and FooPoint classes would just be objects
that implement this interface (and probably store the data in a Plain
Old Structure so you can get/set it without function call overhead).

This also allows you to hook in support for existing types, e.g. suppose
I have a class called MailappContact which encapsulates a contact of
some sort. Presumably you'd implement the interface GDBusSerializable
and then
 
 proxy_invoke_method_sync (PROXY (proxy),
                  ..
                  // in
                  "%MailappContact%", contact_list,
                  ...
                  // out
                  G_TYPE_PTR_ARRAY, "a%MailappContact%", &contact_list,
                  ...

with this working in both directions (e.g. passing and receiving). In
IDL, this would perhaps look like

 @org.gtk.GDBus.GType=MailappContact
 @signature=(sssa{sv})
 struct MailappContact;

 GetContacts (in array<MailappContact> contacts);
 AddContact (MailappContact contact);

and you can do similar things for mapping a{sv}, arrays and hashes to
some known GType implementing the GDBusSerializable interface (for
example libgee would implement these interfaces).

Of course it would be nicer and more portable to use GVariant here
instead of the DBusMessage, DBusMessageIter pair, e.g.

 gboolean (*new_from_variant) (const GVariant  *variant, // immutable
                               const gchar     *interface_name,
                               const gchar     *signature,
                               GValue          *out_value,
                               GError         **error);
 // keep a ref to variant to avoid copying data

 gboolean (*to_variant)       (GVariantBuilder *variant_builder,
                               const gchar     *interface_name,
                               const gchar     *signature,
                               GValue          *value,
                               GError         **error);

It's not like this in GDBus mostly because we're still trying to figure
out the best way to do type mapping.

And, hey, we don't have an IDL language just yet. 

There's also the concern this is over-complicated and/or over-engineered
and I wanted to start out with the simple stuff. I'm still not sure it's
*worth* it to throw this kind of complexity at the problem - just saying
it's entirely possible.

Plus it deviates slightly from standard D-Bus insofar we require structs
to be named - though any D-Bus interface should be representable in IDL
(you can make up names) - not the other way around without throwing away
information.

Whether this stuff is using DBusMessage/DBusMessageIter or GVariant, I
don't really care that much. For me, it's really an implementation
detail, even one that can be changed at a later date without breaking
too much stuff. Though it would be *nice* to use GVariant (with slight
changes and a different name and a compatible license (it's still
GPLv3).

But, to repeat myself... what I *don't* think is a good idea is to do
D-Bus support at the GLib level where you have to manually do stuff like
building / iterating over messages whether it's DBusMessage or GVariant
or whatever way you want to represent the data in memory. Mapping things
*properly* to native types is the only way forward.

So a bit more research and experimentation with type mapping is required
I think. It probably involves building an IDL language and compiler too.

    David




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