[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