Hi all,

After the long discussions a few weeks back about the new property system and 
some trial to port the existing properties over to the new system, it became 
somewhat clear to me that the approach we had tried so far won’t work and be 
ready in time for the feature freeze.

The main problems identified where
* the reliance on [[no_unique_address]] which is a C++20 feature
* The additional storage overhead for bindings
* the fact that the decision whether a property can use bindings or not is a 
decision that changes the ABI. This implied, that we could not add binding 
support to a property without breaking binary compatibility after 6.0 has been 
released.

So I sat down and did quite a bit of work redesigning the system over the last 
three weeks. I am pretty happy with the result I now came up with, and hope to 
have it all merged before the feature freeze.

The redesigned system keeps most of the low level API the same, so you still 
have QProperty<T> and QPropertyBinding to create bindings. But it does 
integrate much better with the existing property system that we have from Qt 
5.x.

QProperty<T> has as a design choice coupled the storage of the property data 
with an additional pointer to store any possible binding related data. The 
redesign breaks this hard dependency, and allows separating the data storage 
from the binding data.

I’ve added a new QBindingStorage class that can store bindings for a whole 
object. QObject now has one of those by default. If bindings aren’t used this 
comes down to two additional pointers inside QObjectPrivate (and not one 
additional pointer per property). The class is currently marked internal, but 
can in principle be used to add a binding storage also to other types of 
objects.

Together with QBindingStorage, we now have a couple of new classes to store 
properties without memory overhead. As QBindingStorage and those classes are 
aimed at QObject derived classes (others should use QProperty<T> with inline 
binding storage), those classes are called 
QObjectBindable/Computed/CompatProperty. They all add binding support to a 
property, but serve slightly different purposes:
* QObjectBindableProperty should be the default to use, especially for new 
code. It does lazy binding evaluation, and does support an optional changed 
signal.
* QObjectComputedProperty can be used for read-only properties where the value 
is computed on the fly. It supports being used in binding expressions, but you 
can not create a binding on it (as it’s read-only).
* QObjectCompatProperty is there to help port existing properties over and make 
them bindable. It does eager binding evaluation and calls the existing property 
setter. This is the class to use for easy porting if you can’t or don’t want to 
refactor a lot of code. It needs to evaluate bindings eagerly, as many of the 
existing property setters have side effects.

Using those classes happens through a 
Q_OBJECT_BINDABLE/COMPUTED/COMPAT_PROPERTY macro. We need the macro to be able 
to get back from the properties to the binding storage. This is the one place 
where we rely on a non standard (but supported by all our compilers) feature. 
Namely the ability to call offsetof() on non-standard types.

Binding support through the public API is now exposed through a QBindable<T> 
class, that provides the binding interface towards those properties. In 
addition to the getter and setter, the object will also need one Bindable<Type> 
bindableProp() for a property prop to make the binding support accessible. This 
method will need to get implemented by the developer.

As a final piece, moc can also generate some support for introspecting those 
bindings. This simply requires adding a “BINDABLE bindableProp” section to the 
existing Q_PROPERTY() macro. With that, you can set and inspect bindings though 
QMetaProperty.

You can find an example where I ported QAbstractAnimation to enable bindings 
here: https://codereview.qt-project.org/c/qt/qtbase/+/310748. 

As you can see, it only required changing the storage of the property data to 
use Q_OBJECT_BINDABLE(COMPUTED)_PROPERTY, adding the bindingProp() accessors to 
the public API and making them known to the meta object system by adding the 
BINDABLE section to Q_PROPERTY().

To summarise the advantages of the redesigned system:

* Very little memory overhead if bindings aren’t used
* Very low runtime overhead if bindings aren’t used
* Binding support can be added to properties in QObjects in a binary compatible 
way
* Straight forward extension of the existing and known property system
* Much easier to port existing properties

https://codereview.qt-project.org/c/qt/qtbase/+/310748 is also the end of the 
patch series that does implement the required infrastructure. It’s still 
lacking a bit of documentation, but the series is now pretty clean and should 
be in a good state for reviewing.

Cheers,
Lars

_______________________________________________
Development mailing list
[email protected]
https://lists.qt-project.org/listinfo/development

Reply via email to