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>
This commit is contained in:
parent
38f8d531a2
commit
7a7051b58f
@ -1,6 +1,7 @@
|
|||||||
// Copyright (C) 2021 The Qt Company Ltd.
|
// Copyright (C) 2021 The Qt Company Ltd.
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
@ -50,5 +51,12 @@ int main() {
|
|||||||
QMetaType::registerConverter<CustomStringType, QString>([](const CustomStringType &str) {
|
QMetaType::registerConverter<CustomStringType, QString>([](const CustomStringType &str) {
|
||||||
return QString::fromUtf8(str.data());
|
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]
|
//! [unaryfunc]
|
||||||
}
|
}
|
||||||
|
@ -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.
|
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
|
\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
|
\snippet qmetatype/registerConverters.cpp unaryfunc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#ifdef Bool
|
#ifdef Bool
|
||||||
#error qmetatype.h must be included before any header file that defines 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 {
|
auto converter = [function = std::move(function)](const void *from, void *to) -> bool {
|
||||||
const From *f = static_cast<const From *>(from);
|
const From *f = static_cast<const From *>(from);
|
||||||
To *t = static_cast<To *>(to);
|
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 true;
|
||||||
};
|
};
|
||||||
return registerConverterImpl<From, To>(std::move(converter), fromType, toType);
|
return registerConverterImpl<From, To>(std::move(converter), fromType, toType);
|
||||||
|
@ -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>
|
template<typename From, typename To>
|
||||||
bool hasRegisteredConverterFunction()
|
bool hasRegisteredConverterFunction()
|
||||||
{
|
{
|
||||||
@ -81,6 +105,7 @@ void customTypeNotYetConvertible()
|
|||||||
testCustomTypeNotYetConvertible<QLineF, CustomConvertibleType>();
|
testCustomTypeNotYetConvertible<QLineF, CustomConvertibleType>();
|
||||||
testCustomTypeNotYetConvertible<QChar, CustomConvertibleType>();
|
testCustomTypeNotYetConvertible<QChar, CustomConvertibleType>();
|
||||||
testCustomTypeNotYetConvertible<CustomConvertibleType, CustomConvertibleType2>();
|
testCustomTypeNotYetConvertible<CustomConvertibleType, CustomConvertibleType2>();
|
||||||
|
testCustomTypeNotYetConvertible<CustomConvertibleType, std::optional<CustomConvertibleType>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerCustomTypeConversions()
|
void registerCustomTypeConversions()
|
||||||
@ -99,20 +124,22 @@ void registerCustomTypeConversions()
|
|||||||
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLine>(&CustomConvertibleType::convert<QLine>)));
|
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLine>(&CustomConvertibleType::convert<QLine>)));
|
||||||
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLineF>(&CustomConvertibleType::convertOk<QLineF>)));
|
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLineF>(&CustomConvertibleType::convertOk<QLineF>)));
|
||||||
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QChar>(&CustomConvertibleType::convert<QChar>)));
|
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<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<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<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<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<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<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<QChar, CustomConvertibleType>(ConvertFunctor<QChar>())));
|
||||||
|
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, std::optional<CustomConvertibleType>>(OptionalWrapper<CustomConvertibleType>())));
|
||||||
|
|
||||||
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>()));
|
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>()));
|
||||||
QTest::ignoreMessage(QtWarningMsg, "Type conversion already registered from type CustomConvertibleType to type CustomConvertibleType2");
|
QTest::ignoreMessage(QtWarningMsg, "Type conversion already registered from type CustomConvertibleType to type CustomConvertibleType2");
|
||||||
QVERIFY((!QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>()));
|
QVERIFY((!QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>()));
|
||||||
@ -171,7 +198,7 @@ void tst_QMetaType::convertCustomType()
|
|||||||
QCOMPARE(v.toString(), ok ? testQString : QString());
|
QCOMPARE(v.toString(), ok ? testQString : QString());
|
||||||
QCOMPARE(v.value<QString>(), ok ? testQString : QString());
|
QCOMPARE(v.value<QString>(), ok ? testQString : QString());
|
||||||
QVERIFY(CustomConvertibleType::s_value.canConvert<CustomConvertibleType>());
|
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);
|
QFETCH(bool, testBool);
|
||||||
CustomConvertibleType::s_value = testBool;
|
CustomConvertibleType::s_value = testBool;
|
||||||
@ -183,7 +210,7 @@ void tst_QMetaType::convertCustomType()
|
|||||||
CustomConvertibleType::s_value = testInt;
|
CustomConvertibleType::s_value = testInt;
|
||||||
QCOMPARE(v.toInt(), ok ? testInt : 0);
|
QCOMPARE(v.toInt(), ok ? testInt : 0);
|
||||||
QCOMPARE(v.value<int>(), 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);
|
QFETCH(double, testDouble);
|
||||||
CustomConvertibleType::s_value = testDouble;
|
CustomConvertibleType::s_value = testDouble;
|
||||||
@ -195,7 +222,7 @@ void tst_QMetaType::convertCustomType()
|
|||||||
CustomConvertibleType::s_value = testFloat;
|
CustomConvertibleType::s_value = testFloat;
|
||||||
QCOMPARE(v.toFloat(), ok ? testFloat : 0.0);
|
QCOMPARE(v.toFloat(), ok ? testFloat : 0.0);
|
||||||
QCOMPARE(v.value<float>(), 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);
|
QFETCH(QRect, testQRect);
|
||||||
CustomConvertibleType::s_value = testQRect;
|
CustomConvertibleType::s_value = testQRect;
|
||||||
@ -207,7 +234,7 @@ void tst_QMetaType::convertCustomType()
|
|||||||
CustomConvertibleType::s_value = testQRectF;
|
CustomConvertibleType::s_value = testQRectF;
|
||||||
QCOMPARE(v.toRectF(), ok ? testQRectF : QRectF());
|
QCOMPARE(v.toRectF(), ok ? testQRectF : QRectF());
|
||||||
QCOMPARE(v.value<QRectF>(), 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);
|
QFETCH(QPoint, testQPoint);
|
||||||
CustomConvertibleType::s_value = testQPoint;
|
CustomConvertibleType::s_value = testQPoint;
|
||||||
@ -219,7 +246,7 @@ void tst_QMetaType::convertCustomType()
|
|||||||
CustomConvertibleType::s_value = testQPointF;
|
CustomConvertibleType::s_value = testQPointF;
|
||||||
QCOMPARE(v.toPointF(), ok ? testQPointF : QPointF());
|
QCOMPARE(v.toPointF(), ok ? testQPointF : QPointF());
|
||||||
QCOMPARE(v.value<QPointF>(), 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);
|
QFETCH(QSize, testQSize);
|
||||||
CustomConvertibleType::s_value = testQSize;
|
CustomConvertibleType::s_value = testQSize;
|
||||||
@ -231,7 +258,7 @@ void tst_QMetaType::convertCustomType()
|
|||||||
CustomConvertibleType::s_value = testQSizeF;
|
CustomConvertibleType::s_value = testQSizeF;
|
||||||
QCOMPARE(v.toSizeF(), ok ? testQSizeF : QSizeF());
|
QCOMPARE(v.toSizeF(), ok ? testQSizeF : QSizeF());
|
||||||
QCOMPARE(v.value<QSizeF>(), 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);
|
QFETCH(QLine, testQLine);
|
||||||
CustomConvertibleType::s_value = testQLine;
|
CustomConvertibleType::s_value = testQLine;
|
||||||
@ -243,7 +270,7 @@ void tst_QMetaType::convertCustomType()
|
|||||||
CustomConvertibleType::s_value = testQLineF;
|
CustomConvertibleType::s_value = testQLineF;
|
||||||
QCOMPARE(v.toLineF(), ok ? testQLineF : QLineF());
|
QCOMPARE(v.toLineF(), ok ? testQLineF : QLineF());
|
||||||
QCOMPARE(v.value<QLineF>(), 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);
|
QFETCH(QChar, testQChar);
|
||||||
CustomConvertibleType::s_value = testQChar;
|
CustomConvertibleType::s_value = testQChar;
|
||||||
@ -255,6 +282,18 @@ void tst_QMetaType::convertCustomType()
|
|||||||
QVERIFY(v.canConvert(QMetaType(::qMetaTypeId<CustomConvertibleType2>())));
|
QVERIFY(v.canConvert(QMetaType(::qMetaTypeId<CustomConvertibleType2>())));
|
||||||
QCOMPARE(v.value<CustomConvertibleType2>().m_foo, testCustom.m_foo);
|
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);
|
QFETCH(DerivedGadgetType, testDerived);
|
||||||
v = QVariant::fromValue(testDerived);
|
v = QVariant::fromValue(testDerived);
|
||||||
QCOMPARE(v.metaType(), QMetaType::fromType<DerivedGadgetType>());
|
QCOMPARE(v.metaType(), QMetaType::fromType<DerivedGadgetType>());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user