Implement QMetaType::canConvert() and use it in QVariant

Use the fact that we return the conversion function as a lambda
to find out reliably whether a conversion between two types
can be done.

This requires some minor adjustments to our tests:

* Nothing can convert to an unknown type and vice versa
* Adjust results to the fact that we don't convert from char
  to QString anymore (where the old method was incorrect)
* QStringList->QString requires some adjustments, as we only
  convert if the string list has exactly one element. For now
  we return true in canConvert(), but the conversion behavior
  in this case is something we should rethink, as it is very
  surprising.

Change-Id: I3f5f87ee9cb99d690f5a7d13b13d6a6313d8038e
Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io>
bb10
Lars Knoll 2020-07-14 12:26:16 +02:00
parent 2d3b31171d
commit 9d36032370
7 changed files with 159 additions and 402 deletions

View File

@ -1829,6 +1829,18 @@ static bool convertToEnum(const void *from, int fromTypeId, void *to, const QMet
}
}
#ifndef QT_BOOTSTRAPPED
static bool canConvertMetaObject(const QMetaType &fromType, const QMetaType &toType)
{
if ((fromType.flags() & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) {
return fromType.metaObject()->inherits(toType.metaObject()) ||
toType.metaObject()->inherits(fromType.metaObject());
}
return false;
}
#endif
/*!
Converts the object at \a from from \a fromTypeId to the preallocated space at \a to
typed \a toTypeId. Returns \c true, if the conversion succeeded, otherwise false.
@ -1836,6 +1848,16 @@ static bool convertToEnum(const void *from, int fromTypeId, void *to, const QMet
*/
bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId)
{
if (fromTypeId == UnknownType || toTypeId == UnknownType)
return false;
if (fromTypeId == toTypeId) {
// just make a copy
QMetaType(fromTypeId).destruct(to);
QMetaType(fromTypeId).construct(to, from);
return true;
}
if (auto moduleHelper = qModuleHelperForType(qMax(fromTypeId, toTypeId))) {
if (moduleHelper->convert(from, fromTypeId, to, toTypeId))
return true;
@ -1861,6 +1883,126 @@ bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId
return false;
}
/*!
Returns \c true if QMetaType::convert can convert from \a fromType to
\a toType.
The following conversions are supported by Qt:
\table
\header \li Type \li Automatically Cast To
\row \li \l QMetaType::Bool \li \l QMetaType::QChar, \l QMetaType::Double,
\l QMetaType::Int, \l QMetaType::LongLong, \l QMetaType::QString,
\l QMetaType::UInt, \l QMetaType::ULongLong
\row \li \l QMetaType::QByteArray \li \l QMetaType::Double,
\l QMetaType::Int, \l QMetaType::LongLong, \l QMetaType::QString,
\l QMetaType::UInt, \l QMetaType::ULongLong, \l QMetaType::QUuid
\row \li \l QMetaType::QChar \li \l QMetaType::Bool, \l QMetaType::Int,
\l QMetaType::UInt, \l QMetaType::LongLong, \l QMetaType::ULongLong
\row \li \l QMetaType::QColor \li \l QMetaType::QString
\row \li \l QMetaType::QDate \li \l QMetaType::QDateTime,
\l QMetaType::QString
\row \li \l QMetaType::QDateTime \li \l QMetaType::QDate,
\l QMetaType::QString, \l QMetaType::QTime
\row \li \l QMetaType::Double \li \l QMetaType::Bool, \l QMetaType::Int,
\l QMetaType::LongLong, \l QMetaType::QString, \l QMetaType::UInt,
\l QMetaType::ULongLong
\row \li \l QMetaType::QFont \li \l QMetaType::QString
\row \li \l QMetaType::Int \li \l QMetaType::Bool, \l QMetaType::QChar,
\l QMetaType::Double, \l QMetaType::LongLong, \l QMetaType::QString,
\l QMetaType::UInt, \l QMetaType::ULongLong
\row \li \l QMetaType::QKeySequence \li \l QMetaType::Int,
\l QMetaType::QString
\row \li \l QMetaType::QVariantList \li \l QMetaType::QStringList (if the
list's items can be converted to QStrings)
\row \li \l QMetaType::LongLong \li \l QMetaType::Bool,
\l QMetaType::QByteArray, \l QMetaType::QChar, \l QMetaType::Double,
\l QMetaType::Int, \l QMetaType::QString, \l QMetaType::UInt,
\l QMetaType::ULongLong
\row \li \l QMetaType::QPoint \li QMetaType::QPointF
\row \li \l QMetaType::QRect \li QMetaType::QRectF
\row \li \l QMetaType::QString \li \l QMetaType::Bool,
\l QMetaType::QByteArray, \l QMetaType::QChar, \l QMetaType::QColor,
\l QMetaType::QDate, \l QMetaType::QDateTime, \l QMetaType::Double,
\l QMetaType::QFont, \l QMetaType::Int, \l QMetaType::QKeySequence,
\l QMetaType::LongLong, \l QMetaType::QStringList, \l QMetaType::QTime,
\l QMetaType::UInt, \l QMetaType::ULongLong, \l QMetaType::QUuid
\row \li \l QMetaType::QStringList \li \l QMetaType::QVariantList,
\l QMetaType::QString (if the list contains exactly one item)
\row \li \l QMetaType::QTime \li \l QMetaType::QString
\row \li \l QMetaType::UInt \li \l QMetaType::Bool, \l QMetaType::QChar,
\l QMetaType::Double, \l QMetaType::Int, \l QMetaType::LongLong,
\l QMetaType::QString, \l QMetaType::ULongLong
\row \li \l QMetaType::ULongLong \li \l QMetaType::Bool,
\l QMetaType::QChar, \l QMetaType::Double, \l QMetaType::Int,
\l QMetaType::LongLong, \l QMetaType::QString, \l QMetaType::UInt
\row \li \l QMetaType::QUuid \li \l QMetaType::QByteArray, \l QMetaType::QString
\endtable
Casting between primitive type (int, float, bool etc.) is supported.
Converting between pointers of types derived from QObject will also return true for this
function if a qobject_cast from the type described by \a fromType to the type described
by \a toType would succeed.
A cast from a sequential container will also return true for this
function if the \a toType is QVariantList.
Similarly, a cast from an associative container will also return true for this
function the \a toType is QVariantHash or QVariantMap.
\sa convert(), QSequentialIterable, Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(), QAssociativeIterable,
Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE()
*/
bool QMetaType::canConvert(const QMetaType &fromType, const QMetaType &toType)
{
int fromTypeId = fromType.id();
int toTypeId = toType.id();
if (fromTypeId == UnknownType || toTypeId == UnknownType)
return false;
if (fromTypeId == toTypeId)
return true;
if (auto moduleHelper = qModuleHelperForType(qMax(fromTypeId, toTypeId))) {
if (moduleHelper->convert(nullptr, fromTypeId, nullptr, toTypeId))
return true;
}
const QMetaType::ConverterFunction * const f =
customTypesConversionRegistry()->function(qMakePair(fromTypeId, toTypeId));
if (f)
return true;
if (fromType.flags() & QMetaType::IsEnumeration) {
if (toTypeId == QMetaType::QString || toTypeId == QMetaType::QByteArray)
return true;
return QMetaType::canConvert(QMetaType(LongLong), toType);
}
if (toType.flags() & QMetaType::IsEnumeration) {
if (fromTypeId == QMetaType::QString || fromTypeId == QMetaType::QByteArray)
return true;
return QMetaType::canConvert(fromType, QMetaType(LongLong));
}
if (toTypeId == Nullptr && fromType.flags() & QMetaType::IsPointer)
return true;
#ifndef QT_BOOTSTRAPPED
if (canConvertMetaObject(fromType, toType))
return true;
#endif
return false;
}
/*!
bool QMetaType::compare(const void *lhs, const void *rhs, int typeId, int* result)
\deprecated Use the non-static compare method instead
Compares the objects at \a lhs and \a rhs. Both objects need to be of type \a typeId.
\a result is set to less than, equal to or greater than zero, if \a lhs is less than, equal to
or greater than \a rhs. Returns \c true, if the comparison succeeded, otherwise \c false.
*/
/*!
\fn bool QMetaType::hasRegisteredConverterFunction()
Returns \c true, if the meta type system has a registered conversion from type From to type To.

View File

@ -524,6 +524,7 @@ public:
#endif
static bool convert(const void *from, int fromTypeId, void *to, int toTypeId);
static bool canConvert(const QMetaType &fromType, const QMetaType &toType);
#if QT_DEPRECATED_SINCE(6, 0)
QT_DEPRECATED_VERSION_6_0
static bool compare(const void *lhs, const void *rhs, int typeId, int *result)

View File

@ -1962,220 +1962,18 @@ QVariantList QVariant::toList() const
return qVariantToHelper<QVariantList>(d);
}
static const quint32 qCanConvertMatrix[QMetaType::LastCoreType + 1] =
{
/*Invalid*/ 0,
/*Bool*/ 1 << QMetaType::Double | 1 << QMetaType::Int | 1 << QMetaType::UInt
| 1 << QMetaType::LongLong | 1 << QMetaType::ULongLong | 1 << QMetaType::QByteArray
| 1 << QMetaType::QString | 1 << QMetaType::QChar,
/*Int*/ 1 << QMetaType::UInt | 1 << QMetaType::QString | 1 << QMetaType::Double
| 1 << QMetaType::Bool | 1 << QMetaType::LongLong | 1 << QMetaType::ULongLong
| 1 << QMetaType::QChar | 1 << QMetaType::QByteArray | 1 << QMetaType::Int,
/*UInt*/ 1 << QMetaType::Int | 1 << QMetaType::QString | 1 << QMetaType::Double
| 1 << QMetaType::Bool | 1 << QMetaType::LongLong | 1 << QMetaType::ULongLong
| 1 << QMetaType::QChar | 1 << QMetaType::QByteArray,
/*LLong*/ 1 << QMetaType::Int | 1 << QMetaType::QString | 1 << QMetaType::Double
| 1 << QMetaType::Bool | 1 << QMetaType::UInt | 1 << QMetaType::ULongLong
| 1 << QMetaType::QChar | 1 << QMetaType::QByteArray,
/*ULlong*/ 1 << QMetaType::Int | 1 << QMetaType::QString | 1 << QMetaType::Double
| 1 << QMetaType::Bool | 1 << QMetaType::UInt | 1 << QMetaType::LongLong
| 1 << QMetaType::QChar | 1 << QMetaType::QByteArray,
/*double*/ 1 << QMetaType::Int | 1 << QMetaType::QString | 1 << QMetaType::ULongLong
| 1 << QMetaType::Bool | 1 << QMetaType::UInt | 1 << QMetaType::LongLong
| 1 << QMetaType::QByteArray,
/*QChar*/ 1 << QMetaType::Int | 1 << QMetaType::UInt | 1 << QMetaType::LongLong
| 1 << QMetaType::ULongLong,
/*QMap*/ 0,
/*QList*/ 1 << QMetaType::QStringList,
/*QString*/ 1 << QMetaType::QStringList | 1 << QMetaType::QByteArray | 1 << QMetaType::Int
| 1 << QMetaType::UInt | 1 << QMetaType::Bool | 1 << QMetaType::Double
| 1 << QMetaType::QDate | 1 << QMetaType::QTime | 1 << QMetaType::QDateTime
| 1 << QMetaType::LongLong | 1 << QMetaType::ULongLong | 1 << QMetaType::QChar
| 1 << QMetaType::QUrl | 1 << QMetaType::QUuid,
/*QStringList*/ 1 << QMetaType::QVariantList | 1 << QMetaType::QString,
/*QByteArray*/ 1 << QMetaType::QString | 1 << QMetaType::Int | 1 << QMetaType::UInt | 1 << QMetaType::Bool
| 1 << QMetaType::Double | 1 << QMetaType::LongLong | 1 << QMetaType::ULongLong
| 1 << QMetaType::QUuid,
/*QBitArray*/ 0,
/*QDate*/ 1 << QMetaType::QString | 1 << QMetaType::QDateTime,
/*QTime*/ 1 << QMetaType::QString | 1 << QMetaType::QDateTime,
/*QDateTime*/ 1 << QMetaType::QString | 1 << QMetaType::QDate,
/*QUrl*/ 1 << QMetaType::QString,
/*QLocale*/ 0,
/*QRect*/ 1 << QMetaType::QRectF,
/*QRectF*/ 1 << QMetaType::QRect,
/*QSize*/ 1 << QMetaType::QSizeF,
/*QSizeF*/ 1 << QMetaType::QSize,
/*QLine*/ 1 << QMetaType::QLineF,
/*QLineF*/ 1 << QMetaType::QLine,
/*QPoint*/ 1 << QMetaType::QPointF,
/*QPointF*/ 1 << QMetaType::QPoint,
/*unused, was: QRegExp*/ 0,
/*QHash*/ 0,
/*QEasingCurve*/ 0,
/*QUuid*/ 1 << QMetaType::QString | 1 << QMetaType::QByteArray,
};
static const size_t qCanConvertMatrixMaximumTargetType = 8 * sizeof(*qCanConvertMatrix);
#ifndef QT_BOOTSTRAPPED
/*
Returns \c true if from inherits to.
*/
static bool canConvertMetaObject(const QMetaObject *from, const QMetaObject *to)
{
if (from && to == &QObject::staticMetaObject)
return true;
while (from) {
if (from == to)
return true;
from = from->superClass();
}
return false;
}
#endif
static bool canConvertMetaObject(int fromId, int toId, QObject *fromObject)
{
#ifndef QT_BOOTSTRAPPED
QMetaType toType(toId);
if ((QMetaType::typeFlags(fromId) & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) {
if (!fromObject)
return true;
return canConvertMetaObject(fromObject->metaObject(), toType.metaObject());
}
#else
Q_UNUSED(fromId);
Q_UNUSED(toId);
Q_UNUSED(fromObject);
#endif
return false;
}
/*!
Returns \c true if the variant's type can be cast to the requested
type, \a targetTypeId. Such casting is done automatically when calling the
toInt(), toBool(), ... methods.
The following casts are done automatically:
\table
\header \li Type \li Automatically Cast To
\row \li \l QMetaType::Bool \li \l QMetaType::QChar, \l QMetaType::Double,
\l QMetaType::Int, \l QMetaType::LongLong, \l QMetaType::QString,
\l QMetaType::UInt, \l QMetaType::ULongLong
\row \li \l QMetaType::QByteArray \li \l QMetaType::Double,
\l QMetaType::Int, \l QMetaType::LongLong, \l QMetaType::QString,
\l QMetaType::UInt, \l QMetaType::ULongLong, \l QMetaType::QUuid
\row \li \l QMetaType::QChar \li \l QMetaType::Bool, \l QMetaType::Int,
\l QMetaType::UInt, \l QMetaType::LongLong, \l QMetaType::ULongLong
\row \li \l QMetaType::QColor \li \l QMetaType::QString
\row \li \l QMetaType::QDate \li \l QMetaType::QDateTime,
\l QMetaType::QString
\row \li \l QMetaType::QDateTime \li \l QMetaType::QDate,
\l QMetaType::QString, \l QMetaType::QTime
\row \li \l QMetaType::Double \li \l QMetaType::Bool, \l QMetaType::Int,
\l QMetaType::LongLong, \l QMetaType::QString, \l QMetaType::UInt,
\l QMetaType::ULongLong
\row \li \l QMetaType::QFont \li \l QMetaType::QString
\row \li \l QMetaType::Int \li \l QMetaType::Bool, \l QMetaType::QChar,
\l QMetaType::Double, \l QMetaType::LongLong, \l QMetaType::QString,
\l QMetaType::UInt, \l QMetaType::ULongLong
\row \li \l QMetaType::QKeySequence \li \l QMetaType::Int,
\l QMetaType::QString
\row \li \l QMetaType::QVariantList \li \l QMetaType::QStringList (if the
list's items can be converted to QStrings)
\row \li \l QMetaType::LongLong \li \l QMetaType::Bool,
\l QMetaType::QByteArray, \l QMetaType::QChar, \l QMetaType::Double,
\l QMetaType::Int, \l QMetaType::QString, \l QMetaType::UInt,
\l QMetaType::ULongLong
\row \li \l QMetaType::QPoint \li QMetaType::QPointF
\row \li \l QMetaType::QRect \li QMetaType::QRectF
\row \li \l QMetaType::QString \li \l QMetaType::Bool,
\l QMetaType::QByteArray, \l QMetaType::QChar, \l QMetaType::QColor,
\l QMetaType::QDate, \l QMetaType::QDateTime, \l QMetaType::Double,
\l QMetaType::QFont, \l QMetaType::Int, \l QMetaType::QKeySequence,
\l QMetaType::LongLong, \l QMetaType::QStringList, \l QMetaType::QTime,
\l QMetaType::UInt, \l QMetaType::ULongLong, \l QMetaType::QUuid
\row \li \l QMetaType::QStringList \li \l QMetaType::QVariantList,
\l QMetaType::QString (if the list contains exactly one item)
\row \li \l QMetaType::QTime \li \l QMetaType::QString
\row \li \l QMetaType::UInt \li \l QMetaType::Bool, \l QMetaType::QChar,
\l QMetaType::Double, \l QMetaType::Int, \l QMetaType::LongLong,
\l QMetaType::QString, \l QMetaType::ULongLong
\row \li \l QMetaType::ULongLong \li \l QMetaType::Bool,
\l QMetaType::QChar, \l QMetaType::Double, \l QMetaType::Int,
\l QMetaType::LongLong, \l QMetaType::QString, \l QMetaType::UInt
\row \li \l QMetaType::QUuid \li \l QMetaType::QByteArray, \l QMetaType::QString
\endtable
A QVariant containing a pointer to a type derived from QObject will also 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.
A QVariant containing a sequential container will also return true for this
function if the \a targetTypeId is QVariantList. It is possible to iterate over
the contents of the container without extracting it as a (copied) QVariantList:
\snippet code/src_corelib_kernel_qvariant.cpp 9
This requires that the value_type of the container is itself a metatype.
Similarly, a QVariant containing a sequential container will also return true for this
function the \a targetTypeId is QVariantHash or QVariantMap. It is possible to iterate over
the contents of the container without extracting it as a (copied) QVariantHash or QVariantMap:
\snippet code/src_corelib_kernel_qvariant.cpp 10
\sa convert(), QSequentialIterable, Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(), QAssociativeIterable,
Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE()
\sa QMetaType::canConvert()
*/
bool QVariant::canConvert(int targetTypeId) const
{
if (d.typeId() == targetTypeId)
if (d.typeId() == targetTypeId && targetTypeId != QMetaType::UnknownType)
return true;
#if QT_CONFIG(itemmodel)
if ((targetTypeId == QMetaType::QModelIndex
&& d.typeId() == QMetaType::QPersistentModelIndex)
|| (targetTypeId == QMetaType::QPersistentModelIndex
&& d.typeId() == QMetaType::QModelIndex))
return true;
#endif
if (targetTypeId == QMetaType::QVariantList
&& (d.typeId() == QMetaType::QVariantList || d.typeId() == QMetaType::QStringList
|| d.typeId() == QMetaType::QByteArrayList
@ -2197,193 +1995,7 @@ bool QVariant::canConvert(int targetTypeId) const
return true;
}
if ((d.typeId() >= QMetaType::LastCoreType|| targetTypeId >= QMetaType::LastCoreType)
&& QMetaType::hasRegisteredConverterFunction(d.typeId(), targetTypeId)) {
return true;
}
// TODO Reimplement this function, currently it works but it is a historical mess.
uint currentType = d.typeId();
if (currentType == QMetaType::SChar || currentType == QMetaType::Char)
currentType = QMetaType::UInt;
if (targetTypeId == QMetaType::SChar || currentType == QMetaType::Char)
targetTypeId = QMetaType::UInt;
if (currentType == QMetaType::Short || currentType == QMetaType::UShort)
currentType = QMetaType::Int;
if (targetTypeId == QMetaType::Short || currentType == QMetaType::UShort)
targetTypeId = QMetaType::Int;
if (currentType == QMetaType::Float)
currentType = QMetaType::Double;
if (targetTypeId == QMetaType::Float)
targetTypeId = QMetaType::Double;
if (currentType == uint(targetTypeId))
return true;
if (targetTypeId < 0)
return false;
if (targetTypeId >= QMetaType::User) {
if (QMetaType::typeFlags(targetTypeId) & QMetaType::IsEnumeration) {
targetTypeId = QMetaType::Int;
} else {
return canConvertMetaObject(currentType, targetTypeId, d.get<QObject *>());
}
}
if (currentType == QMetaType::QJsonValue || targetTypeId == QMetaType::QJsonValue) {
switch (currentType == QMetaType::QJsonValue ? targetTypeId : currentType) {
case QMetaType::Nullptr:
case QMetaType::QString:
case QMetaType::Bool:
case QMetaType::Int:
case QMetaType::UInt:
case QMetaType::Double:
case QMetaType::Float:
case QMetaType::ULong:
case QMetaType::Long:
case QMetaType::LongLong:
case QMetaType::ULongLong:
case QMetaType::UShort:
case QMetaType::UChar:
case QMetaType::Char:
case QMetaType::SChar:
case QMetaType::Short:
case QMetaType::QVariantList:
case QMetaType::QVariantMap:
case QMetaType::QVariantHash:
case QMetaType::QCborValue:
case QMetaType::QCborArray:
case QMetaType::QCborMap:
return true;
default:
return false;
}
}
if (currentType == QMetaType::QJsonArray)
return targetTypeId == QMetaType::QVariantList || targetTypeId == QMetaType::QCborValue
|| targetTypeId == QMetaType::QCborArray;
if (currentType == QMetaType::QJsonObject)
return targetTypeId == QMetaType::QVariantMap || targetTypeId == QMetaType::QVariantHash
|| targetTypeId == QMetaType::QCborValue || targetTypeId == QMetaType::QCborMap;
if (currentType == QMetaType::QCborValue || targetTypeId == QMetaType::QCborValue) {
switch (currentType == QMetaType::QCborValue ? targetTypeId : currentType) {
case QMetaType::UnknownType:
case QMetaType::Nullptr:
case QMetaType::Bool:
case QMetaType::Int:
case QMetaType::UInt:
case QMetaType::Double:
case QMetaType::Float:
case QMetaType::ULong:
case QMetaType::Long:
case QMetaType::LongLong:
case QMetaType::ULongLong:
case QMetaType::UShort:
case QMetaType::UChar:
case QMetaType::Char:
case QMetaType::SChar:
case QMetaType::Short:
case QMetaType::QString:
case QMetaType::QByteArray:
case QMetaType::QDateTime:
case QMetaType::QUrl:
#if QT_CONFIG(regularexpression)
case QMetaType::QRegularExpression:
#endif
case QMetaType::QUuid:
case QMetaType::QVariantList:
case QMetaType::QVariantMap:
case QMetaType::QVariantHash:
case QMetaType::QJsonValue:
case QMetaType::QJsonArray:
case QMetaType::QJsonObject:
case QMetaType::QJsonDocument:
case QMetaType::QCborArray:
case QMetaType::QCborMap:
case QMetaType::QCborSimpleType:
return true;
default:
return false;
}
}
if (currentType == QMetaType::QCborArray)
return targetTypeId == QMetaType::QVariantList || targetTypeId == QMetaType::QCborValue
|| targetTypeId == QMetaType::QJsonArray;
if (currentType == QMetaType::QCborMap)
return targetTypeId == QMetaType::QVariantMap || targetTypeId == QMetaType::QVariantHash
|| targetTypeId == QMetaType::QCborValue || targetTypeId == QMetaType::QJsonObject;
// FIXME It should be LastCoreType intead of Uuid
if (currentType > int(QMetaType::QUuid) || targetTypeId > int(QMetaType::QUuid)) {
switch (uint(targetTypeId)) {
case QVariant::Int:
#if QT_CONFIG(shortcut)
if (currentType == QVariant::KeySequence)
return true;
Q_FALLTHROUGH();
#endif
case QVariant::UInt:
case QVariant::LongLong:
case QVariant::ULongLong:
return currentType == QMetaType::ULong
|| currentType == QMetaType::Long
|| currentType == QMetaType::UShort
|| currentType == QMetaType::UChar
|| currentType == QMetaType::Char
|| currentType == QMetaType::SChar
|| currentType == QMetaType::Short
|| QMetaType::typeFlags(currentType) & QMetaType::IsEnumeration;
case QVariant::Image:
return currentType == QVariant::Pixmap || currentType == QVariant::Bitmap;
case QVariant::Pixmap:
return currentType == QVariant::Image || currentType == QVariant::Bitmap
|| currentType == QVariant::Brush;
case QVariant::Bitmap:
return currentType == QVariant::Pixmap || currentType == QVariant::Image;
case QVariant::ByteArray:
return currentType == QVariant::Color || currentType == QMetaType::Nullptr
|| ((QMetaType::typeFlags(currentType) & QMetaType::IsEnumeration) && QMetaType::metaObjectForType(currentType));
case QVariant::String:
return currentType == QVariant::Font
|| currentType == QVariant::Color || currentType == QMetaType::Nullptr
#if QT_CONFIG(shortcut)
|| currentType == QVariant::KeySequence
#endif
|| ((QMetaType::typeFlags(currentType) & QMetaType::IsEnumeration) && QMetaType::metaObjectForType(currentType));
#if QT_CONFIG(shortcut)
case QVariant::KeySequence:
return currentType == QVariant::String || currentType == QVariant::Int;
#endif
case QVariant::Font:
return currentType == QVariant::String;
case QVariant::Color:
return currentType == QVariant::String || currentType == QVariant::ByteArray
|| currentType == QVariant::Brush;
case QVariant::Brush:
return currentType == QVariant::Color || currentType == QVariant::Pixmap;
case QMetaType::Long:
case QMetaType::Char:
case QMetaType::SChar:
case QMetaType::UChar:
case QMetaType::ULong:
case QMetaType::Short:
case QMetaType::UShort:
return currentType == QVariant::Int
|| (currentType < qCanConvertMatrixMaximumTargetType
&& qCanConvertMatrix[QVariant::Int] & (1U << currentType))
|| QMetaType::typeFlags(currentType) & QMetaType::IsEnumeration;
case QMetaType::QObjectStar:
return canConvertMetaObject(currentType, targetTypeId, d.get<QObject *>());
default:
return false;
}
}
if (targetTypeId == String && currentType == StringList)
return d.get<QStringList>().count() == 1;
return currentType < qCanConvertMatrixMaximumTargetType
&& qCanConvertMatrix[targetTypeId] & (1U << currentType);
return QMetaType::canConvert(d.type(), QMetaType(targetTypeId));
}
/*!
@ -2409,7 +2021,7 @@ bool QVariant::canConvert(int targetTypeId) const
bool QVariant::convert(int targetTypeId)
{
if (d.typeId() == targetTypeId)
return true;
return (targetTypeId != QMetaType::UnknownType);
QVariant oldValue = *this;

View File

@ -929,7 +929,8 @@ void tst_QSettings::testIniParsing()
if ( settings.status() == QSettings::NoError ) { // else no point proceeding
QVariant v = settings.value(key);
QVERIFY(v.canConvert(expect.type()));
if (expect.isValid())
QVERIFY(v.canConvert(expect.type()));
// check some types so as to give prettier error messages
if ( v.type() == QVariant::String ) {
QCOMPARE(v.toString(), expect.toString());

View File

@ -13,6 +13,7 @@ qt_add_test(tst_qvariant
../../../other/qvariant_common
PUBLIC_LIBRARIES
Qt::CorePrivate
Qt::Gui
)
# Resources:

View File

@ -1,6 +1,6 @@
CONFIG += testcase
TARGET = tst_qvariant
QT = core-private testlib
QT = core-private gui testlib
INCLUDEPATH += $$PWD/../../../other/qvariant_common
SOURCES = tst_qvariant.cpp
RESOURCES += qvariant.qrc

View File

@ -477,7 +477,7 @@ void tst_QVariant::canConvert_data()
<< var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y;
var = QVariant();
QTest::newRow("Invalid")
<< var << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N;
<< var << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N;
var = QVariant(QList<QVariant>());
QTest::newRow("List")
<< var << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N;
@ -520,12 +520,12 @@ void tst_QVariant::canConvert_data()
var = QVariant::fromValue<signed char>(-1);
QTest::newRow("SChar")
<< var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y;
var = QVariant((short)-3);
var = QVariant::fromValue((short)-3);
QTest::newRow("Short")
<< var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y;
var = QVariant((ushort)7);
<< var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y;
var = QVariant::fromValue((ushort)7);
QTest::newRow("UShort")
<< var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y;
<< var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y;
var = QVariant::fromValue<QJsonValue>(QJsonValue(QStringLiteral("hello")));
QTest::newRow("JsonValue")
<< var << N << N << Y << N << N << N << N << N << N << Y << N << N << Y << N << N << Y << Y << Y << N << N << N << N << N << N << N << N << Y << N << N << Y << Y;
@ -2705,17 +2705,17 @@ void tst_QVariant::canConvertQStringList_data() const
QTest::addColumn<QStringList>("input");
QTest::addColumn<QString>("result");
QTest::newRow("An empty list") << false << QStringList() << QString();
QTest::newRow("An empty list") << true << QStringList() << QString();
QTest::newRow("A single item") << true << QStringList(QLatin1String("foo")) << QString::fromLatin1("foo");
QTest::newRow("A single, but empty item") << true << QStringList(QString()) << QString();
QStringList l;
l << "a" << "b";
QTest::newRow("Two items") << false << l << QString();
QTest::newRow("Two items") << true << l << QString();
l << "c";
QTest::newRow("Three items") << false << l << QString();
QTest::newRow("Three items") << true << l << QString();
}
template<typename T> void convertMetaType()