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)
{
*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
\a copy if \a copy is not \nullptr.
Constructs 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 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()
instead to construct variants from the pointer types represented by
\c QMetaType::VoidStar, and \c QMetaType::QObjectStar.
If \a type does not support copy and default construction, the variant will
be invalid.
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.
\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();
if (isValidMetaTypeForVariant(type.iface(), copy))
customConstruct(type.iface(), &d, copy);
else
d = {};
*this = fromMetaType(type, copy);
}
QVariant::QVariant(int val) noexcept : d(std::piecewise_construct_t{}, val) {}
@ -2711,6 +2711,41 @@ QT_WARNING_POP
\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)
\relates QVariant

View File

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

View File

@ -442,11 +442,21 @@ void tst_QVariant::constructor()
QVERIFY(var3.isNull());
QVERIFY(var3.isValid());
QVariant var3a = QVariant::fromMetaType(QMetaType::fromType<QString>());
QCOMPARE(var3a.typeName(), "QString");
QVERIFY(var3a.isNull());
QVERIFY(var3a.isValid());
QVariant var4 {QMetaType()};
QCOMPARE(var4.typeId(), QMetaType::UnknownType);
QVERIFY(var4.isNull());
QVERIFY(!var4.isValid());
QVariant var4a = QVariant::fromMetaType(QMetaType());
QCOMPARE(var4a.typeId(), QMetaType::UnknownType);
QVERIFY(var4a.isNull());
QVERIFY(!var4a.isValid());
QVariant var5(QLatin1String("hallo"));
QCOMPARE(var5.typeId(), QMetaType::QString);
QCOMPARE(var5.typeName(), "QString");
@ -487,6 +497,14 @@ void tst_QVariant::constructor_invalid()
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"));
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"));
QVariant variant(QMetaType(typeId), /* copy */ nullptr);