2017-09-11 10:14 GMT+02:00 Eike Ziller <[email protected]>: > >> On Sep 11, 2017, at 09:54, Elvis Stansvik <[email protected]> wrote: >> >> 2017-09-11 9:33 GMT+02:00 Eike Ziller <[email protected]>: >>> >>>> On Sep 10, 2017, at 13:09, Elvis Stansvik <[email protected]> wrote: >>>> >>>> Right, the two-phase plugin initialization and object pool is quite >>>> well described in the docs: >>>> >>>> https://doc-snapshots.qt.io/qtcreator-extending/plugin-lifecycle.html >>>> >>>> I think my question was a little vague, sorry about that. >>>> >>>> Consider an example: Some functionality "foo" is to be added to the >>>> core plugin, and a plugin Bar written to implement that functionality. >>>> >>>> Approach 1: >>>> >>>> - Add an interface IFoo to the core plugin. >>>> - Bar implements IFoo and add an instance of its implementation to the >>>> pool in its initialize(). >>>> - The core plugin get the object from the pool (in its >>>> extensionsInitialized()), and make use of it. >>>> >>>> Approach 2: >>>> >>>> - Add a singleton Foo to the core plugin. >>>> - Bar carries out its work using direct calls on Foo::instance(). >>>> >>>> I was just wondering if the second approach is ever used, or if the >>>> mechanism set up by the object pool + two-phase initialization is >>>> always used. If approach 2 is used, I was interested in what the >>>> deciding factor is between the two approaches. >>> >>> Many good things have already been said, I’m late :) >> >> You're very welcome any time Eike :) >> >>> >>> Basically there is also >>> >>> Approach 3: >>> >>> - Add an interface IFoo to the core plugin >>> - Add a static method for registering instances of IFoo to core plugin >>> - Bar implements IFoo and registers an instance of its implementation via >>> the method above >>> - The core plugin gets the object(s) that have been registered through the >>> static method (in its extensionsInitialized or later) >> >> Ah yes of course. >> >>> >>> Approach 1 works when there should not be a hard runtime dependency between >>> the plugins (at compile time the interface header is needed). For making >>> this work without a link dependency, the interface must use >>> Q_DECLARE_INTERFACE. I think the only place where it actually used this way >>> is with CodePaster::Service, to avoid a runtime dependency between >>> CodePaster and DiffEditor plugins. Historically we used that a lot also >>> when there actually already was a hard runtime dependency between the >>> plugins, which is problematic because of the performance issue that André >>> hinted at. >> >> Aha, I was actually going to ask if plugin dependencies (as declared >> in their metadata) always implies a link time dependency as well, >> because when I looked at qtcreator.pri (the loops at the bottom that >> add to LIBS), it looked to me like it does. But you're saying that if >> Q_DECLARE_INTERFACE is used in the depended-upon plugin's interface >> class, then there will be no link time dependency introduced, despite >> -l<dep> being always being added to the link line for the depending >> plugin? > > To avoid an actual link time dependency, the dependency has to also be > declared as “optional”, i.e. > QTC_PLUGIN_RECOMMENDS instead of QTC_PLUGIN_DEPENDS
Ah, right, I see now. Elvis > > Br, Eike > >> >>> >>> In Approach 2 we actually moved towards using mostly classes with static >>> methods, only using the instance() when we actually need an instance (e.g. >>> for connecting to signals). It works well when a hard runtime dependency is >>> ok and Bar is the driving force for the work to be done. I would say that >>> we do this a lot, e.g. ActionManager, EditorManager, …, but I don’t know if >>> you meant that kind of interaction ;) >> >> Yes, my description of 2 was a bit vague, but this was what I meant I think. >> >> Having static methods that delegate to the instance() does make the >> calls more readable, so I like that. >> >>> >>> Approach 3 works well if a hard runtime dependency between the plugins is >>> ok. We still experiment with ways for avoiding the monotonous work of >>> adding add/registerXXX, remove/unregisterXXX, methods for managing the list >>> of implementations. One experiment is Utils::ObjectPool. One could add a >>> public static instance of that to the core plugin in your example, and then >>> call Core::foos.addObject(myIFooImpl) from Bar plugin. >> >> Ah, I hadn't seen that one. That makes sense. I see that it also >> defaults to destroying the objects when the pool destructs, which >> makes sense since looking at the number of IPlugin::addObject calls vs >> IPlugin::addAutoReleasedObject, it looks like most of the time you >> want the auto-destruction behavior. >> >>> >>> I’d also prefer having fewer hard runtime dependencies, but it is a >>> struggle… Qt helps a bit, but it is still not nice. >>> There is the Q_DECLARE_INTERFACE/Q_INTERFACES combo + object pool, there is >>> invokeMethod + object pool, and the other thing that we also use is custom >>> QObject properties (e.g. CodePaster looks for a custom property “plainText” >>> in the current IEditor, as a fallback for finding pasteable text). >> >> Alright, thanks for the insight! >> >> Elvis >> >>> >>> Br, Eike > > -- > Eike Ziller > Principal Software Engineer > > The Qt Company GmbH > Rudower Chaussee 13 > D-12489 Berlin > [email protected] > http://qt.io > Geschäftsführer: Mika Pälsi, > Juha Varelius, Mika Harjuaho > Sitz der Gesellschaft: Berlin, Registergericht: Amtsgericht Charlottenburg, > HRB 144331 B > _______________________________________________ Qt-creator mailing list [email protected] http://lists.qt-project.org/mailman/listinfo/qt-creator
