Playing with Qt's reflexion

11 views
Skip to first unread message

Yann Dirson

unread,
Apr 6, 2008, 8:18:55 PM4/6/08
to tagua...@googlegroups.com
Following the recent discussion on IRC with Paolo, I started to look
more in-depth at the Qt Object model, what we can get from it, and how
to use it. My example (and problem ;) here is setting up a
MiniShogi::Behaviour instance reusing from Shogi::Behaviour, which
itself reuses from Chess::Behaviour through Delegators::Behaviour.

Even without taking into account the fact that it requires link-time
dependency between plugins, we can see an example of a problem when
using standard RTTI, while trying to clone the Shogi::Behaviour
referenced by the Shogi::State we just successfully cloned:

const Shogi::Behaviour* shogi_behaviour =
dynamic_cast<const Shogi::Behaviour*>(shogi_state->behaviour());
Q_ASSERT(shogi_behaviour);
kDebug() << "shogi_behaviour"<< shogi_behaviour
<< "is a" << typeid(*shogi_behaviour).name();
Shogi::Behaviour* shogi_behaviour_clone =
dynamic_cast<Shogi::Behaviour*>(shogi_behaviour->clone());
kDebug() << "shogi_behaviour_clone" << shogi_behaviour_clone
<< "is a" << typeid(*shogi_behaviour_clone).name();
Q_ASSERT(shogi_behaviour_clone);
Behaviour* behaviour = new Behaviour(shogi_behaviour_clone);

What I get for traces is:

tagua(10470) taguaminishogi_initrepo: shogi_behaviour Shogi::Behaviour(0x81a1098) is a N5Shogi9BehaviourE
tagua(10470) Shogi::Behaviour::clone: Shogi::Behaviour(0x81a1098) 0x81a10a8 is a PKN5Shogi9BehaviourE
tagua(10470) taguaminishogi_initrepo: shogi_behaviour_clone Shogi::Behaviour(0x8134168) is a N5Shogi9BehaviourE
tagua(10470): Behaviour::setDelegator needs a Shogi::Behaviour, cannot use a P10IBehaviour

That is, despîte the shogi_behaviour_clone pointing to a real
Shogi::Behaviour, the type info does not cross the shared-library
boundary. The GCC FAQ has something about this issue[1], but it seems
difficult to get things right.

I guess this is enough of reasons to stop playing with inter-plugin
link-time dependencies, if any other would be enclined to do so in the
future :)


So I have started to dig further into using Qt reflection.

The first major problem I encounter is that our "interface classes"
are not QObjects: this requires to do all sort of dirty things like
typing method arguments as Component*, loosing compiler type
checks... or to change things so that those "interface" types inherit
Component themselves.

The good news is: at last my testbed implementation of Shogi promotion
gets inherited by MiniShogi !

Despite the highly incomplete state of my implementation, I have
pushed this to my public repo (still on branch component-api), I'll
try to have some sleep now ;). It will require a good deal of
cleanup, and finishing the whole migration (I have only converted the
Behaviour hierarchy so far, and even then, I restricted myself to what
was necessary for MiniShogi - the same changes have to be done for all
variants and all component hierarchies).

The bad news is, we're now down to "manual delegation" to call against
a component whatever method was not declared in the core interface for
that component - it's quite tedious and error-prone to write the call
to those additional methods. We could maybe improve that using a
translation from an IDL description, as discussed on IRC (we would
need to generate both a delegator and macros to invoke delegatable
methods - which will not guard us against forgetting to use the
macros), or as I mentionned on an earlier post, by having all of these
repetitive things generated on the fly as a preprocessing stage, using
an openc++-like mechanism. If someone wants to look at such a one of
these approaches, I think we'll shortly be ready to use such a work :)

Best regards,
--
Yann

[1] http://gcc.gnu.org/faq.html#dso

Paolo Capriotti

unread,
Apr 7, 2008, 4:54:16 AM4/7/08
to tagua...@googlegroups.com
Yann Dirson wrote:
>
> I guess this is enough of reasons to stop playing with inter-plugin
> link-time dependencies, if any other would be enclined to do so in the
> future :)

Agreed! This is a very good reason to avoid "soft" dependencies between
plugins. Header-only classes are compiled as different classes in the
two plugins, so RTTI doesn't work correctly.
All classes that should behave polymorphically must have at least one
virtual method defined in a cpp file. But then you need a link-time
dependency.

> So I have started to dig further into using Qt reflection.
>
> The first major problem I encounter is that our "interface classes"
> are not QObjects: this requires to do all sort of dirty things like
> typing method arguments as Component*, loosing compiler type
> checks... or to change things so that those "interface" types inherit
> Component themselves.

I would like to avoid that, because otherwise these types stop to be
interfaces (Component is quite a heavy concrete class). I think you
don't actually need to have IBehaviour inherit Component, if you put a
couple of dynamic_cast's at the right spots :)

> The good news is: at last my testbed implementation of Shogi promotion
> gets inherited by MiniShogi !

\o/

> Despite the highly incomplete state of my implementation, I have
> pushed this to my public repo (still on branch component-api), I'll
> try to have some sleep now ;). It will require a good deal of
> cleanup, and finishing the whole migration (I have only converted the
> Behaviour hierarchy so far, and even then, I restricted myself to what
> was necessary for MiniShogi - the same changes have to be done for all
> variants and all component hierarchies).
>
> The bad news is, we're now down to "manual delegation" to call against
> a component whatever method was not declared in the core interface for
> that component - it's quite tedious and error-prone to write the call
> to those additional methods. We could maybe improve that using a
> translation from an IDL description, as discussed on IRC (we would
> need to generate both a delegator and macros to invoke delegatable
> methods - which will not guard us against forgetting to use the
> macros), or as I mentionned on an earlier post, by having all of these
> repetitive things generated on the fly as a preprocessing stage, using
> an openc++-like mechanism. If someone wants to look at such a one of
> these approaches, I think we'll shortly be ready to use such a work :)

I think the problem is that we're using a language which is simply not
expressive enough. It seems to me that most of the problems and ugliness
of the implementation would be solved by creating bindings to a language
that allows for a little more dynamic behaviour.
The most immediate examples would be Ruby, Python or Javascript, but I
think that even Java or C# would behave a lot better in this respect,
though probably some ugliness would still remain using these languages.
During the last Christmas holidays, I had hacked together an incomplete
ruby binding which allowed to use Qt's reflection transparently (via
method missing). If we manage to make something like that work perfectly
(that means resolving all the outstanding memory management issues,
first) we could even think to keep the C++ API internal to avoid the
hassle to expose this complicated mess that even our most dedicated
developer (heck, even myself!) has trouble understanding :)
But if someone feels like exploring the openc++ world, I would not
certainly discourage him. Having a nice C++ API for plugin writers would
be ideal, since C++ is the native language of the KDE community.

Ciao,
Paolo Capriotti

Yann Dirson

unread,
Apr 7, 2008, 5:09:22 AM4/7/08
to tagua...@googlegroups.com
On Mon, Apr 07, 2008 at 10:54:16AM +0200, Paolo Capriotti wrote:
>
> Yann Dirson wrote:
> >
> > I guess this is enough of reasons to stop playing with inter-plugin
> > link-time dependencies, if any other would be enclined to do so in the
> > future :)
>
> Agreed! This is a very good reason to avoid "soft" dependencies between
> plugins. Header-only classes are compiled as different classes in the
> two plugins, so RTTI doesn't work correctly.
> All classes that should behave polymorphically must have at least one
> virtual method defined in a cpp file. But then you need a link-time
> dependency.

Yes, but that's not the only problem. If I understand correctly the
FAQ item, by default the symbols necessary for RTTI are not exported,
so the dynamic linker cannot collapse type-info for identical classes
into identical type-info. This a problem which does not impact Qt's
qobject_cast.

> > So I have started to dig further into using Qt reflection.
> >
> > The first major problem I encounter is that our "interface classes"
> > are not QObjects: this requires to do all sort of dirty things like
> > typing method arguments as Component*, loosing compiler type
> > checks... or to change things so that those "interface" types inherit
> > Component themselves.
>
> I would like to avoid that, because otherwise these types stop to be
> interfaces (Component is quite a heavy concrete class). I think you
> don't actually need to have IBehaviour inherit Component, if you put a
> couple of dynamic_cast's at the right spots :)

Well, this is what I started to do, but I got hit pretty rapidly by
the RTTI/shared-lib problem, which magically disappeared with Qt
reflexion.

Additionally, since every concrete component has to be a subclass of
Component anyway, inheriting at the upper level also makes the
hierarchy tree cleaner. Sure those upper-level classes are not pure
interfaces any more, but do we lose anything by doing so ?

But of course, anyone is free to point to something I would have
missed - it would be light enough to revert that change, and then to
switch to dynamic_cast.


> I think the problem is that we're using a language which is simply not
> expressive enough. It seems to me that most of the problems and ugliness
> of the implementation would be solved by creating bindings to a language
> that allows for a little more dynamic behaviour.
> The most immediate examples would be Ruby, Python or Javascript, but I
> think that even Java or C# would behave a lot better in this respect,
> though probably some ugliness would still remain using these languages.
> During the last Christmas holidays, I had hacked together an incomplete
> ruby binding which allowed to use Qt's reflection transparently (via
> method missing). If we manage to make something like that work perfectly
> (that means resolving all the outstanding memory management issues,
> first) we could even think to keep the C++ API internal to avoid the
> hassle to expose this complicated mess that even our most dedicated
> developer (heck, even myself!) has trouble understanding :)
> But if someone feels like exploring the openc++ world, I would not
> certainly discourage him. Having a nice C++ API for plugin writers would
> be ideal, since C++ is the native language of the KDE community.

Seconded :)

--
Yann

Reply all
Reply to author
Forward
0 new messages