Bug 59024 - Think about a more introspectable way of doing mixins
Summary: Think about a more introspectable way of doing mixins
Status: RESOLVED MOVED
Alias: None
Product: Telepathy
Classification: Unclassified
Component: tp-glib (show other bugs)
Version: unspecified
Hardware: Other All
: medium normal
Assignee: Telepathy bugs list
QA Contact: Telepathy bugs list
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: tp-glib-1.0
  Show dependency treegraph
 
Reported: 2013-01-04 13:25 UTC by Xavier Claessens
Modified: 2019-12-03 20:41 UTC (History)
0 users

See Also:
i915 platform:
i915 features:


Attachments
[bugfix] inspect-cm: fix the "one CM" case (880 bytes, patch)
2013-10-22 14:59 UTC, Simon McVittie
Details | Splinter Review
[cleanup] codegen: factor out copy_into_gvalue (6.27 KB, patch)
2013-10-22 15:00 UTC, Simon McVittie
Details | Splinter Review
[WiP] Add TpExportable, an introspectable way for mixins to implement methods (21.48 KB, patch)
2013-10-22 15:05 UTC, Simon McVittie
Details | Splinter Review
[WiP] TpExportable: add introspectable ways to return values (1.29 KB, patch)
2013-10-22 15:07 UTC, Simon McVittie
Details | Splinter Review
[WiP] TpDBusPropertiesMixin: allow linking to an object via TpExportable (8.01 KB, patch)
2013-10-22 15:09 UTC, Simon McVittie
Details | Splinter Review
[WiP] TpBaseConnection: implement Exportable (867 bytes, patch)
2013-10-22 15:10 UTC, Simon McVittie
Details | Splinter Review
[WiP] TpBaseConnection: implement properties via TpExportable::call-method (1.66 KB, patch)
2013-10-22 15:10 UTC, Simon McVittie
Details | Splinter Review

Description Xavier Claessens 2013-01-04 13:25:19 UTC
In bug #14540 we decided to use a new way of doing TpNamesMixin:

Some rules for TpFooMixin:

1) Virtual methods are in a proper GInterface TpFooInterface
2) No public TpFooMixin struct, all data are kept internal
3) functions takes the base class (e.g. TpBaseConnection) as first arg
4) tp_foo_mixin_init() only takes one arg (the base object) and store its internal mixin struct using g_object_set_qdata_full().
5) no tp_foo_mixin_finalize() needed.
Comment 1 Xavier Claessens 2013-01-08 12:30:41 UTC
Extra potential g-i problems:

6) tp_foo_mixin_init_dbus_properties(GObjectClass*): is it possible to call in class_init in binding languages? what are the alternatives?

7) tp_foo_mixin_iface_init(): Can we give functions to implement the dbus ifaces in a subclasses not in C?

8) tp_foo_mixin_do_something(): Will g-i understand that this is a function on TpBaseConnection object? Will I be able to do base_connection.do_something()?


Maybe crazy, but I'm wondering if we should make those mixins really part of TpBaseConnection, but optionally initialized. how it would looks like:

TpBaseConnectionPrivate{TpFooMixinPrivate *foo_mixin; ...}

TpBaseConnection::finalize{_tp_foo_mixin_free (self->priv->foo_mixin); ...}

tp_base_connection_init_foo(TpBaseConnection *self){self->priv->foo_mixin = _tp_foo_mixin_new();}

tp_base_connection_foo_changed(TpBaseConnection *self)
{g_return_val_if_fail(self->priv->foo_mixin != NULL); ...}

But still that won't fix 6) and 7).
Comment 2 Simon McVittie 2013-10-22 14:58:32 UTC
(In reply to comment #1)
> 6) tp_foo_mixin_init_dbus_properties(GObjectClass*): is it possible to call
> in class_init in binding languages? what are the alternatives?

If necessary, we can teach the TpDBusPropertiesMixin to search a per-instance list before the per-class one.

I have a work-in-progress branch which reduces it to:

* implement TpExportable with no methods (introspectable, I think)

* call Tp.DBusPropertiesMixin.implement_interface(...) from class_init
  (not introspectable)

* call Tp.DBusPropertiesMixin.init(self) from __init__ or equivalent
  (introspectable, I think)

> 7) tp_foo_mixin_iface_init(): Can we give functions to implement the dbus
> ifaces in a subclasses not in C?

No, but we can use TpExportable.

> 8) tp_foo_mixin_do_something(): Will g-i understand that this is a function
> on TpBaseConnection object? Will I be able to do
> base_connection.do_something()?

No, you'll have to do Tp.FooMixin.do_something(self). I don't think that's *so* bad.

We can use GInterfaces for syntactic sugar if necessary? If you implement Tp.FooMixable with a foo_do_something() method, you can call self.foo_do_something() (really tp_foo_mixable_foo_do_something() in C). We could use the same GInterface to hold the necessary vfuncs for the actual implementation.

> Maybe crazy, but I'm wondering if we should make those mixins really part of
> TpBaseConnection, but optionally initialized.

I would like to keep this as a last resort: if all our objects are this tightly coupled, replacing one becomes really annoying.
Comment 3 Simon McVittie 2013-10-22 14:59:15 UTC
Created attachment 87989 [details] [review]
[bugfix] inspect-cm: fix the "one CM" case

---

I happened to run into this while testing. Please review.
Comment 4 Simon McVittie 2013-10-22 15:00:02 UTC
Created attachment 87990 [details] [review]
[cleanup] codegen: factor out copy_into_gvalue

---

I needed this for TpExportable, but it's already a small code reduction, so we could apply it.
Comment 5 Simon McVittie 2013-10-22 15:05:25 UTC
Created attachment 87991 [details] [review]
[WiP] Add TpExportable, an introspectable way for mixins to  implement methods

The main problems with our current mixins, from an introspection point
of view, are:

* needing to be able to call functions during class initialization;
* needing to be able to implement a parent object's D-Bus methods,
  currently done with G_IMPLEMENT_INTERFACE, which is not introspectable

The first can be avoided by calling similar functions during instance
initialization, if necessary.

The second is somewhat harder, but we can use a signal as our hook.

---

We could add get-property and set-property signals which work similarly, if desired.

Open questions include:

* Should the signal be call-method::inter.face.method or just call-method::inter.face? (Currently call-method::inter.face.method.)

* If a parent class and its subclass both connect to call-method for the same method, I think the parent class' implementation will get the first chance to respond to the method call, which is clearly wrong. Can we arrange for the most recently-connected signal connection to be called first, instead?

This needs more regression tests, probably.
Comment 6 Simon McVittie 2013-10-22 15:07:20 UTC
Created attachment 87992 [details] [review]
[WiP] TpExportable: add introspectable ways to return values

dbus-glib types are awkward in introspected languages. Let's try
GVariant instead.

This requires an as-yet unmerged dbus-glib branch (Bug #48821).
Comment 7 Simon McVittie 2013-10-22 15:09:38 UTC
Created attachment 87993 [details] [review]
[WiP] TpDBusPropertiesMixin: allow linking to an object via  TpExportable

This should let introspected code use the mixin, I think.

---

... or not, because it can't call implement_interface() yet. Still, it's a start...

We'd also have to export at least a trivial amount of the TpSvc* stuff (just the fact that the struct exists) to introspected code, for it to be able to do a trivial no-vfuncs implementation of an interface.

The Contacts mixin is the obvious candidate for the next place to use this technology.
Comment 8 Simon McVittie 2013-10-22 15:10:01 UTC
Created attachment 87994 [details] [review]
[WiP] TpBaseConnection: implement Exportable
Comment 9 Simon McVittie 2013-10-22 15:10:18 UTC
Created attachment 87995 [details] [review]
[WiP] TpBaseConnection: implement properties via  TpExportable::call-method
Comment 10 Guillaume Desmottes 2013-10-23 07:42:15 UTC
Comment on attachment 87989 [details] [review]
[bugfix] inspect-cm: fix the "one CM" case

Review of attachment 87989 [details] [review]:
-----------------------------------------------------------------

++
Comment 11 Guillaume Desmottes 2013-10-23 07:42:43 UTC
Comment on attachment 87990 [details] [review]
[cleanup] codegen: factor out copy_into_gvalue

Review of attachment 87990 [details] [review]:
-----------------------------------------------------------------

++
Comment 12 Xavier Claessens 2014-04-09 20:47:37 UTC
Now that we ported tp-glib to GDBus, it became possible[1] to implement dbus interfaces using gdbus-codegen skeletons. That means that TpPresenceMixin can be just like TpBaseContactList[2]: a standalone GObject.

TpPresenceMixin could be turned into an abstract GObject that takes a TpBaseConnection as construct-only property. It will then create the gdbus-codegen' skeleton, implement all its stuff, and simply call tp_dbus_object_add_skeleton(base_connection, presence_skeleton);

We only have 3 mixins, How Hard Can It Be?™

[1] With few extra patches, still unmerged at time of writing:
http://cgit.collabora.com/git/user/xclaesse/telepathy-glib.git/log/?h=gdbus-custom-skeletons

[2] With few extra patches, still unmerged at time of writing: 
http://cgit.collabora.com/git/user/xclaesse/telepathy-glib.git/log/?h=gdbus-contactlist
Comment 13 Simon McVittie 2014-04-10 12:33:09 UTC
(In reply to comment #12)
> TpPresenceMixin could be turned into an abstract GObject that takes a
> TpBaseConnection as construct-only property. It will then create the
> gdbus-codegen' skeleton, implement all its stuff, and simply call
> tp_dbus_object_add_skeleton(base_connection, presence_skeleton);

Yes, I think this is a good concept - but I think tp_dbus_object_add_skeleton() can in fact just be g_dbus_object_skeleton_add_interface() in constructed(). I have a rather sketchy branch which implements this for TpBaseProtocol (because it's the simplest).

I'll try it for TpBaseConnection, rebasing your implementation of a custom GDBusInterfaceSkeleton onto it, when I get a chance.

> We only have 3 mixins, How Hard Can It Be?™

Indeed.
Comment 14 Xavier Claessens 2014-04-11 03:28:02 UTC
I changed my mind actually. If we make a TpBasePresence it means we'll have to subclass it in every CM (and test CM) and I'm far too lazy to do that in C.

So I turned it into a GInterface instead and TpBaseConnection implements the dbus interface using gdbus-codegen's skeleton if (TP_IS_PRESENCE_MIXIN (self)). Simple, introspectable, and minimal changes needed in CMs.

My branches are starting to stack... http://cgit.collabora.com/git/user/xclaesse/telepathy-glib.git/log/?h=gdbus-presence
Comment 15 Simon McVittie 2014-04-11 10:27:56 UTC
(In reply to comment #14)
> TpBaseConnection implements the
> dbus interface using gdbus-codegen's skeleton if (TP_IS_PRESENCE_MIXIN
> (self)).

I didn't like this design in the past, and I still don't. I would like to keep non-core interfaces out of TpBaseConnection.

With the GDBusObjectSkeleton branch I'm trying to finish, I think we can do this, though:

* TpPresenceMixin is a GInterface
* TpPresenceSkeleton is a GDBusInterfaceSkeleton subclass
* HazeConnection has-a TpPresenceSkeleton, and implements TpPresenceMixin
* TpPresenceSkeleton calls methods on TpPresenceMixin
* haze_connection_constructed() adds its TpPresenceSkeleton to its
  GDBusObjectSkeleton list of interfaces

which isn't that much boilerplate and keeps the loose coupling.
Comment 16 Simon McVittie 2014-04-11 10:31:17 UTC
(In reply to comment #14)
> My branches are starting to stack...

I would like to finish my GDBusObjectSkeleton branch so it can replace your tp_dbus_object_add_interface(). Then we can pull in your TpBaseConnection and TpBaseContactList changes.

I am strongly opposed to letting telepathy-glib get a significant distance ahead of the CMs, which is why I'm taking so long to do anything on this - I don't consider it to be done until the CMs pass tests. We decoupled them early on in 'next' development, and it was months/years before we had versions of all the bits that compiled together, let alone worked.
Comment 17 Xavier Claessens 2014-04-11 15:41:42 UTC
(In reply to comment #15)
> * TpPresenceMixin is a GInterface
> * TpPresenceSkeleton is a GDBusInterfaceSkeleton subclass
> * HazeConnection has-a TpPresenceSkeleton, and implements TpPresenceMixin
> * TpPresenceSkeleton calls methods on TpPresenceMixin
> * haze_connection_constructed() adds its TpPresenceSkeleton to its
>   GDBusObjectSkeleton list of interfaces

Hmmm. But TpPresenceSkeleton would be subclass of _TpGDBusConnectionInterfacePresence1 and I don't want that codegen to be public. I woudld the rather have:

 * TpPresenceMixinInterface is-a GInterface for TpBaseConnection subclasses.
 * TpPresenceMixin is-a GObject.
 * TpPresenceMixin has-a _TpGDBusConnectionInterfacePresence1.
 * TpPresenceMixin has-a TpPresenceMixinInterface implementation (HazeConnection).
 * TpPresenceMixin::constructed calls g_dbus_object_skeleton_add_interface(self->priv->base_conn, self->priv->skeleton);

I think presence is small and fundamental enough to be part of TpBaseConnection instead of having yet another object with all its boilerplate. And that doesn't prevent doing standalone objects for future sidecars interfaces. What I like in my branch is that CMs only need to implement an interface, nothing else. Even fill_contact_attributes() is handled for them.
Comment 18 GitLab Migration User 2019-12-03 20:41:06 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/telepathy/telepathy-glib/issues/106.


Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.