[Development] QProperty and library coding guide

Lars Knoll lars.knoll at qt.io
Fri Jul 17 19:00:07 CEST 2020

> On 17 Jul 2020, at 15:01, Ulf Hermann <ulf.hermann at qt.io> wrote:
>>>>> QAction *action = ~~~;
>>>>> auto prop = action->text;
>>> This already gives you the string. You cannot retrieve the property
>>> itself. You can alternatively do action->text() or action->text.value().
>>> They all do the same thing.
>> Uhm... sorry, no, this doesn't really compute for me. Ignore the copy semantics for a second (use const auto &, if necessary), what's decltype(prop)? If it's QString, then you can't write .value() after it.
> OK, I got you wrong, and I was confused about the cast operators in QProperty and the property wrappers. Sorry. Indeed "auto prop = action->text;" would give you a useless object that pokes random memory whenever you invoke any method of it. That needs to be fixed indeed. You should not be able to copy the property wrapper out of the object. For properties on QObject that would be easy as QObject itself is not copyable. We could just =delete the copy ctor and assignment operators. We might also prevent external construction of the struct by having some "secret" extra argument, kind of like QPrivateSignal.

Right, using auto for the return value is something we need to think about a bit more.
> However, for Q_GADGET this would fall apart.
> For reference, Q_PRIVATE_QPROPERTY looks like this:
> #define Q_PRIVATE_QPROPERTY(accessor, type, name, setter, ...) \
>            struct _qt_property_api_##name { \
>                type value() const; \
>                type operator()() const { return value(); } \
>                void setValue(type &&); \
>                void setValue(type const &); \
>                void operator=(type const &v) { setValue(v); } \
>                void operator=(type &&v) { setValue(std::move(v)); } \
>                QPropertyBinding<type> setBinding(const QPropertyBinding<type> &); \
>                QPropertyBinding<type> setBinding(QPropertyBinding<type> &&); \
>                QPropertyBinding<type> operator=(const QPropertyBinding<type> &b) { return setBinding(b); } \
>                QPropertyBinding<type> operator=(QPropertyBinding<type> &&b) { return setBinding(std::move(b)); } \
>                bool setBinding(const QUntypedPropertyBinding &); \
>                template <typename Functor> \
>                QPropertyBinding<type> setBinding(Functor f, \
>                                                  const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) \
>                { \
>                    return setBinding(Qt::makePropertyBinding(f, location)); \
>                } \
>                bool hasBinding() const; \
>                QPropertyBinding<type> binding() const; \
>                QPropertyBinding<type> takeBinding(); \
>            }; \
>            void setter(type const& value);
> So, in fact we need to rework this and provide only methods instead of a struct. Now we need a naming convention for all those methods and some way of avoiding name clashes.

I am confused. Removing the struct means we’re back to a setFoo()/foo() style API, and especially, we have a different API for people using Property directly and what we provide in our public API. That simply doesn’t make sense.

> If QNotifiedProperty didn't need members of the private object as template parameters, we could just have one extra method that retrieves a reference to the underlying Q(Notified)Property (or rather two: const and non-const).
>> No, actually this makes perfect sense, but was contradicted before:
>>> We are not casting these structs to or from anything though, do we?
> That statement was wrong. We do need to cast the structs. Another argument for eliminating them.

I really want API consistency. We have QProperty as a public class for our users. Even if we need to hide our data for BC reasons, our API should not feel different than that one.

We’ve had a couple of ideas behind the design of the new property API:

* Allow for bindings from C++

Bindings in C++ are a killer feature for Qt 6. It was what made QML so successful over the last years. It’s also one of the things that enabled the huge performance gains we were able to achieve in Qt for MCU.

* Easy and transparent in its usage

Qt’s API and the interface we provide to our users should be the same. QProperty must hide it’s storage completely, so we can guarantee that bindings will work. For that we do need a QProperty class

* Ideally a “real” property style API that also other languages provide

If you have a property, I simply want to assign a value to it, or assign from it
to read out the value. That might not be 100% achievable given the ‘auto’ problem pointed out above, but it would be great if we could.


More information about the Development mailing list