[Qt-creator] Couple of questions about the design of Qt Creator
Elvis Stansvik
elvstone at gmail.com
Mon Sep 11 10:16:33 CEST 2017
2017-09-11 10:14 GMT+02:00 Eike Ziller <Eike.Ziller at qt.io>:
>
>> On Sep 11, 2017, at 09:54, Elvis Stansvik <elvstone at gmail.com> wrote:
>>
>> 2017-09-11 9:33 GMT+02:00 Eike Ziller <Eike.Ziller at qt.io>:
>>>
>>>> On Sep 10, 2017, at 13:09, Elvis Stansvik <elvstone at gmail.com> 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
> eike.ziller at qt.io
> http://qt.io
> Geschäftsführer: Mika Pälsi,
> Juha Varelius, Mika Harjuaho
> Sitz der Gesellschaft: Berlin, Registergericht: Amtsgericht Charlottenburg, HRB 144331 B
>
More information about the Qt-creator
mailing list