Allow conversion of QVariants with real null values

Update documentation to be clearer on the special null variant state
with no value as opposed to a variant with a null value, and only block
conversions of the former.

Change-Id: I24fd50285414e049de87de54a63700a89bd5adf1
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Allan Sandfeld Jensen 2017-07-18 11:18:35 +02:00
parent bf6ae8406b
commit bef7e4a222
2 changed files with 51 additions and 13 deletions

View File

@ -1459,7 +1459,11 @@ Q_CORE_EXPORT void QVariantPrivate::registerHandler(const int /* Modules::Names
/*!
\fn QVariant::QVariant(Type type)
Constructs a null variant of type \a type.
Constructs an uninitialized variant of type \a type. This will create a
variant in a special null state that if accessed will return a default
constructed value of the \a type.
\sa isNull()
*/
@ -3305,17 +3309,20 @@ bool QVariant::canConvert(int targetTypeId) const
/*!
Casts the variant to the requested type, \a targetTypeId. If the cast cannot be
done, the variant is cleared. Returns \c true if the current type of
the variant was successfully cast; otherwise returns \c false.
done, the variant is still changed to the requested type, but is left in a cleared
null state similar to that constructed by QVariant(Type).
Returns \c true if the current type of the variant was successfully cast;
otherwise returns \c false.
A QVariant containing a pointer to a type derived from QObject will also convert
and return true for this function if a qobject_cast to the type described
by \a targetTypeId would succeed. Note that this only works for QObject subclasses
which use the Q_OBJECT macro.
\warning For historical reasons, converting a null QVariant results
in a null value of the desired type (e.g., an empty string for
QString) and a result of false.
\note converting QVariants that are null due to not being initialized or having
failed a previous conversion will always fail, changing the type, remaining null,
and returning \c false.
\sa canConvert(), clear()
*/
@ -3332,7 +3339,8 @@ bool QVariant::convert(int targetTypeId)
return false;
create(targetTypeId, 0);
if (oldValue.isNull())
// Fail if the value is not initialized or was forced null by a previous failed convert.
if (oldValue.d.is_null)
return false;
if ((QMetaType::typeFlags(oldValue.userType()) & QMetaType::PointerToQObject) && (QMetaType::typeFlags(targetTypeId) & QMetaType::PointerToQObject)) {
@ -3732,12 +3740,14 @@ void* QVariant::data()
/*!
Returns \c true if this is a null variant, false otherwise. A variant is
considered null if it contains a default constructed value or a built-in
type instance that has an isNull method, in which case the result
would be the same as calling isNull on the wrapped object.
considered null if it contains no initialized value or it contains an instance
of built-in type that has an isNull method, in which case the result would be
the same as calling isNull on the wrapped object.
\warning The result of the function doesn't affect == operator, which means
that two values can be equal even if one of them is null and another is not.
\warning Null variants is not a single state and two null variants may easily
return \c false on the == operator if they do not contain similar null values.
\sa QVariant(Type), convert(int)
*/
bool QVariant::isNull() const
{

View File

@ -277,6 +277,8 @@ private slots:
void compareSanity();
void compareRich();
void nullConvert();
void accessSequentialContainerKey();
private:
@ -4861,6 +4863,33 @@ void tst_QVariant::compareRich()
<< QStringLiteral("d"));
}
void tst_QVariant::nullConvert()
{
// Test quirks with QVariants different types of null states.
// null variant with no initialized value
QVariant nullVar(QVariant::String);
QVERIFY(nullVar.isValid());
QVERIFY(nullVar.isNull());
// We can not convert a variant with no value
QVERIFY(!nullVar.convert(QVariant::Url));
QCOMPARE(nullVar.type(), QVariant::Url);
QVERIFY(nullVar.isNull());
// variant initialized with null value
QVariant nullStr = QVariant::fromValue(QString());
QVERIFY(nullStr.isValid());
QVERIFY(nullStr.isNull());
// We can convert an initialized null value however
QVERIFY(nullStr.convert(QVariant::Url));
QCOMPARE(nullStr.type(), QVariant::Url);
QVERIFY(nullStr.isValid());
// QUrl does not have an isNull method
QVERIFY(!nullStr.isNull());
// The URL is not valid however
QVERIFY(!nullStr.toUrl().isValid());
}
void tst_QVariant::accessSequentialContainerKey()
{
QString nameResult;
@ -4885,6 +4914,5 @@ void tst_QVariant::accessSequentialContainerKey()
QCOMPARE(nameResult, QStringLiteral("Seven"));
}
QTEST_MAIN(tst_QVariant)
#include "tst_qvariant.moc"