[Development] std::optional for Q_PROPERTY

Volker Hilsheimer volker.hilsheimer at qt.io
Fri Jul 21 11:01:03 CEST 2023


> On 20 Jul 2023, at 16:17, Fabian Kosmale via Development <development at qt-project.org> wrote:
>> Hi everyone,
>> 
>> something that came up during this year’s KDE Akademy was that we
>> believe Q_PROPERTY should be able to handle std::optional, such that you
>> get a null QVariant out of it if it has no value. The main use case
>> being interfacing “more modern C++” with QML in a type-safe way, rather
>> than using QJSValue::Undefined for invalid results.
>> 

[…]

>> Is that something that we would like to see? I was told that a QMetaType
>> is “full” and version bump needs to happen first before it can be
>> extended or something?
>> 
>> Cheers
>> Kai Uwe
> 
> Hi,
> 
> as far as QMetaType is concerned: We need a way to have some introspection on
> the "contained" type. That can't really be shoehorned into the current
> QMetaTypeInterface, because that one has neither enough space, nor a
> useable function pointer that could be hijacked.
> However, incrementing the QMetaTypeInterface revision and appending a new
> member is possible (QMTI has been written with extensibility in mind), and then
> you can add another function pointer to e.g. retrieve QMetaType::fromType<T>()
> if you have QMetaType::fromType<std::optional<T>>. Gettnig that in should be
> mostly straightforward.

[…]

> In general, I think having a way to expose optional value in a nice way is
> worthwhile, but I'm currently not sure what the best way forward would be.


Agree that supporting std::optional would be useful!

> At the QML layer, we would need to have some discussion on how to map the
> values. Should a std::optional<T> be "undefined if it's nullopt, and behave
> like Q_PROPERTY of type T otherwise"?

With `maybeValue` being a `Q_PROPERTY(std::optional<T> …)`,

    object->property(“maybeValue”);

would return a null-QVariant if the property is nullopt, which maps to undefined in QML. But should it hold a std::optional<T> or a T otherwise? QML needs a T; and on the C++ side we should probably keep the complete type information (and .value<T>() will have to return the same thing either way).


How about conversion:

    QVariant(std::optional<T>>).value<U>(); // return U{} if T cannot be converted; otherwise U(T);

    QVariant().value<std::optional<T>>(); // nullopt, not a std::optional holding a T{}
    QVariant(42).value<std::optional<int>>(); // std::optional holding 42
    QVariant(QSize()).value<std::optional<int>>(); // probably nullopt?


> That would mostly work, unless you want
> a RESETable property, where RESET shouldn't lead to a nullopt value.



Reseting a property should reset to what it was when the object was newly constructed, which would probably have been nullopt. Why is it problematic it RESETing a std::optional produces something else than nullopt?


> Should nullopt be mapped to null instead (but then you can't distinguish
> nullopt and nullptr). Or should we have optional<T> in QML in the same
> way we have list<T>? That would require defining its interaction with
> JS code.



Setting such a property to undefined should produce a std::nullopt on the C++ side, I think. And symmetrically, the value of a nullopt property should be undefined on the QML/JS side.


>> The question is also, where do we draw the line: I could see a use for
>> having an std::variant property for when we have a known set of types we
>> accept (e.g. std::variant<QString, QIcon>). Again something where we
>> currently would do a QVariant with manual conversions and missing
>> introspection.


Can you elaborate on that? What kind of introspection are you missing? Are you looking for a type-error on the caller (i.e. QML) side? E.g. if the property is of type std::variant<QString, QIcon>, and QML tries to set it to something else? What if that “something else" can be converted to a QString?

Volker



More information about the Development mailing list