diff options
author | Arno Rehn <a.rehn@menlosystems.com> | 2022-09-23 16:31:47 +0200 |
---|---|---|
committer | Arno Rehn <a.rehn@menlosystems.com> | 2022-09-27 20:59:21 +0200 |
commit | 7a7051b58f78a85cc55aece7b8691583c33ac47e (patch) | |
tree | e5c8bef3bac3a8e6bbd60818fc7e2af6e01ba96e | |
parent | 38f8d531a26720cbf5574f06481c58ef4dad7e4b (diff) |
QMetaType: Support custom unary converters with optional<To> return type
To indicate success of a conversion, the public API has previously only
supported registering member functions of the form To (From::*)(bool *).
When adding custom converters for types that cannot be modified, this is
usually not a possibility.
As an alternative, this patch adds support for std::optional in the
UnaryFunction overload of QMetaType::registerConverter. If the returned
optional has no value, the conversion is considered failed.
Task-number: QTBUG-92902
Change-Id: Ibac52d2cb9b5a2457081b4bebb0def1f03e3c55d
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | src/corelib/doc/snippets/qmetatype/registerConverters.cpp | 8 | ||||
-rw-r--r-- | src/corelib/kernel/qmetatype.cpp | 3 | ||||
-rw-r--r-- | src/corelib/kernel/qmetatype.h | 12 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp | 67 |
4 files changed, 74 insertions, 16 deletions
diff --git a/src/corelib/doc/snippets/qmetatype/registerConverters.cpp b/src/corelib/doc/snippets/qmetatype/registerConverters.cpp index 5c5e76c7b0..f53d04b7a6 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 <QJsonObject> #include <QMetaType> #include <QString> @@ -50,5 +51,12 @@ int main() { QMetaType::registerConverter<CustomStringType, QString>([](const CustomStringType &str) { return QString::fromUtf8(str.data()); }); + QMetaType::registerConverter<QJsonValue, QPointF>( + [](const QJsonValue &value) -> std::optional<QPointF> { + 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 a294c24b06..5ad7967605 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> 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 b46b16604a..f6eb4536a4 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -24,6 +24,8 @@ #include <list> #include <map> #include <functional> +#include <optional> +#include <type_traits> #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<const From *>(from); To *t = static_cast<To *>(to); - *t = function(*f); + auto &&r = function(*f); + if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<decltype(r)>>, + std::optional<To>>) { + if (!r) + return false; + *t = *std::forward<decltype(r)>(r); + } else { + *t = std::forward<decltype(r)>(r); + } return true; }; return registerConverterImpl<From, To>(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 f2343bbbd2..c0650c7b1a 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<typename T> +struct OptionalWrapper +{ + std::optional<T> operator()(const T& t) const + { + if (!CustomConvertibleType::s_ok) + return std::nullopt; + + return t; + } +}; + +template<typename From> +struct ConvertFunctorWithOptional +{ + std::optional<CustomConvertibleType> operator()(const From& f) const + { + if (!CustomConvertibleType::s_ok) + return std::nullopt; + + return CustomConvertibleType(QVariant::fromValue(f)); + } +}; + template<typename From, typename To> bool hasRegisteredConverterFunction() { @@ -81,6 +105,7 @@ void customTypeNotYetConvertible() testCustomTypeNotYetConvertible<QLineF, CustomConvertibleType>(); testCustomTypeNotYetConvertible<QChar, CustomConvertibleType>(); testCustomTypeNotYetConvertible<CustomConvertibleType, CustomConvertibleType2>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, std::optional<CustomConvertibleType>>(); } void registerCustomTypeConversions() @@ -99,20 +124,22 @@ void registerCustomTypeConversions() QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLine>(&CustomConvertibleType::convert<QLine>))); QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLineF>(&CustomConvertibleType::convertOk<QLineF>))); QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QChar>(&CustomConvertibleType::convert<QChar>))); - QVERIFY((QMetaType::registerConverter<QString, CustomConvertibleType>(ConvertFunctor<QString>()))); + QVERIFY((QMetaType::registerConverter<QString, CustomConvertibleType>(ConvertFunctorWithOptional<QString>()))); QVERIFY((QMetaType::registerConverter<bool, CustomConvertibleType>(ConvertFunctor<bool>()))); - QVERIFY((QMetaType::registerConverter<int, CustomConvertibleType>(ConvertFunctor<int>()))); + QVERIFY((QMetaType::registerConverter<int, CustomConvertibleType>(ConvertFunctorWithOptional<int>()))); QVERIFY((QMetaType::registerConverter<double, CustomConvertibleType>(ConvertFunctor<double>()))); - QVERIFY((QMetaType::registerConverter<float, CustomConvertibleType>(ConvertFunctor<float>()))); + QVERIFY((QMetaType::registerConverter<float, CustomConvertibleType>(ConvertFunctorWithOptional<float>()))); QVERIFY((QMetaType::registerConverter<QRect, CustomConvertibleType>(ConvertFunctor<QRect>()))); - QVERIFY((QMetaType::registerConverter<QRectF, CustomConvertibleType>(ConvertFunctor<QRectF>()))); + QVERIFY((QMetaType::registerConverter<QRectF, CustomConvertibleType>(ConvertFunctorWithOptional<QRectF>()))); QVERIFY((QMetaType::registerConverter<QPoint, CustomConvertibleType>(ConvertFunctor<QPoint>()))); - QVERIFY((QMetaType::registerConverter<QPointF, CustomConvertibleType>(ConvertFunctor<QPointF>()))); + QVERIFY((QMetaType::registerConverter<QPointF, CustomConvertibleType>(ConvertFunctorWithOptional<QPointF>()))); QVERIFY((QMetaType::registerConverter<QSize, CustomConvertibleType>(ConvertFunctor<QSize>()))); - QVERIFY((QMetaType::registerConverter<QSizeF, CustomConvertibleType>(ConvertFunctor<QSizeF>()))); + QVERIFY((QMetaType::registerConverter<QSizeF, CustomConvertibleType>(ConvertFunctorWithOptional<QSizeF>()))); QVERIFY((QMetaType::registerConverter<QLine, CustomConvertibleType>(ConvertFunctor<QLine>()))); - QVERIFY((QMetaType::registerConverter<QLineF, CustomConvertibleType>(ConvertFunctor<QLineF>()))); + QVERIFY((QMetaType::registerConverter<QLineF, CustomConvertibleType>(ConvertFunctorWithOptional<QLineF>()))); QVERIFY((QMetaType::registerConverter<QChar, CustomConvertibleType>(ConvertFunctor<QChar>()))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, std::optional<CustomConvertibleType>>(OptionalWrapper<CustomConvertibleType>()))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>())); QTest::ignoreMessage(QtWarningMsg, "Type conversion already registered from type CustomConvertibleType to type CustomConvertibleType2"); QVERIFY((!QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>())); @@ -171,7 +198,7 @@ void tst_QMetaType::convertCustomType() QCOMPARE(v.toString(), ok ? testQString : QString()); QCOMPARE(v.value<QString>(), ok ? testQString : QString()); QVERIFY(CustomConvertibleType::s_value.canConvert<CustomConvertibleType>()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toString()), testQString); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().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<int>(), ok ? testInt : 0); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toInt()), testInt); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().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<float>(), ok ? testFloat : 0.0); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toFloat()), testFloat); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().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<QRectF>(), ok ? testQRectF : QRectF()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toRectF()), testQRectF); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().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<QPointF>(), ok ? testQPointF : QPointF()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toPointF()), testQPointF); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().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<QSizeF>(), ok ? testQSizeF : QSizeF()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toSizeF()), testQSizeF); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().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<QLineF>(), ok ? testQLineF : QLineF()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toLineF()), testQLineF); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().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<CustomConvertibleType2>()))); QCOMPARE(v.value<CustomConvertibleType2>().m_foo, testCustom.m_foo); + // Check that converters that actually convert to std::optional<T> 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<std::optional<CustomConvertibleType>>())); + QVERIFY(v.convert(QMetaType::fromType<std::optional<CustomConvertibleType>>())); + QCOMPARE(v.value<std::optional<CustomConvertibleType>>().has_value(), ok); + if (ok) { + QCOMPARE(v.value<std::optional<CustomConvertibleType>>().value().m_foo, testCustom.m_foo); + } + QFETCH(DerivedGadgetType, testDerived); v = QVariant::fromValue(testDerived); QCOMPARE(v.metaType(), QMetaType::fromType<DerivedGadgetType>()); |