Greetings and Salutations,
As some of you may know, QMetaObject has a problem with what we call
"shadowing". Consider:
class Base
{
Q_OBJECT
Q_PROPERTY(int a READ a CONSTANT)
public:
int a() const { return 4; }
Q_INVOKABLE int b() const { return 5; }
};
class Derived : public Base
{
Q_OBJECT
Q_PROPERTY(int a READ a CONSTANT) // shadows Base::a
public:
int a() const { return 6; }
// Shadows Base::b
Q_INVOKABLE int b() const { return 7; }
};
Now when you get a Base* and retrieve its property "a" or call its
method "b" via the QMetaObject, you don't actually know which property
you are retrieving or which method you are calling.
From C++ you can introspect the QMetaObject hierarchy and determine
what property or method you're dealing with. This is much more
complicated than the usual way of simply doing
someObject->property("a"), though.
From QML, you can't. QML will always use the most-derived QMetaObject
that has the property or method. You can shadow properties and methods
further in QML, and most egregiously, you can shadow properties with
methods and vice versa. This is rather dangerous because we internally
rely on many of the properties of, say QQuickItem, to actually assume
the values we set on them. If you shadow a property like
QQuickItem::data, you're certainly in for some surprises. It is quite
common to add a property called "data" to one of your custom types,
though. So, this is inviting some serious foot archery. For fun and
confusion, try the following:
// Base.qml
import QtQuick
Item {
id: root
property int data
}
// Derived.qml
import QtQuick
Base{
Rectangle {}
}
This results in the following warning:
Derived.qml:3:4: Cannot assign value of type "Rectangle" to property
"data", expecting "int"
We've recognized this as a problem a long time ago and introduced the
FINAL attribute to Q_PROPERTY. The FINAL attribute only takes effect if
you use QML. If you shadow a FINAL property in pure C++, it does nothing
(unfortunately). However, if you try to use a *shadowed* FINAL property
in QML you get a warning or an error, depending on the context. We can't
really do better than this for FINAL alone. You can call moc on Base and
Derived independently, and you can re-moc Base after building Derived.
This way you can sneakily introduce a FINAL after the derived type
already exists. So we have to accept some fuzziness there. It follows
that also in QML we can't flat out reject all your shadowed properties.
We can just assume they are not shadowed and still use the base type's
properties in case they are.
On top of all this complication, however, the presence of FINAL is not
quite enough because you have to remember to add FINAL to every property
that's eligible for finality. Keeping track of this is messy and
error-prone. As a result, few properties are FINAL in practice, even if
most should be.
C++, back in the day, had a similar problem with virtual methods. When
you declared a method in a derived type, you'd have to look up all the
methods in all base types to figure out if the method you were declaring
was virtual by inheritance. This was error-prone. You could accidentally
introduce overriding methods that weren't meant to be virtual and vice
versa.
In C++, this was solved by introducing the "override" and "final"
keywords to mark methods that are intentionally overriding a virtual
one. The introduction of the "override" and "final" keywords came with a
number of warnings and errors that now steer you towards adding the new
keywords to all methods that intentionally override, and only those.
In Qt 6.11, we're doing the same to Q_PROPERTY. In addition to FINAL,
you can now add attributes VIRTUAL and OVERRIDE to mark properties that
are intended to be overridden and ones that intentionally override a
virtual property. Likewise, in QML you can mark properties with the
"virtual", "override", and "final" keywords. If you use those new
attributes and keywords, you get appropriate warnings and errors in QML
(but not in C++, because see above). If you don't use them, you don't
get warned ... yet. This is because we haven't adapted all of Qt to be
free of those warnings, yet.
We still highly recommend you clean up unintended shadowing and use the
new keywords and attributes where applicable already in Qt 6.11. You can
get warned about shadowing properties without the new keywords and
attributes by enabling debug messages from the
qt.qml.propertyCache.append logging category. For example with an
environment variable like this:
QT_LOGGING_RULES="qt.qml.propertyCache.append.debug=true"
For Qt 6.12, we are planning to enable these warnings by default. Among
other things, this will mean that you will get warned when shadowing
properties from Qt types that aren't meant to be shadowed.
In addition, we are planning to resolve the issue of shadowing methods
and shadowing properties with methods in a similar way. We don't know
when this will be ready, though.
best regards,
Ulf
--
Development mailing list
[email protected]
https://lists.qt-project.org/listinfo/development