[Development] The future of QProperty and QBindable
Ulf Hermann
ulf.hermann at qt.io
Fri Mar 7 10:14:56 CET 2025
Hi,
We've had a discussion [1] about QProperty and QBindable during the last
contributors' summit, but unfortunately didn't reach any conclusion.
I'll summarize the most important part of the discussion here:
"There is functionality to retrofit Q_PROPERTY with synthetic bindables."
This may be surprising but bear with me. First, (belated) big thanks to
Patrick Stewart for making this happen. The relevant API are the
constructors for QBindable [2][3].
Without integration with moc, Q_PROPERTY and QML, QProperty and
QBindable would be fairly simple and self-contained pieces of code. They
could be moved to a separate library, e.g. QtBindable and live there
without hurting anyone.
Conversely, the deep integration with moc, Q_PROPERTY and QML gives us a
lot of headaches. If you use a QProperty to back a Q_PROPERTY you need
to be extremely careful when writing getters and setter, so that you
don't accidentally produce spurious dependencies. A classic example is a
setter first comparing the value before setting it. You can't just do
"if (someQProperty.value() == newValue)". You have to do
"someProperty.valueBypassingBindings()". Furthermore, you need to call
removeBindingUnlessInWrapper() at the right place so that any bindings
to the property are broken when setting a new value (even if the value
is the same). The complexity grows if you have properties that depend on
each other or otherwise interact. In general, this is not something we
can ask users to get right. We ourselves have produced a lot of bugs
[4][5] when introducing bindables into the few places in Qt where we
have them now. And we're not done fixing them [6]. Finally, the
QBindingStorage member in QObjectData that makes all this possible,
increases the size of every QObject by two pointers, no matter if it
uses bindables or not.
Let's take a step back here, though. The original idea of introducing
QProperty and QBindable was twofold:
1. Offer a convenient C++ API for bindings.
2. Enable the Qt Quick Compiler to generate better C++ code for bindings.
1 can be achieved without deep integration into moc and Q_PROPERTY. An
external adapter for Q_PROPERTY is generally enough to connect
Q_PROPERTY with bindables. Granted, the result will probably be slower
than a deeply integrated bindable, However, given the amount of
optimization we needed to put in to get any visible performance edge for
QProperty/QBindable over classic signal/slot connections at all, I
expect the difference to be small.
2 turned out to be chimera. qmlsc can generate rather performant code
for a binding using QProperty directly, but there are preconditions:
a, The binding needs to be compilable in the first place.
b, You need to use direct mode [7], which implies private API and
accessible headers.
c, The property written to has to be bindable.
d, The properties read from have to be bindable.
In practice, this rarely materializes. On the flip side, when not used
directly like this, but rather through dynamically typed lookups inside
QQmlEngine, or in the adaptor methods for AOT-compiled non-direct code,
QProperty and QBindable do not offer a clear performance benefit over
regular READ/WRITE/NOTIFY properties. They do, however, produce a lot of
complexity. I'm currently working on the property-to-property binding we
use to implement the interaction between models and delegates via
required properties [8]. This thing requires four different
implementations to cover all the permutations. And this is just one example.
So here is my suggestion:
1, Deprecate the BINDABLE attribute to Q_PROPERTY. We probably can't
have moc produce a warning about it right away, but we can already adapt
the documentation.
2, Deprecate QObjectBindableProperty and friends.
3, Revert our own bindable Q_PROPERTYs to be backed by simple data
members rather than QProperty again. Implement the public fooBindable()
methods in terms of the Q_PROPERTY adaptors for QBindable and deprecate
them. Drop all the BINDABLEs in private API.
4, Remove the integration with QProperty and QBindable in QML. QML will
always use the READ/WRITE/NOTIFY methods again and ignore any BINDABLE.
5, In Qt7, move the remaining QProperty and QBindable code into a
separate library.
This sounds easy, but there is a slight caveat: You can omit the change
signal if you have a BINDABLE, and QML will currently still be able to
listen to the property. I guess some projects are doing this. If QML
were to completely ignore all bindables, it would not be able to get
notified about those properties anymore. Therefore, both moc and the QML
engine should warn about BINDABLEs without NOTIFY. Ideally, as a porting
helper, moc could temporarily accept "NOTIFY default" to generate a
notification signal from a bindable, but I don't know if we can pull
this off. Only afterwards, maybe after two minor versions of Qt, we can
remove bindable support from QML.
I'm registering another session for the upcoming contributors' summit in
May [9], but I would appreciate opinions already before.
best regards,
Ulf
[1] https://wiki.qt.io/QtCS2024_QProperty
[2] https://doc.qt.io/qt-6/qbindable.html#QBindable
[3] https://doc.qt.io/qt-6/qbindable.html#QBindable-1
[4] https://bugreports.qt.io/browse/QTBUG-116211
[5] https://bugreports.qt.io/browse/QTBUG-116345 and its sub-tasks
[6] https://codereview.qt-project.org/c/qt/qtbase/+/611485
[7] https://doc.qt.io/Qt-6/qtqml-qml-script-compiler.html#direct-mode
[8]
https://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/qml/qml/qqmlpropertytopropertybinding.cpp
[9] https://wiki.qt.io/Qt_Contributor_Summit_2025_-_Program
More information about the Development
mailing list