QVariant: add fromMetaType

The QVariant(QMetaType) constructor is a major anti-pattern: unlike
*every* other QVariant's constructor, it doesn't build a QVariant
holding the QMetaType object, but a QVariant of the specified type.

Introduce a named constructor for this use case instead.

In principle, this should lead to a deprecation of the QMetaType
constructor... except that it's used everywhere, so I can't do it at
this time.

Drive-by, improve the documentation of the QVariant(QMetaType)
constructor (since it's basically c&p for the new fromMetaType
function).

[ChangeLog][QtCore][QVariant] Added the QVariant::fromMetaType named
constructor, that builds a QVariant of a given QMetaType.

Change-Id: I4a499526bd0fe98eed0c1a3e91bcfc21efa9e352
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Giuseppe D'Angelo 2023-09-13 10:27:04 +02:00
parent 41824a02cb
commit 4a6cbfbe5c
3 changed files with 69 additions and 14 deletions

View File

@ -516,7 +516,7 @@ void QVariant::create(int type, const void *copy)
*/ */
void QVariant::create(QMetaType type, const void *copy) void QVariant::create(QMetaType type, const void *copy)
{ {
*this = QVariant(type, copy); *this = QVariant::fromMetaType(type, copy);
} }
/*! /*!
@ -910,27 +910,27 @@ void *QVariant::prepareForEmplace(QMetaType type)
*/ */
/*! /*!
Constructs variant of type \a type, and initializes with Constructs a variant of type \a type, and initializes it with
\a copy if \a copy is not \nullptr. a copy of \c{*copy} if \a copy is not \nullptr (in which case, \a copy
must point to an object of type \a type).
Note that you have to pass the address of the variable you want stored. Note that you have to pass the address of the object you want stored.
Usually, you never have to use this constructor, use QVariant::fromValue() Usually, you never have to use this constructor, use QVariant::fromValue()
instead to construct variants from the pointer types represented by instead to construct variants from the pointer types represented by
\c QMetaType::VoidStar, and \c QMetaType::QObjectStar. \c QMetaType::VoidStar, and \c QMetaType::QObjectStar.
If \a type does not support copy and default construction, the variant will If \a type does not support copy construction and \a copy is not \nullptr,
be invalid. the variant will be invalid. Similarly, if \a copy is \nullptr and
\a type does not support default construction, the variant will be
invalid.
\sa QVariant::fromValue(), QMetaType::Type \sa QVariant::fromMetaType, QVariant::fromValue(), QMetaType::Type
*/ */
QVariant::QVariant(QMetaType type, const void *copy) : d(type.iface()) QVariant::QVariant(QMetaType type, const void *copy)
: d()
{ {
type.registerType(); *this = fromMetaType(type, copy);
if (isValidMetaTypeForVariant(type.iface(), copy))
customConstruct(type.iface(), &d, copy);
else
d = {};
} }
QVariant::QVariant(int val) noexcept : d(std::piecewise_construct_t{}, val) {} QVariant::QVariant(int val) noexcept : d(std::piecewise_construct_t{}, val) {}
@ -2711,6 +2711,41 @@ QT_WARNING_POP
\overload \overload
*/ */
/*!
\since 6.7
Creates a variant of type \a type, and initializes it with
a copy of \c{*copy} if \a copy is not \nullptr (in which case, \a copy
must point to an object of type \a type).
Note that you have to pass the address of the object you want stored.
Usually, you never have to use this constructor, use QVariant::fromValue()
instead to construct variants from the pointer types represented by
\c QMetaType::VoidStar, and \c QMetaType::QObjectStar.
If \a type does not support copy construction and \a copy is not \nullptr,
the variant will be invalid. Similarly, if \a copy is \nullptr and
\a type does not support default construction, the variant will be
invalid.
Returns the QVariant created as described above.
\sa QVariant::fromValue(), QMetaType::Type
*/
QVariant QVariant::fromMetaType(QMetaType type, const void *copy)
{
QVariant result;
type.registerType();
const auto iface = type.iface();
if (isValidMetaTypeForVariant(iface, copy)) {
result.d = Private(iface);
customConstruct(iface, &result.d, copy);
}
return result;
}
/*! /*!
\fn template<typename T> T qvariant_cast(const QVariant &value) \fn template<typename T> T qvariant_cast(const QVariant &value)
\relates QVariant \relates QVariant

View File

@ -539,7 +539,7 @@ public:
// handle special cases // handle special cases
using Type = std::remove_cv_t<T>; using Type = std::remove_cv_t<T>;
if constexpr (std::is_null_pointer_v<Type>) if constexpr (std::is_null_pointer_v<Type>)
return QVariant(QMetaType::fromType<std::nullptr_t>()); return QVariant::fromMetaType(QMetaType::fromType<std::nullptr_t>());
else if constexpr (std::is_same_v<Type, QVariant>) else if constexpr (std::is_same_v<Type, QVariant>)
return std::forward<T>(value); return std::forward<T>(value);
else if constexpr (std::is_same_v<Type, std::monostate>) else if constexpr (std::is_same_v<Type, std::monostate>)
@ -586,6 +586,8 @@ public:
return fromStdVariantImpl(std::move(value)); return fromStdVariantImpl(std::move(value));
} }
static QVariant fromMetaType(QMetaType type, const void *copy = nullptr);
template<typename T> template<typename T>
bool canConvert() const bool canConvert() const
{ return canConvert(QMetaType::fromType<T>()); } { return canConvert(QMetaType::fromType<T>()); }

View File

@ -442,11 +442,21 @@ void tst_QVariant::constructor()
QVERIFY(var3.isNull()); QVERIFY(var3.isNull());
QVERIFY(var3.isValid()); QVERIFY(var3.isValid());
QVariant var3a = QVariant::fromMetaType(QMetaType::fromType<QString>());
QCOMPARE(var3a.typeName(), "QString");
QVERIFY(var3a.isNull());
QVERIFY(var3a.isValid());
QVariant var4 {QMetaType()}; QVariant var4 {QMetaType()};
QCOMPARE(var4.typeId(), QMetaType::UnknownType); QCOMPARE(var4.typeId(), QMetaType::UnknownType);
QVERIFY(var4.isNull()); QVERIFY(var4.isNull());
QVERIFY(!var4.isValid()); QVERIFY(!var4.isValid());
QVariant var4a = QVariant::fromMetaType(QMetaType());
QCOMPARE(var4a.typeId(), QMetaType::UnknownType);
QVERIFY(var4a.isNull());
QVERIFY(!var4a.isValid());
QVariant var5(QLatin1String("hallo")); QVariant var5(QLatin1String("hallo"));
QCOMPARE(var5.typeId(), QMetaType::QString); QCOMPARE(var5.typeId(), QMetaType::QString);
QCOMPARE(var5.typeName(), "QString"); QCOMPARE(var5.typeName(), "QString");
@ -487,6 +497,14 @@ void tst_QVariant::constructor_invalid()
QCOMPARE(variant.typeId(), int(QMetaType::UnknownType)); QCOMPARE(variant.typeId(), int(QMetaType::UnknownType));
QCOMPARE(variant.userType(), int(QMetaType::UnknownType)); QCOMPARE(variant.userType(), int(QMetaType::UnknownType));
} }
{
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type"));
QVariant variant = QVariant::fromMetaType(QMetaType(typeId));
QVERIFY(!variant.isValid());
QVERIFY(variant.isNull());
QCOMPARE(variant.typeId(), int(QMetaType::UnknownType));
QCOMPARE(variant.userType(), int(QMetaType::UnknownType));
}
{ {
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type")); QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type"));
QVariant variant(QMetaType(typeId), /* copy */ nullptr); QVariant variant(QMetaType(typeId), /* copy */ nullptr);