summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qvariant.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qvariant.cpp')
-rw-r--r--src/corelib/kernel/qvariant.cpp447
1 files changed, 386 insertions, 61 deletions
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index 3db19f1ee5..f7a4abbf68 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
-** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
-** Contact: http://www.qt-project.org/legal
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
+** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
@@ -11,9 +11,9 @@
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
@@ -24,8 +24,8 @@
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
@@ -57,6 +57,7 @@
#endif
#include "private/qvariant_p.h"
#include "qmetatype_p.h"
+#include <qmetaobject.h>
#ifndef QT_NO_GEOM_VARIANT
#include "qsize.h"
@@ -66,16 +67,22 @@
#endif
#include <float.h>
+#include <cstring>
QT_BEGIN_NAMESPACE
-#ifndef DBL_DIG
-# define DBL_DIG 10
+#ifndef DBL_MANT_DIG
+# define DBL_MANT_DIG 53
#endif
-#ifndef FLT_DIG
-# define FLT_DIG 6
+#ifndef FLT_MANT_DIG
+# define FLT_MANT_DIG 24
#endif
+const int log10_2_10000 = 30103; // log10(2) * 100000
+// same as C++11 std::numeric_limits<T>::max_digits10
+const int max_digits10_double = (DBL_MANT_DIG * log10_2_10000) / 100000 + 2;
+const int max_digits10_float = (FLT_MANT_DIG * log10_2_10000) / 100000 + 2;
+
namespace {
class HandlersManager
{
@@ -196,6 +203,12 @@ static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok)
return v_cast<QByteArray>(d)->toLongLong(ok);
case QVariant::Bool:
return qlonglong(d->data.b);
+#ifndef QT_BOOTSTRAPPED
+ case QMetaType::QJsonValue:
+ if (!v_cast<QJsonValue>(d)->isDouble())
+ break;
+ // no break
+#endif
case QVariant::Double:
case QVariant::Int:
case QMetaType::Char:
@@ -204,7 +217,6 @@ static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok)
case QMetaType::Long:
case QMetaType::Float:
case QMetaType::LongLong:
- case QMetaType::QJsonValue:
return qMetaTypeNumber(d);
case QVariant::ULongLong:
case QVariant::UInt:
@@ -215,8 +227,9 @@ static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok)
return qlonglong(qMetaTypeUNumber(d));
}
- if (QMetaType::typeFlags(d->type) & QMetaType::IsEnumeration) {
- switch (QMetaType::sizeOf(d->type)) {
+ QMetaType typeInfo(d->type);
+ if (typeInfo.flags() & QMetaType::IsEnumeration) {
+ switch (typeInfo.sizeOf()) {
case 1:
return d->is_shared ? *reinterpret_cast<signed char *>(d->data.shared->ptr) : d->data.sc;
case 2:
@@ -232,6 +245,26 @@ static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok)
return Q_INT64_C(0);
}
+static qreal qConvertToRealNumber(const QVariant::Private *d, bool *ok)
+{
+ *ok = true;
+ switch (uint(d->type)) {
+ case QVariant::Double:
+ return qreal(d->data.d);
+ case QMetaType::Float:
+ return qreal(d->data.f);
+ case QVariant::ULongLong:
+ case QVariant::UInt:
+ case QMetaType::UChar:
+ case QMetaType::UShort:
+ case QMetaType::ULong:
+ return qreal(qMetaTypeUNumber(d));
+ default:
+ // includes enum conversion as well as invalid types
+ return qreal(qConvertToNumber(d, ok));
+ }
+}
+
static qulonglong qConvertToUnsignedNumber(const QVariant::Private *d, bool *ok)
{
*ok = true;
@@ -245,6 +278,12 @@ static qulonglong qConvertToUnsignedNumber(const QVariant::Private *d, bool *ok)
return v_cast<QByteArray>(d)->toULongLong(ok);
case QVariant::Bool:
return qulonglong(d->data.b);
+#ifndef QT_BOOTSTRAPPED
+ case QMetaType::QJsonValue:
+ if (!v_cast<QJsonValue>(d)->isDouble())
+ break;
+ // no break
+#endif
case QVariant::Double:
case QVariant::Int:
case QMetaType::Char:
@@ -253,7 +292,6 @@ static qulonglong qConvertToUnsignedNumber(const QVariant::Private *d, bool *ok)
case QMetaType::Long:
case QMetaType::Float:
case QMetaType::LongLong:
- case QMetaType::QJsonValue:
return qulonglong(qMetaTypeNumber(d));
case QVariant::ULongLong:
case QVariant::UInt:
@@ -263,8 +301,9 @@ static qulonglong qConvertToUnsignedNumber(const QVariant::Private *d, bool *ok)
return qMetaTypeUNumber(d);
}
- if (QMetaType::typeFlags(d->type) & QMetaType::IsEnumeration) {
- switch (QMetaType::sizeOf(d->type)) {
+ QMetaType typeInfo(d->type);
+ if (typeInfo.flags() & QMetaType::IsEnumeration) {
+ switch (typeInfo.sizeOf()) {
case 1:
return d->is_shared ? *reinterpret_cast<uchar *>(d->data.shared->ptr) : d->data.uc;
case 2:
@@ -297,6 +336,27 @@ static const void *constData(const QVariant::Private &d)
return d.is_shared ? d.data.shared->ptr : reinterpret_cast<const void *>(&d.data.c);
}
+#ifndef QT_NO_QOBJECT
+/*!
+ \internal
+ returns a QMetaEnum for a given meta tape type id if possible
+*/
+static QMetaEnum metaEnumFromType(int type)
+{
+ QMetaType t(type);
+ if (t.flags() & QMetaType::IsEnumeration) {
+ if (const QMetaObject *metaObject = t.metaObject()) {
+ const char *enumName = QMetaType::typeName(type);
+ const char *lastColon = std::strrchr(enumName, ':');
+ if (lastColon)
+ enumName = lastColon + 1;
+ return metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
+ }
+ }
+ return QMetaEnum();
+}
+#endif
+
/*!
\internal
@@ -330,7 +390,25 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
return false;
}
break;
-#endif
+ case QVariant::ModelIndex:
+ switch (d->type) {
+ case QVariant::PersistentModelIndex:
+ *static_cast<QModelIndex *>(result) = QModelIndex(*v_cast<QPersistentModelIndex>(d));
+ break;
+ default:
+ return false;
+ }
+ break;
+ case QVariant::PersistentModelIndex:
+ switch (d->type) {
+ case QVariant::ModelIndex:
+ *static_cast<QPersistentModelIndex *>(result) = QPersistentModelIndex(*v_cast<QModelIndex>(d));
+ break;
+ default:
+ return false;
+ }
+ break;
+#endif // QT_BOOTSTRAPPED
case QVariant::String: {
QString *str = static_cast<QString *>(result);
switch (d->type) {
@@ -355,10 +433,10 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
*str = QString::number(qMetaTypeUNumber(d));
break;
case QMetaType::Float:
- *str = QString::number(d->data.f, 'g', FLT_DIG);
+ *str = QString::number(d->data.f, 'g', max_digits10_float);
break;
case QVariant::Double:
- *str = QString::number(d->data.d, 'g', DBL_DIG);
+ *str = QString::number(d->data.d, 'g', max_digits10_double);
break;
#if !defined(QT_NO_DATESTRING)
case QVariant::Date:
@@ -386,13 +464,25 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
*str = v_cast<QUrl>(d)->toString();
break;
case QMetaType::QJsonValue:
- *str = v_cast<QJsonValue>(d)->toString();
+ if (v_cast<QJsonValue>(d)->isString())
+ *str = v_cast<QJsonValue>(d)->toString();
+ else if (!v_cast<QJsonValue>(d)->isNull())
+ return false;
break;
#endif
case QVariant::Uuid:
*str = v_cast<QUuid>(d)->toString();
break;
default:
+#ifndef QT_NO_QOBJECT
+ {
+ QMetaEnum en = metaEnumFromType(d->type);
+ if (en.isValid()) {
+ *str = QString::fromUtf8(en.valueToKey(qConvertToNumber(d, ok)));
+ return *ok;
+ }
+ }
+#endif
return false;
}
break;
@@ -535,10 +625,10 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
*ba = v_cast<QString>(d)->toUtf8();
break;
case QVariant::Double:
- *ba = QByteArray::number(d->data.d, 'g', DBL_DIG);
+ *ba = QByteArray::number(d->data.d, 'g', max_digits10_double);
break;
case QMetaType::Float:
- *ba = QByteArray::number(d->data.f, 'g', FLT_DIG);
+ *ba = QByteArray::number(d->data.f, 'g', max_digits10_float);
break;
case QMetaType::Char:
case QMetaType::SChar:
@@ -561,6 +651,15 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
*ba = QByteArray(d->data.b ? "true" : "false");
break;
default:
+#ifndef QT_NO_QOBJECT
+ {
+ QMetaEnum en = metaEnumFromType(d->type);
+ if (en.isValid()) {
+ *ba = en.valueToKey(qConvertToNumber(d, ok));
+ return *ok;
+ }
+ }
+#endif
return false;
}
}
@@ -630,7 +729,9 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
break;
#ifndef QT_BOOTSTRAPPED
case QMetaType::QJsonValue:
- *b = v_cast<QJsonValue>(d)->toBool();
+ *b = v_cast<QJsonValue>(d)->toBool(false);
+ if (!v_cast<QJsonValue>(d)->isBool())
+ return false;
break;
#endif
default:
@@ -671,7 +772,9 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
break;
#ifndef QT_BOOTSTRAPPED
case QMetaType::QJsonValue:
- *f = v_cast<QJsonValue>(d)->toDouble();
+ *f = v_cast<QJsonValue>(d)->toDouble(0.0);
+ if (!v_cast<QJsonValue>(d)->isDouble())
+ return false;
break;
#endif
default:
@@ -712,7 +815,9 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
break;
#ifndef QT_BOOTSTRAPPED
case QMetaType::QJsonValue:
- *f = v_cast<QJsonValue>(d)->toDouble();
+ *f = v_cast<QJsonValue>(d)->toDouble(0.0);
+ if (!v_cast<QJsonValue>(d)->isDouble())
+ return false;
break;
#endif
default:
@@ -730,6 +835,14 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
} else if (qstrcmp(QMetaType::typeName(d->type), "QList<QVariant>") == 0) {
*static_cast<QVariantList *>(result) =
*static_cast<QList<QVariant> *>(d->data.shared->ptr);
+#ifndef QT_BOOTSTRAPPED
+ } else if (d->type == QMetaType::QJsonValue) {
+ if (!v_cast<QJsonValue>(d)->isArray())
+ return false;
+ *static_cast<QVariantList *>(result) = v_cast<QJsonValue>(d)->toArray().toVariantList();
+ } else if (d->type == QMetaType::QJsonArray) {
+ *static_cast<QVariantList *>(result) = v_cast<QJsonArray>(d)->toVariantList();
+#endif
} else {
return false;
}
@@ -738,6 +851,14 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
if (qstrcmp(QMetaType::typeName(d->type), "QMap<QString, QVariant>") == 0) {
*static_cast<QVariantMap *>(result) =
*static_cast<QMap<QString, QVariant> *>(d->data.shared->ptr);
+#ifndef QT_BOOTSTRAPPED
+ } else if (d->type == QMetaType::QJsonValue) {
+ if (!v_cast<QJsonValue>(d)->isObject())
+ return false;
+ *static_cast<QVariantMap *>(result) = v_cast<QJsonValue>(d)->toObject().toVariantMap();
+ } else if (d->type == QMetaType::QJsonObject) {
+ *static_cast<QVariantMap *>(result) = v_cast<QJsonObject>(d)->toVariantMap();
+#endif
} else {
return false;
}
@@ -746,6 +867,14 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
if (qstrcmp(QMetaType::typeName(d->type), "QHash<QString, QVariant>") == 0) {
*static_cast<QVariantHash *>(result) =
*static_cast<QHash<QString, QVariant> *>(d->data.shared->ptr);
+#ifndef QT_BOOTSTRAPPED
+ } else if (d->type == QMetaType::QJsonValue) {
+ if (!v_cast<QJsonValue>(d)->isObject())
+ return false;
+ *static_cast<QVariantHash *>(result) = v_cast<QJsonValue>(d)->toObject().toVariantHash();
+ } else if (d->type == QMetaType::QJsonObject) {
+ *static_cast<QVariantHash *>(result) = v_cast<QJsonObject>(d)->toVariantHash();
+#endif
} else {
return false;
}
@@ -791,6 +920,31 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok)
}
break;
default:
+#ifndef QT_NO_QOBJECT
+ if (d->type == QVariant::String || d->type == QVariant::ByteArray) {
+ QMetaEnum en = metaEnumFromType(t);
+ if (en.isValid()) {
+ QByteArray keys = (d->type == QVariant::String) ? v_cast<QString>(d)->toUtf8() : *v_cast<QByteArray>(d);
+ int value = en.keysToValue(keys.constData(), ok);
+ if (*ok) {
+ switch (QMetaType::sizeOf(t)) {
+ case 1:
+ *static_cast<signed char *>(result) = value;
+ return true;
+ case 2:
+ *static_cast<qint16 *>(result) = value;
+ return true;
+ case 4:
+ *static_cast<qint32 *>(result) = value;
+ return true;
+ case 8:
+ *static_cast<qint64 *>(result) = value;
+ return true;
+ }
+ }
+ }
+ }
+#endif
return false;
}
return true;
@@ -1072,6 +1226,7 @@ Q_CORE_EXPORT void QVariantPrivate::registerHandler(const int /* Modules::Names
\value EasingCurve a QEasingCurve
\value Uuid a QUuid
\value ModelIndex a QModelIndex
+ \value PersistentModelIndex a QPersistentModelIndex (since 5.5)
\value Font a QFont
\value Hash a QVariantHash
\value Icon a QIcon
@@ -1319,7 +1474,14 @@ QVariant::QVariant(const char *val)
\since 5.0
\fn QVariant::QVariant(const QModelIndex &val)
- Constructs a new variant with an modelIndex value, \a val.
+ Constructs a new variant with a QModelIndex value, \a val.
+*/
+
+/*!
+ \since 5.5
+ \fn QVariant::QVariant(const QPersistentModelIndex &val)
+
+ Constructs a new variant with a QPersistentModelIndex value, \a val.
*/
/*!
@@ -1627,6 +1789,9 @@ QVariant::QVariant(const QUuid &uuid)
QVariant::QVariant(const QModelIndex &modelIndex)
: d(ModelIndex)
{ v_construct<QModelIndex>(&d, modelIndex); }
+QVariant::QVariant(const QPersistentModelIndex &modelIndex)
+ : d(PersistentModelIndex)
+{ v_construct<QPersistentModelIndex>(&d, modelIndex); }
QVariant::QVariant(const QJsonValue &jsonValue)
: d(QMetaType::QJsonValue)
{ v_construct<QJsonValue>(&d, jsonValue); }
@@ -2356,7 +2521,7 @@ QUuid QVariant::toUuid() const
Returns the variant as a QModelIndex if the variant has userType() \l
QModelIndex; otherwise returns a default constructed QModelIndex.
- \sa canConvert(), convert()
+ \sa canConvert(), convert(), toPersistentModelIndex()
*/
QModelIndex QVariant::toModelIndex() const
{
@@ -2364,6 +2529,19 @@ QModelIndex QVariant::toModelIndex() const
}
/*!
+ \since 5.5
+
+ Returns the variant as a QPersistentModelIndex if the variant has userType() \l
+ QPersistentModelIndex; otherwise returns a default constructed QPersistentModelIndex.
+
+ \sa canConvert(), convert(), toModelIndex()
+*/
+QPersistentModelIndex QVariant::toPersistentModelIndex() const
+{
+ return qVariantToHelper<QPersistentModelIndex>(d, handlerManager);
+}
+
+/*!
\since 5.0
Returns the variant as a QJsonValue if the variant has userType() \l
@@ -2830,6 +3008,10 @@ static bool canConvertMetaObject(int fromId, int toId, QObject *fromObject)
*/
bool QVariant::canConvert(int targetTypeId) const
{
+ if ((targetTypeId == QMetaType::QModelIndex && d.type == QMetaType::QPersistentModelIndex)
+ || (targetTypeId == QMetaType::QPersistentModelIndex && d.type == QMetaType::QModelIndex))
+ return true;
+
if (targetTypeId == QMetaType::QVariantList
&& (d.type == QMetaType::QVariantList
|| d.type == QMetaType::QStringList
@@ -2897,11 +3079,18 @@ bool QVariant::canConvert(int targetTypeId) const
case QMetaType::Char:
case QMetaType::SChar:
case QMetaType::Short:
+ case QMetaType::QVariantList:
+ case QMetaType::QVariantMap:
+ case QMetaType::QVariantHash:
return true;
default:
return false;
}
}
+ if (currentType == QMetaType::QJsonArray)
+ return targetTypeId == QMetaType::QVariantList;
+ if (currentType == QMetaType::QJsonObject)
+ return targetTypeId == QMetaType::QVariantMap || targetTypeId == QMetaType::QVariantHash;
// FIXME It should be LastCoreType intead of Uuid
if (currentType > int(QMetaType::QUuid) || targetTypeId > int(QMetaType::QUuid)) {
@@ -2929,10 +3118,12 @@ bool QVariant::canConvert(int targetTypeId) const
case QVariant::Bitmap:
return currentType == QVariant::Pixmap || currentType == QVariant::Image;
case QVariant::ByteArray:
- return currentType == QVariant::Color;
+ return currentType == QVariant::Color
+ || ((QMetaType::typeFlags(currentType) & QMetaType::IsEnumeration) && QMetaType::metaObjectForType(currentType));
case QVariant::String:
return currentType == QVariant::KeySequence || currentType == QVariant::Font
- || currentType == QVariant::Color;
+ || currentType == QVariant::Color
+ || ((QMetaType::typeFlags(currentType) & QMetaType::IsEnumeration) && QMetaType::metaObjectForType(currentType));
case QVariant::KeySequence:
return currentType == QVariant::String || currentType == QVariant::Int;
case QVariant::Font:
@@ -3133,8 +3324,22 @@ bool QVariant::convert(const int type, void *ptr) const
static bool qIsNumericType(uint tp)
{
- return (tp >= QVariant::Bool && tp <= QVariant::Double)
- || (tp >= QMetaType::Long && tp <= QMetaType::Float);
+ static const qulonglong numericTypeBits =
+ Q_UINT64_C(1) << QMetaType::Bool |
+ Q_UINT64_C(1) << QMetaType::Double |
+ Q_UINT64_C(1) << QMetaType::Float |
+ Q_UINT64_C(1) << QMetaType::Char |
+ Q_UINT64_C(1) << QMetaType::SChar |
+ Q_UINT64_C(1) << QMetaType::UChar |
+ Q_UINT64_C(1) << QMetaType::Short |
+ Q_UINT64_C(1) << QMetaType::UShort |
+ Q_UINT64_C(1) << QMetaType::Int |
+ Q_UINT64_C(1) << QMetaType::UInt |
+ Q_UINT64_C(1) << QMetaType::Long |
+ Q_UINT64_C(1) << QMetaType::ULong |
+ Q_UINT64_C(1) << QMetaType::LongLong |
+ Q_UINT64_C(1) << QMetaType::ULongLong;
+ return tp < (CHAR_BIT * sizeof numericTypeBits) ? numericTypeBits & (Q_UINT64_C(1) << tp) : false;
}
static bool qIsFloatingPoint(uint tp)
@@ -3142,26 +3347,137 @@ static bool qIsFloatingPoint(uint tp)
return tp == QVariant::Double || tp == QMetaType::Float;
}
+static int normalizeLowerRanks(uint tp)
+{
+ static const qulonglong numericTypeBits =
+ Q_UINT64_C(1) << QMetaType::Bool |
+ Q_UINT64_C(1) << QMetaType::Char |
+ Q_UINT64_C(1) << QMetaType::SChar |
+ Q_UINT64_C(1) << QMetaType::UChar |
+ Q_UINT64_C(1) << QMetaType::Short |
+ Q_UINT64_C(1) << QMetaType::UShort;
+ return numericTypeBits & (Q_UINT64_C(1) << tp) ? QVariant::Int : tp;
+}
+
+static int normalizeLong(uint tp)
+{
+ const uint IntType = sizeof(long) == sizeof(int) ? QVariant::Int : QVariant::LongLong;
+ const uint UIntType = sizeof(ulong) == sizeof(uint) ? QVariant::UInt : QVariant::ULongLong;
+ return tp == QMetaType::Long ? IntType :
+ tp == QMetaType::ULong ? UIntType : tp;
+}
+
+static int numericTypePromotion(uint t1, uint t2)
+{
+ Q_ASSERT(qIsNumericType(t1));
+ Q_ASSERT(qIsNumericType(t2));
+
+ // C++ integral ranks: (4.13 Integer conversion rank [conv.rank])
+ // bool < signed char < short < int < long < long long
+ // unsigneds have the same rank as their signed counterparts
+ // C++ integral promotion rules (4.5 Integral Promotions [conv.prom])
+ // - any type with rank less than int can be converted to int or unsigned int
+ // 5 Expressions [expr] paragraph 9:
+ // - if either operand is double, the other shall be converted to double
+ // - " " float, " " " float
+ // - if both operands have the same type, no further conversion is needed.
+ // - if both are signed or if both are unsigned, convert to the one with highest rank
+ // - if the unsigned has higher or same rank, convert the signed to the unsigned one
+ // - if the signed can represent all values of the unsigned, convert to the signed
+ // - otherwise, convert to the unsigned corresponding to the rank of the signed
+
+ // floating point: we deviate from the C++ standard by always using qreal
+ if (qIsFloatingPoint(t1) || qIsFloatingPoint(t2))
+ return QMetaType::QReal;
+
+ // integral rules:
+ // for all platforms we support, int can always hold the values of lower-ranked types
+ t1 = normalizeLowerRanks(t1);
+ t2 = normalizeLowerRanks(t2);
+
+ // normalize long / ulong: in all platforms we run, they're either the same as int or as long long
+ t1 = normalizeLong(t1);
+ t2 = normalizeLong(t2);
+
+ // implement the other rules
+ // the four possibilities are Int, UInt, LongLong and ULongLong
+ // if any of the two is ULongLong, then it wins (highest rank, unsigned)
+ // otherwise, if one of the two is LongLong, then the other is either LongLong too or lower-ranked
+ // otherwise, if one of the two is UInt, then the other is either UInt too or Int
+ if (t1 == QVariant::ULongLong || t2 == QVariant::ULongLong)
+ return QVariant::ULongLong;
+ if (t1 == QVariant::LongLong || t2 == QVariant::LongLong)
+ return QVariant::LongLong;
+ if (t1 == QVariant::UInt || t2 == QVariant::UInt)
+ return QVariant::UInt;
+ return QVariant::Int;
+}
+
+static int integralCompare(uint promotedType, const QVariant::Private *d1, const QVariant::Private *d2)
+{
+ // use toLongLong to retrieve the data, it gets us all the bits
+ bool ok;
+ qlonglong l1 = qConvertToNumber(d1, &ok);
+ Q_ASSERT(ok);
+
+ qlonglong l2 = qConvertToNumber(d2, &ok);
+ Q_ASSERT(ok);
+
+ if (promotedType == QVariant::Int)
+ return int(l1) < int(l2) ? -1 : int(l1) == int(l2) ? 0 : 1;
+ if (promotedType == QVariant::UInt)
+ return uint(l1) < uint(l2) ? -1 : uint(l1) == uint(l2) ? 0 : 1;
+ if (promotedType == QVariant::LongLong)
+ return l1 < l2 ? -1 : l1 == l2 ? 0 : 1;
+ if (promotedType == QVariant::ULongLong)
+ return qulonglong(l1) < qulonglong(l2) ? -1 : qulonglong(l1) == qulonglong(l2) ? 0 : 1;
+
+ Q_UNREACHABLE();
+ return 0;
+}
+
+static int numericCompare(const QVariant::Private *d1, const QVariant::Private *d2)
+{
+ uint promotedType = numericTypePromotion(d1->type, d2->type);
+ if (promotedType != QMetaType::QReal)
+ return integralCompare(promotedType, d1, d2);
+
+ // qreal comparisons
+ bool ok;
+ qreal r1 = qConvertToRealNumber(d1, &ok);
+ Q_ASSERT(ok);
+ qreal r2 = qConvertToRealNumber(d2, &ok);
+ Q_ASSERT(ok);
+ if (qFuzzyCompare(r1, r2))
+ return 0;
+ return r1 < r2 ? -1 : 1;
+}
+
/*!
\internal
*/
bool QVariant::cmp(const QVariant &v) const
{
+ // try numerics first, with C++ type promotion rules (no conversion)
+ if (qIsNumericType(d.type) && qIsNumericType(v.d.type))
+ return numericCompare(&d, &v.d) == 0;
+
QVariant v1 = *this;
QVariant v2 = v;
if (d.type != v2.d.type) {
- if (qIsNumericType(d.type) && qIsNumericType(v.d.type)) {
- if (qIsFloatingPoint(d.type) || qIsFloatingPoint(v.d.type))
- return qFuzzyCompare(toReal(), v.toReal());
- else
- return toLongLong() == v.toLongLong();
+ if (v2.canConvert(v1.d.type)) {
+ if (!v2.convert(v1.d.type))
+ return false;
+ } else {
+ // try the opposite conversion, it might work
+ qSwap(v1, v2);
+ if (!v2.convert(v1.d.type))
+ return false;
}
- if (!v2.canConvert(v1.d.type) || !v2.convert(v1.d.type))
- return false;
}
if (v1.d.type >= QMetaType::User) {
int result;
- if (QMetaType::compare(QT_PREPEND_NAMESPACE(constData(v1.d)), QT_PREPEND_NAMESPACE(constData(v2.d)), v1.d.type, &result))
+ if (QMetaType::equals(QT_PREPEND_NAMESPACE(constData(v1.d)), QT_PREPEND_NAMESPACE(constData(v2.d)), v1.d.type, &result))
return result == 0;
}
return handlerManager[v1.d.type]->compare(&v1.d, &v2.d);
@@ -3172,10 +3488,17 @@ bool QVariant::cmp(const QVariant &v) const
*/
int QVariant::compare(const QVariant &v) const
{
+ // try numerics first, with C++ type promotion rules (no conversion)
+ if (qIsNumericType(d.type) && qIsNumericType(v.d.type))
+ return numericCompare(&d, &v.d);
+
+ // check for equality next, as more types implement operator== than operator<
if (cmp(v))
return 0;
+
QVariant v1 = *this;
QVariant v2 = v;
+
if (v1.d.type != v2.d.type) {
// if both types differ, try to convert
if (v2.canConvert(v1.d.type)) {
@@ -3197,18 +3520,16 @@ int QVariant::compare(const QVariant &v) const
}
return r;
}
+
+ // did we end up with two numerics? If so, restart
+ if (qIsNumericType(v1.d.type) && qIsNumericType(v2.d.type))
+ return v1.compare(v2);
}
if (v1.d.type >= QMetaType::User) {
int result;
if (QMetaType::compare(QT_PREPEND_NAMESPACE(constData(d)), QT_PREPEND_NAMESPACE(constData(v2.d)), d.type, &result))
return result;
}
- if (qIsNumericType(v1.d.type)) {
- if (qIsFloatingPoint(v1.d.type))
- return v1.toReal() < v2.toReal() ? -1 : 1;
- else
- return v1.toLongLong() < v2.toLongLong() ? -1 : 1;
- }
switch (v1.d.type) {
case QVariant::Date:
return v1.toDate() < v2.toDate() ? -1 : 1;
@@ -3267,10 +3588,11 @@ bool QVariant::isNull() const
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QVariant &v)
{
+ QDebugStateSaver saver(dbg);
const uint typeId = v.d.type;
dbg.nospace() << "QVariant(";
if (typeId != QMetaType::UnknownType) {
- dbg.nospace() << QMetaType::typeName(typeId) << ", ";
+ dbg << QMetaType::typeName(typeId) << ", ";
bool userStream = false;
bool canConvertToString = false;
if (typeId >= QMetaType::User) {
@@ -3282,19 +3604,20 @@ QDebug operator<<(QDebug dbg, const QVariant &v)
else if (!userStream)
handlerManager[typeId]->debugStream(dbg, v);
} else {
- dbg.nospace() << "Invalid";
+ dbg << "Invalid";
}
- dbg.nospace() << ')';
- return dbg.space();
+ dbg << ')';
+ return dbg;
}
QDebug operator<<(QDebug dbg, const QVariant::Type p)
{
+ QDebugStateSaver saver(dbg);
dbg.nospace() << "QVariant::"
<< (int(p) != int(QMetaType::UnknownType)
? QMetaType::typeName(p)
: "Invalid");
- return dbg.space();
+ return dbg;
}
#endif
@@ -3844,11 +4167,11 @@ void QAssociativeIterable::const_iterator::end()
m_impl.end();
}
-void find(QAssociativeIterable::const_iterator &it, const QVariant &key)
+void QAssociativeIterable::const_iterator::find(const QVariant &key)
{
- Q_ASSERT(key.userType() == it.m_impl._metaType_id_key);
+ Q_ASSERT(key.userType() == m_impl._metaType_id_key);
const QtMetaTypePrivate::VariantData dkey(key.userType(), key.constData(), 0 /*key.flags()*/);
- it.m_impl.find(dkey);
+ m_impl.find(dkey);
}
/*!
@@ -3878,7 +4201,7 @@ QAssociativeIterable::const_iterator QAssociativeIterable::end() const
}
/*!
- \internal
+ \since 5.5
Returns a QAssociativeIterable::const_iterator for the given key \a key
in the container, if the types are convertible.
@@ -3889,12 +4212,12 @@ QAssociativeIterable::const_iterator QAssociativeIterable::end() const
\sa begin(), end(), value()
*/
-QAssociativeIterable::const_iterator find(const QAssociativeIterable &iterable, const QVariant &key)
+QAssociativeIterable::const_iterator QAssociativeIterable::find(const QVariant &key) const
{
- QAssociativeIterable::const_iterator it(iterable, new QAtomicInt(0));
+ const_iterator it(*this, new QAtomicInt(0));
QVariant key_ = key;
- if (key_.canConvert(iterable.m_impl._metaType_id_key) && key_.convert(iterable.m_impl._metaType_id_key))
- find(it, key_);
+ if (key_.canConvert(m_impl._metaType_id_key) && key_.convert(m_impl._metaType_id_key))
+ it.find(key_);
else
it.end();
return it;
@@ -3902,10 +4225,12 @@ QAssociativeIterable::const_iterator find(const QAssociativeIterable &iterable,
/*!
Returns the value for the given \a key in the container, if the types are convertible.
+
+ \sa find()
*/
QVariant QAssociativeIterable::value(const QVariant &key) const
{
- const const_iterator it = find(*this, key);
+ const const_iterator it = find(key);
if (it == end())
return QVariant();
return *it;