diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index bfc48774b5..112ef747a3 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -420,38 +420,34 @@ public: int alias; }; -class QMetaTypeConversionRegistry +template +class QMetaTypeFunctionRegistry { public: - typedef QPair Key; - - ~QMetaTypeConversionRegistry() + ~QMetaTypeFunctionRegistry() { const QWriteLocker locker(&lock); map.clear(); } - bool contains(int from, int to) const + bool contains(Key k) const { - const Key k(from, to); const QReadLocker locker(&lock); return map.contains(k); } - bool insertIfNotContains(int from, int to, const QtPrivate::AbstractConverterFunction *f) + bool insertIfNotContains(Key k, const T *f) { - const Key k(from, to); const QWriteLocker locker(&lock); - const QtPrivate::AbstractConverterFunction* &fun = map[k]; + const T* &fun = map[k]; if (fun != 0) return false; fun = f; return true; } - const QtPrivate::AbstractConverterFunction *function(int from, int to) const + const T *function(Key k) const { - const Key k(from, to); const QReadLocker locker(&lock); return map.value(k, 0); } @@ -464,9 +460,16 @@ public: } private: mutable QReadWriteLock lock; - QHash map; + QHash map; }; +typedef QMetaTypeFunctionRegistry > +QMetaTypeConverterRegistry; +typedef QMetaTypeFunctionRegistry +QMetaTypeComparatorRegistry; +typedef QMetaTypeFunctionRegistry +QMetaTypeDebugStreamRegistry; + namespace { union CheckThatItIsPod @@ -478,7 +481,9 @@ union CheckThatItIsPod Q_DECLARE_TYPEINFO(QCustomTypeInfo, Q_MOVABLE_TYPE); Q_GLOBAL_STATIC(QVector, customTypes) Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock) -Q_GLOBAL_STATIC(QMetaTypeConversionRegistry, customTypesConversionRegistry) +Q_GLOBAL_STATIC(QMetaTypeConverterRegistry, customTypesConversionRegistry) +Q_GLOBAL_STATIC(QMetaTypeComparatorRegistry, customTypesComparatorRegistry) +Q_GLOBAL_STATIC(QMetaTypeDebugStreamRegistry, customTypesDebugStreamRegistry) /*! \fn bool QMetaType::registerConverter() @@ -511,6 +516,23 @@ Q_GLOBAL_STATIC(QMetaTypeConversionRegistry, customTypesConversionRegistry) to type To in the meta type system. Returns true if the registration succeeded, otherwise false. */ +/*! + \fn bool QMetaType::registerComparators() + \since 5.2 + Registers comparison operetarors for the user-registered type T. This requires T to have + both an operator== and an operator<. + Returns true if the registration succeeded, otherwise false. +*/ + +#ifndef QT_NO_DEBUG_STREAM +/*! + \fn bool QMetaType::registerDebugStreamOperator() + Registers the debug stream operator for the user-registered type T. This requires T to have + an operator<<(QDebug dbg, T). + Returns true if the registration succeeded, otherwise false. +*/ +#endif + /*! Registers function \a f as converter function from type id \a from to \a to. If there's already a conversion registered, this does nothing but deleting \a f. @@ -520,7 +542,7 @@ Q_GLOBAL_STATIC(QMetaTypeConversionRegistry, customTypesConversionRegistry) */ bool QMetaType::registerConverterFunction(const QtPrivate::AbstractConverterFunction *f, int from, int to) { - if (!customTypesConversionRegistry()->insertIfNotContains(from, to, f)) { + if (!customTypesConversionRegistry()->insertIfNotContains(qMakePair(from, to), f)) { qWarning("Type conversion already registered from type %s to type %s", QMetaType::typeName(from), QMetaType::typeName(to)); return false; @@ -538,6 +560,58 @@ void QMetaType::unregisterConverterFunction(int from, int to) customTypesConversionRegistry()->remove(from, to); } +bool QMetaType::registerComparatorFunction(const QtPrivate::AbstractComparatorFunction *f, int type) +{ + if (!customTypesComparatorRegistry()->insertIfNotContains(type, f)) { + qWarning("Comparators already registered for type %s", QMetaType::typeName(type)); + return false; + } + return true; +} + +/*! + \fn bool QMetaType::hasRegisteredComparators() + Returns true, if the meta type system has registered comparators for type T. + \since 5.2 + */ + +/*! + Returns true, if the meta type system has registered comparators for type id \a typeId. + \since 5.2 + */ +bool QMetaType::hasRegisteredComparators(int typeId) +{ + return customTypesComparatorRegistry()->contains(typeId); +} + +#ifndef QT_NO_DEBUG_STREAM +bool QMetaType::registerDebugStreamOperatorFunction(const QtPrivate::AbstractDebugStreamFunction *f, + int type) +{ + if (!customTypesDebugStreamRegistry()->insertIfNotContains(type, f)) { + qWarning("Debug stream operator already registered for type %s", QMetaType::typeName(type)); + return false; + } + return true; +} + +/*! + \fn bool QMetaType::hasRegisteredDebugStreamOperator() + Returns true, if the meta type system has a registered debug stream operator for type T. + \since 5.2 + */ + +/*! + Returns true, if the meta type system has a registered debug stream operator for type + id \a typeId. + \since 5.2 +*/ +bool QMetaType::hasRegisteredDebugStreamOperator(int typeId) +{ + return customTypesDebugStreamRegistry()->contains(typeId); +} +#endif + /*! Converts the object at \a from from \a fromTypeId to the preallocated space at \a to typed \a toTypeId. Returns true, if the conversion succeeded, otherwise false. @@ -545,10 +619,44 @@ void QMetaType::unregisterConverterFunction(int from, int to) */ bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId) { - const QtPrivate::AbstractConverterFunction * const f = customTypesConversionRegistry()->function(fromTypeId, toTypeId); + const QtPrivate::AbstractConverterFunction * const f = + customTypesConversionRegistry()->function(qMakePair(fromTypeId, toTypeId)); return f && f->convert(f, from, to); } +/*! + 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 true, if the comparison succeeded, otherwiess false. + \since 5.2 +*/ +bool QMetaType::compare(const void *lhs, const void *rhs, int typeId, int* result) +{ + const QtPrivate::AbstractComparatorFunction * const f = + customTypesComparatorRegistry()->function(typeId); + if (!f) + return false; + if (f->equals(f, lhs, rhs)) + *result = 0; + else + *result = f->lessThan(f, lhs, rhs) ? -1 : 1; + return true; +} + +/*! + Streams the object at \a rhs of type \a typeId to the debug stream \a dbg. Returns true + on success, otherwise false. + \since 5.2 +*/ +bool QMetaType::debugStream(QDebug& dbg, const void *rhs, int typeId) +{ + const QtPrivate::AbstractDebugStreamFunction * const f = customTypesDebugStreamRegistry()->function(typeId); + if (!f) + return false; + f->stream(f, dbg, rhs); + return true; +} + /*! \fn bool QMetaType::hasRegisteredConverterFunction() Returns true, if the meta type system has a registered conversion from type From to type To. @@ -563,7 +671,7 @@ bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId */ bool QMetaType::hasRegisteredConverterFunction(int fromTypeId, int toTypeId) { - return customTypesConversionRegistry()->contains(fromTypeId, toTypeId); + return customTypesConversionRegistry()->contains(qMakePair(fromTypeId, toTypeId)); } #ifndef QT_NO_DATASTREAM diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index a6e87bdb12..5823584b46 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -222,6 +222,74 @@ To convertImplicit(const From& from) return from; } +#ifndef QT_NO_DEBUG_STREAM +struct AbstractDebugStreamFunction +{ + typedef void (*Stream)(const AbstractDebugStreamFunction *, QDebug&, const void *); + typedef void (*Destroy)(AbstractDebugStreamFunction *); + explicit AbstractDebugStreamFunction(Stream s = 0, Destroy d = 0) + : stream(s), destroy(d) {} + Q_DISABLE_COPY(AbstractDebugStreamFunction) + Stream stream; + Destroy destroy; +}; + +template +struct BuiltInDebugStreamFunction : public AbstractDebugStreamFunction +{ + BuiltInDebugStreamFunction() + : AbstractDebugStreamFunction(stream, destroy) {} + static void stream(const AbstractDebugStreamFunction *, QDebug& dbg, const void *r) + { + const T *rhs = static_cast(r); + operator<<(dbg, *rhs); + } + + static void destroy(AbstractDebugStreamFunction *_this) + { + delete static_cast(_this); + } +}; +#endif + +struct AbstractComparatorFunction +{ + typedef bool (*LessThan)(const AbstractComparatorFunction *, const void *, const void *); + typedef bool (*Equals)(const AbstractComparatorFunction *, const void *, const void *); + typedef void (*Destroy)(AbstractComparatorFunction *); + explicit AbstractComparatorFunction(LessThan lt = 0, Equals e = 0, Destroy d = 0) + : lessThan(lt), equals(e), destroy(d) {} + Q_DISABLE_COPY(AbstractComparatorFunction) + LessThan lessThan; + Equals equals; + Destroy destroy; +}; + +template +struct BuiltInComparatorFunction : public AbstractComparatorFunction +{ + BuiltInComparatorFunction() + : AbstractComparatorFunction(lessThan, equals, destroy) {} + static bool lessThan(const AbstractComparatorFunction *, const void *l, const void *r) + { + const T *lhs = static_cast(l); + const T *rhs = static_cast(r); + return *lhs < *rhs; + } + + static bool equals(const AbstractComparatorFunction *, const void *l, const void *r) + { + const T *lhs = static_cast(l); + const T *rhs = static_cast(r); + return *lhs == *rhs; + } + + static void destroy(AbstractComparatorFunction *_this) + { + delete static_cast(_this); + } +}; + struct AbstractConverterFunction { typedef bool (*Converter)(const AbstractConverterFunction *, const void *, void*); @@ -437,6 +505,43 @@ public: inline void destruct(void *data) const; public: + template + static bool registerComparators() + { + Q_STATIC_ASSERT_X((!QMetaTypeId2::IsBuiltIn), + "QMetaType::registerComparators: The type must be a custom type."); + + const int typeId = qMetaTypeId(); + static const QtPrivate::BuiltInComparatorFunction f; + return registerComparatorFunction( &f, typeId); + } + template + static bool hasRegisteredComparators() + { + return hasRegisteredComparators(qMetaTypeId()); + } + static bool hasRegisteredComparators(int typeId); + + +#ifndef QT_NO_DEBUG_STREAM + template + static bool registerDebugStreamOperator() + { + Q_STATIC_ASSERT_X((!QMetaTypeId2::IsBuiltIn), + "QMetaType::registerDebugStreamOperator: The type must be a custom type."); + + const int typeId = qMetaTypeId(); + static const QtPrivate::BuiltInDebugStreamFunction f; + return registerDebugStreamOperatorFunction(&f, typeId); + } + template + static bool hasRegisteredDebugStreamOperator() + { + return hasRegisteredDebugStreamOperator(qMetaTypeId()); + } + static bool hasRegisteredDebugStreamOperator(int typeId); +#endif + // implicit conversion supported like double -> float template static bool registerConverter() @@ -490,6 +595,8 @@ public: #endif static bool convert(const void *from, int fromTypeId, void *to, int toTypeId); + static bool compare(const void *lhs, const void *rhs, int typeId, int* result); + static bool debugStream(QDebug& dbg, const void *rhs, int typeId); template static bool hasRegisteredConverterFunction() @@ -527,6 +634,11 @@ private: void *constructExtended(void *where, const void *copy = 0) const; void destructExtended(void *data) const; + static bool registerComparatorFunction(const QtPrivate::AbstractComparatorFunction *f, int type); +#ifndef QT_NO_DEBUG_STREAM + static bool registerDebugStreamOperatorFunction(const QtPrivate::AbstractDebugStreamFunction *f, int type); +#endif + #ifndef Q_NO_TEMPLATE_FRIENDS #ifndef Q_QDOC template diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index f747eb45ab..274a86591b 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -2938,8 +2938,9 @@ bool QVariant::convert(const int type, void *ptr) const which means that two values can be equal even if one of them is null and another is not. - \warning This function doesn't support custom types registered - with qRegisterMetaType(). + \warning To make this function work with a custom type registered with + qRegisterMetaType(), its comparison operator must be registered using + QMetaType::registerComparators(). */ /*! \fn bool operator!=(const QVariant &v1, const QVariant &v2) @@ -2948,8 +2949,9 @@ bool QVariant::convert(const int type, void *ptr) const Returns false if \a v1 and \a v2 are equal; otherwise returns true. - \warning This function doesn't support custom types registered - with qRegisterMetaType(). + \warning To make this function work with a custom type registered with + qRegisterMetaType(), its comparison operator must be registered using + QMetaType::registerComparators(). */ /*! \fn bool QVariant::operator==(const QVariant &v) const @@ -2962,8 +2964,9 @@ bool QVariant::convert(const int type, void *ptr) const type is not the same as this variant's type. See canConvert() for a list of possible conversions. - \warning This function doesn't support custom types registered - with qRegisterMetaType(). + \warning To make this function work with a custom type registered with + qRegisterMetaType(), its comparison operator must be registered using + QMetaType::registerComparators(). */ /*! @@ -2972,8 +2975,61 @@ bool QVariant::convert(const int type, void *ptr) const Compares this QVariant with \a v and returns true if they are not equal; otherwise returns false. - \warning This function doesn't support custom types registered - with qRegisterMetaType(). + \warning To make this function work with a custom type registered with + qRegisterMetaType(), its comparison operator must be registered using + QMetaType::registerComparators(). +*/ + +/*! + \fn bool QVariant::operator<(const QVariant &v) const + + Compares this QVariant with \a v and returns true if this is less than \a v. + + \note Comparability might not be availabe for the type stored in this QVariant + or in \a v. + + \warning To make this function work with a custom type registered with + qRegisterMetaType(), its comparison operator must be registered using + QMetaType::registerComparators(). +*/ + +/*! + \fn bool QVariant::operator<=(const QVariant &v) const + + Compares this QVariant with \a v and returns true if this is less or equal than \a v. + + \note Comparability might not be available for the type stored in this QVariant + or in \a v. + + \warning To make this function work with a custom type registered with + qRegisterMetaType(), its comparison operator must be registered using + QMetaType::registerComparators(). +*/ + +/*! + \fn bool QVariant::operator>(const QVariant &v) const + + Compares this QVariant with \a v and returns true if this is larger than \a v. + + \note Comparability might not be available for the type stored in this QVariant + or in \a v. + + \warning To make this function work with a custom type registered with + qRegisterMetaType(), its comparison operator must be registered using + QMetaType::registerComparators(). +*/ + +/*! + \fn bool QVariant::operator>=(const QVariant &v) const + + Compares this QVariant with \a v and returns true if this is larger or equal than \a v. + + \note Comparability might not be available for the type stored in this QVariant + or in \a v. + + \warning To make this function work with a custom type registered with + qRegisterMetaType(), its comparison operator must be registered using + QMetaType::registerComparators(). */ static bool qIsNumericType(uint tp) @@ -2992,6 +3048,7 @@ static bool qIsFloatingPoint(uint tp) */ bool QVariant::cmp(const QVariant &v) const { + QVariant v1 = *this; QVariant v2 = v; if (d.type != v2.d.type) { if (qIsNumericType(d.type) && qIsNumericType(v.d.type)) { @@ -3000,10 +3057,63 @@ bool QVariant::cmp(const QVariant &v) const else return toLongLong() == v.toLongLong(); } - if (!v2.canConvert(d.type) || !v2.convert(d.type)) + if (!v2.canConvert(v1.d.type) || !v2.convert(v1.d.type)) return false; } - return handlerManager[d.type]->compare(&d, &v2.d); + 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)) + return result == 0; + } + return handlerManager[v1.d.type]->compare(&v1.d, &v2.d); +} + +/*! + \internal + */ +int QVariant::compare(const QVariant &v) const +{ + 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)) { + QVariant temp = v2; + if (temp.convert(v1.d.type)) + v2 = temp; + } + if (v1.d.type != v2.d.type && v1.canConvert(v2.d.type)) { + QVariant temp = v1; + if (temp.convert(v2.d.type)) + v1 = temp; + } + if (v1.d.type != v2.d.type) { + // if conversion fails, default to toString + return v1.toString().compare(v2.toString(), Qt::CaseInsensitive); + } + } + 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; + case QVariant::Time: + return v1.toTime() < v2.toTime() ? -1 : 1; + case QVariant::DateTime: + return v1.toDateTime() < v2.toDateTime() ? -1 : 1; + } + return v1.toString().compare(v2.toString(), Qt::CaseInsensitive); } /*! @@ -3052,7 +3162,14 @@ QDebug operator<<(QDebug dbg, const QVariant &v) dbg.nospace() << "QVariant("; if (typeId != QMetaType::UnknownType) { dbg.nospace() << QMetaType::typeName(typeId) << ", "; - handlerManager[typeId]->debugStream(dbg, v); + bool userStream = false; + if (typeId >= QMetaType::User) + userStream = QMetaType::debugStream(dbg, constData(v.d), typeId); + bool canConvertToString = v.canConvert(); + if (!userStream && canConvertToString) + dbg << v.toString(); + else if (!userStream) + handlerManager[typeId]->debugStream(dbg, v); } else { dbg.nospace() << "Invalid"; } diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 4832a633f8..d5ea491288 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -433,6 +433,14 @@ class Q_CORE_EXPORT QVariant { return cmp(v); } inline bool operator!=(const QVariant &v) const { return !cmp(v); } + inline bool operator<(const QVariant &v) const + { return compare(v) < 0; } + inline bool operator<=(const QVariant &v) const + { return compare(v) <= 0; } + inline bool operator>(const QVariant &v) const + { return compare(v) > 0; } + inline bool operator>=(const QVariant &v) const + { return compare(v) >= 0; } protected: friend inline bool operator==(const QVariant &, const QVariantComparisonHelper &); @@ -450,6 +458,7 @@ public: Private d; void create(int type, const void *copy); bool cmp(const QVariant &other) const; + int compare(const QVariant &other) const; bool convert(const int t, void *ptr) const; private: diff --git a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro index 5009fedc4f..23a8e6d23a 100644 --- a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro +++ b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro @@ -1,6 +1,7 @@ CONFIG += testcase parallel_test TARGET = tst_qmetatype QT = core testlib +INCLUDEPATH += $$PWD/../../../other/qvariant_common SOURCES = tst_qmetatype.cpp TESTDATA=./typeFlags.bin DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 1208178c8b..47900204e7 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -43,6 +43,8 @@ #include #include +#include "tst_qvariant_common.h" + #ifdef Q_OS_LINUX # include #endif @@ -113,6 +115,9 @@ private slots: void constRefs(); void convertCustomType_data(); void convertCustomType(); + void compareCustomType_data(); + void compareCustomType(); + void customDebugStream(); }; struct Foo { int i; }; @@ -1821,7 +1826,7 @@ struct CustomConvertibleType { explicit CustomConvertibleType(const QVariant &foo = QVariant()) : m_foo(foo) {} virtual ~CustomConvertibleType() {} - QString toString() const { return QLatin1String("CustomConvertibleType::toString()"); } + QString toString() const { return m_foo.toString(); } operator QPoint() const { return QPoint(12, 34); } template To convert() const { return s_value.value();} @@ -1833,6 +1838,8 @@ struct CustomConvertibleType static bool s_ok; }; +bool operator<(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs) +{ return lhs.m_foo < rhs.m_foo; } bool operator==(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs) { return lhs.m_foo == rhs.m_foo; } bool operator!=(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs) @@ -1851,6 +1858,16 @@ struct CustomConvertibleType2 QVariant m_foo; }; +struct CustomDebugStreamableType +{ + QString toString() const { return "test"; } +}; + +QDebug operator<<(QDebug dbg, const CustomDebugStreamableType&) +{ + return dbg << "string-content"; +} + bool operator==(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 &rhs) { return lhs.m_foo == rhs.m_foo; } bool operator!=(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 &rhs) @@ -1858,6 +1875,7 @@ bool operator!=(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 Q_DECLARE_METATYPE(CustomConvertibleType); Q_DECLARE_METATYPE(CustomConvertibleType2); +Q_DECLARE_METATYPE(CustomDebugStreamableType); template U convert(const T &t) @@ -2097,6 +2115,73 @@ void tst_QMetaType::convertCustomType() QCOMPARE(v.value().m_foo, testCustom.m_foo); } +void tst_QMetaType::compareCustomType_data() +{ + QMetaType::registerComparators(); + + QTest::addColumn("unsorted"); + QTest::addColumn("sorted"); + + QTest::newRow("int") << (QVariantList() << 37 << 458 << 1 << 243 << -4 << 383) + << (QVariantList() << -4 << 1 << 37 << 243 << 383 << 458); + + QTest::newRow("dobule") << (QVariantList() << 4934.93 << 0.0 << 302.39 << -39.0) + << (QVariantList() << -39.0 << 0.0 << 302.39 << 4934.93); + + QTest::newRow("QString") << (QVariantList() << "Hello" << "World" << "this" << "is" << "a" << "test") + << (QVariantList() << "a" << "Hello" << "is" << "test" << "this" << "World"); + + QTest::newRow("QTime") << (QVariantList() << QTime(14, 39) << QTime(0, 0) << QTime(18, 18) << QTime(9, 27)) + << (QVariantList() << QTime(0, 0) << QTime(9, 27) << QTime(14, 39) << QTime(18, 18)); + + QTest::newRow("QDate") << (QVariantList() << QDate(2013, 3, 23) << QDate(1900, 12, 1) << QDate(2001, 2, 2) << QDate(1982, 12, 16)) + << (QVariantList() << QDate(1900, 12, 1) << QDate(1982, 12, 16) << QDate(2001, 2, 2) << QDate(2013, 3, 23)); + + QTest::newRow("mixed") << (QVariantList() << "Hello" << "World" << QChar('a') << 38 << QChar('z') << -39 << 4.6) + << (QVariantList() << -39 << 4.6 << 38 << QChar('a') << "Hello" << "World" << QChar('z')); + + QTest::newRow("custom") << (QVariantList() << QVariant::fromValue(CustomConvertibleType(1)) << QVariant::fromValue(CustomConvertibleType(100)) << QVariant::fromValue(CustomConvertibleType(50))) + << (QVariantList() << QVariant::fromValue(CustomConvertibleType(1)) << QVariant::fromValue(CustomConvertibleType(50)) << QVariant::fromValue(CustomConvertibleType(100))); +} + +void tst_QMetaType::compareCustomType() +{ + QFETCH(QVariantList, unsorted); + QFETCH(QVariantList, sorted); + qSort(unsorted); + QCOMPARE(unsorted, sorted); +} + +struct MessageHandlerCustom : public MessageHandler +{ + MessageHandlerCustom(const int typeId) + : MessageHandler(typeId, handler) + {} + static void handler(QtMsgType, const QMessageLogContext &, const QString &msg) + { + QCOMPARE(msg.trimmed(), expectedMessage.trimmed()); + } + static QString expectedMessage; +}; + +QString MessageHandlerCustom::expectedMessage; + +void tst_QMetaType::customDebugStream() +{ + MessageHandlerCustom handler(::qMetaTypeId()); + QVariant v1 = QVariant::fromValue(CustomDebugStreamableType()); + handler.expectedMessage = "QVariant(CustomDebugStreamableType, )"; + qDebug() << v1; + + QMetaType::registerConverter(&CustomDebugStreamableType::toString); + handler.expectedMessage = "QVariant(CustomDebugStreamableType, \"test\")"; + qDebug() << v1; + + QMetaType::registerDebugStreamOperator(); + handler.expectedMessage = "QVariant(CustomDebugStreamableType, string-content)"; + qDebug() << v1; +} + // Compile-time test, it should be possible to register function pointer types class Undefined;