diff --git a/src/corelib/doc/snippets/qmetatype/registerConverters.cpp b/src/corelib/doc/snippets/qmetatype/registerConverters.cpp index 5c5e76c7b05..f53d04b7a60 100644 --- a/src/corelib/doc/snippets/qmetatype/registerConverters.cpp +++ b/src/corelib/doc/snippets/qmetatype/registerConverters.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#include #include #include @@ -50,5 +51,12 @@ int main() { QMetaType::registerConverter([](const CustomStringType &str) { return QString::fromUtf8(str.data()); }); + QMetaType::registerConverter( + [](const QJsonValue &value) -> std::optional { + const auto object = value.toObject(); + if (!object.contains("x") || !object.contains("y")) + return std::nullopt; // The conversion fails if the required properties are missing + return QPointF{object["x"].toDouble(), object["y"].toDouble()}; + }); //! [unaryfunc] } diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index a294c24b063..5ad79676050 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -1721,7 +1721,8 @@ Q_GLOBAL_STATIC(QMetaTypeMutableViewRegistry, customTypesMutableViewRegistry) to type To in the meta type system. Returns \c true if the registration succeeded, otherwise false. \a function must take an instance of type \c From and return an instance of \c To. It can be a function - pointer, a lambda or a functor object. + pointer, a lambda or a functor object. Since Qt 6.5, the \a function can also return an instance of + \c std::optional to be able to indicate failed conversions. \snippet qmetatype/registerConverters.cpp unaryfunc */ diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index b46b16604ad..f6eb4536a49 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #ifdef Bool #error qmetatype.h must be included before any header file that defines Bool @@ -600,7 +602,15 @@ public: auto converter = [function = std::move(function)](const void *from, void *to) -> bool { const From *f = static_cast(from); To *t = static_cast(to); - *t = function(*f); + auto &&r = function(*f); + if constexpr (std::is_same_v>, + std::optional>) { + if (!r) + return false; + *t = *std::forward(r); + } else { + *t = std::forward(r); + } return true; }; return registerConverterImpl(std::move(converter), fromType, toType); diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp index f2343bbbd24..c0650c7b1a4 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp @@ -30,6 +30,30 @@ struct ConvertFunctor } }; +template +struct OptionalWrapper +{ + std::optional operator()(const T& t) const + { + if (!CustomConvertibleType::s_ok) + return std::nullopt; + + return t; + } +}; + +template +struct ConvertFunctorWithOptional +{ + std::optional operator()(const From& f) const + { + if (!CustomConvertibleType::s_ok) + return std::nullopt; + + return CustomConvertibleType(QVariant::fromValue(f)); + } +}; + template bool hasRegisteredConverterFunction() { @@ -81,6 +105,7 @@ void customTypeNotYetConvertible() testCustomTypeNotYetConvertible(); testCustomTypeNotYetConvertible(); testCustomTypeNotYetConvertible(); + testCustomTypeNotYetConvertible>(); } void registerCustomTypeConversions() @@ -99,20 +124,22 @@ void registerCustomTypeConversions() QVERIFY((QMetaType::registerConverter(&CustomConvertibleType::convert))); QVERIFY((QMetaType::registerConverter(&CustomConvertibleType::convertOk))); QVERIFY((QMetaType::registerConverter(&CustomConvertibleType::convert))); - QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); + QVERIFY((QMetaType::registerConverter(ConvertFunctorWithOptional()))); QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); - QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); + QVERIFY((QMetaType::registerConverter(ConvertFunctorWithOptional()))); QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); - QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); + QVERIFY((QMetaType::registerConverter(ConvertFunctorWithOptional()))); QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); - QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); + QVERIFY((QMetaType::registerConverter(ConvertFunctorWithOptional()))); QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); - QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); + QVERIFY((QMetaType::registerConverter(ConvertFunctorWithOptional()))); QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); - QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); + QVERIFY((QMetaType::registerConverter(ConvertFunctorWithOptional()))); QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); - QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); + QVERIFY((QMetaType::registerConverter(ConvertFunctorWithOptional()))); QVERIFY((QMetaType::registerConverter(ConvertFunctor()))); + QVERIFY((QMetaType::registerConverter>(OptionalWrapper()))); + QVERIFY((QMetaType::registerConverter())); QTest::ignoreMessage(QtWarningMsg, "Type conversion already registered from type CustomConvertibleType to type CustomConvertibleType2"); QVERIFY((!QMetaType::registerConverter())); @@ -171,7 +198,7 @@ void tst_QMetaType::convertCustomType() QCOMPARE(v.toString(), ok ? testQString : QString()); QCOMPARE(v.value(), ok ? testQString : QString()); QVERIFY(CustomConvertibleType::s_value.canConvert()); - QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toString()), testQString); + QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toString()), ok ? testQString : QString()); QFETCH(bool, testBool); CustomConvertibleType::s_value = testBool; @@ -183,7 +210,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testInt; QCOMPARE(v.toInt(), ok ? testInt : 0); QCOMPARE(v.value(), ok ? testInt : 0); - QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toInt()), testInt); + QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toInt()), ok ? testInt : 0); QFETCH(double, testDouble); CustomConvertibleType::s_value = testDouble; @@ -195,7 +222,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testFloat; QCOMPARE(v.toFloat(), ok ? testFloat : 0.0); QCOMPARE(v.value(), ok ? testFloat : 0.0); - QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toFloat()), testFloat); + QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toFloat()), ok ? testFloat : 0); QFETCH(QRect, testQRect); CustomConvertibleType::s_value = testQRect; @@ -207,7 +234,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testQRectF; QCOMPARE(v.toRectF(), ok ? testQRectF : QRectF()); QCOMPARE(v.value(), ok ? testQRectF : QRectF()); - QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toRectF()), testQRectF); + QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toRectF()), ok ? testQRectF : QRectF()); QFETCH(QPoint, testQPoint); CustomConvertibleType::s_value = testQPoint; @@ -219,7 +246,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testQPointF; QCOMPARE(v.toPointF(), ok ? testQPointF : QPointF()); QCOMPARE(v.value(), ok ? testQPointF : QPointF()); - QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toPointF()), testQPointF); + QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toPointF()), ok ? testQPointF : QPointF()); QFETCH(QSize, testQSize); CustomConvertibleType::s_value = testQSize; @@ -231,7 +258,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testQSizeF; QCOMPARE(v.toSizeF(), ok ? testQSizeF : QSizeF()); QCOMPARE(v.value(), ok ? testQSizeF : QSizeF()); - QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toSizeF()), testQSizeF); + QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toSizeF()), ok ? testQSizeF : QSizeF()); QFETCH(QLine, testQLine); CustomConvertibleType::s_value = testQLine; @@ -243,7 +270,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testQLineF; QCOMPARE(v.toLineF(), ok ? testQLineF : QLineF()); QCOMPARE(v.value(), ok ? testQLineF : QLineF()); - QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toLineF()), testQLineF); + QCOMPARE((CustomConvertibleType::s_value.value().m_foo.toLineF()), ok ? testQLineF : QLineF()); QFETCH(QChar, testQChar); CustomConvertibleType::s_value = testQChar; @@ -255,6 +282,18 @@ void tst_QMetaType::convertCustomType() QVERIFY(v.canConvert(QMetaType(::qMetaTypeId()))); QCOMPARE(v.value().m_foo, testCustom.m_foo); + // Check that converters that actually convert to std::optional are not + // taken to indicate success or failure of the conversion. In these cases, + // the conversion must always succeed, even if the converter has returned a + // nullopt. + v = QVariant::fromValue(testCustom); + QVERIFY(v.canConvert(QMetaType::fromType>())); + QVERIFY(v.convert(QMetaType::fromType>())); + QCOMPARE(v.value>().has_value(), ok); + if (ok) { + QCOMPARE(v.value>().value().m_foo, testCustom.m_foo); + } + QFETCH(DerivedGadgetType, testDerived); v = QVariant::fromValue(testDerived); QCOMPARE(v.metaType(), QMetaType::fromType());