Core: QDebug and comparison operator support metatypes.

This patch adds a way to enable operator<, operator== and operator<<
into QDebug for QVariants with custom types.

Change-Id: I3d12d891bd7252ad2b8f1de69bced354800a1f29
Reviewed-by: Stephen Kelly <stephen.kelly@kdab.com>
This commit is contained in:
Christoph Schleifenbaum 2013-03-20 17:14:38 +01:00 committed by The Qt Project
parent 63354e0d09
commit 7ed15da3c1
6 changed files with 460 additions and 28 deletions

View File

@ -420,38 +420,34 @@ public:
int alias;
};
class QMetaTypeConversionRegistry
template<typename T, typename Key>
class QMetaTypeFunctionRegistry
{
public:
typedef QPair<int, int> 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<Key, const QtPrivate::AbstractConverterFunction *> map;
QHash<Key, const T *> map;
};
typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractConverterFunction,QPair<int,int> >
QMetaTypeConverterRegistry;
typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractComparatorFunction,int>
QMetaTypeComparatorRegistry;
typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractDebugStreamFunction,int>
QMetaTypeDebugStreamRegistry;
namespace
{
union CheckThatItIsPod
@ -478,7 +481,9 @@ union CheckThatItIsPod
Q_DECLARE_TYPEINFO(QCustomTypeInfo, Q_MOVABLE_TYPE);
Q_GLOBAL_STATIC(QVector<QCustomTypeInfo>, 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

View File

@ -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<typename T>
struct BuiltInDebugStreamFunction : public AbstractDebugStreamFunction
{
BuiltInDebugStreamFunction()
: AbstractDebugStreamFunction(stream, destroy) {}
static void stream(const AbstractDebugStreamFunction *, QDebug& dbg, const void *r)
{
const T *rhs = static_cast<const T *>(r);
operator<<(dbg, *rhs);
}
static void destroy(AbstractDebugStreamFunction *_this)
{
delete static_cast<BuiltInDebugStreamFunction *>(_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<typename T>
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<const T *>(l);
const T *rhs = static_cast<const T *>(r);
return *lhs < *rhs;
}
static bool equals(const AbstractComparatorFunction *, const void *l, const void *r)
{
const T *lhs = static_cast<const T *>(l);
const T *rhs = static_cast<const T *>(r);
return *lhs == *rhs;
}
static void destroy(AbstractComparatorFunction *_this)
{
delete static_cast<BuiltInComparatorFunction *>(_this);
}
};
struct AbstractConverterFunction
{
typedef bool (*Converter)(const AbstractConverterFunction *, const void *, void*);
@ -437,6 +505,43 @@ public:
inline void destruct(void *data) const;
public:
template<typename T>
static bool registerComparators()
{
Q_STATIC_ASSERT_X((!QMetaTypeId2<T>::IsBuiltIn),
"QMetaType::registerComparators: The type must be a custom type.");
const int typeId = qMetaTypeId<T>();
static const QtPrivate::BuiltInComparatorFunction<T> f;
return registerComparatorFunction( &f, typeId);
}
template<typename T>
static bool hasRegisteredComparators()
{
return hasRegisteredComparators(qMetaTypeId<T>());
}
static bool hasRegisteredComparators(int typeId);
#ifndef QT_NO_DEBUG_STREAM
template<typename T>
static bool registerDebugStreamOperator()
{
Q_STATIC_ASSERT_X((!QMetaTypeId2<T>::IsBuiltIn),
"QMetaType::registerDebugStreamOperator: The type must be a custom type.");
const int typeId = qMetaTypeId<T>();
static const QtPrivate::BuiltInDebugStreamFunction<T> f;
return registerDebugStreamOperatorFunction(&f, typeId);
}
template<typename T>
static bool hasRegisteredDebugStreamOperator()
{
return hasRegisteredDebugStreamOperator(qMetaTypeId<T>());
}
static bool hasRegisteredDebugStreamOperator(int typeId);
#endif
// implicit conversion supported like double -> float
template<typename From, typename To>
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<typename From, typename To>
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<typename T>

View File

@ -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<QString>();
if (!userStream && canConvertToString)
dbg << v.toString();
else if (!userStream)
handlerManager[typeId]->debugStream(dbg, v);
} else {
dbg.nospace() << "Invalid";
}

View File

@ -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:

View File

@ -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

View File

@ -43,6 +43,8 @@
#include <QtCore>
#include <QtTest/QtTest>
#include "tst_qvariant_common.h"
#ifdef Q_OS_LINUX
# include <pthread.h>
#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<typename To>
To convert() const { return s_value.value<To>();}
@ -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<typename T, typename U>
U convert(const T &t)
@ -2097,6 +2115,73 @@ void tst_QMetaType::convertCustomType()
QCOMPARE(v.value<CustomConvertibleType2>().m_foo, testCustom.m_foo);
}
void tst_QMetaType::compareCustomType_data()
{
QMetaType::registerComparators<CustomConvertibleType>();
QTest::addColumn<QVariantList>("unsorted");
QTest::addColumn<QVariantList>("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<CustomDebugStreamableType>());
QVariant v1 = QVariant::fromValue(CustomDebugStreamableType());
handler.expectedMessage = "QVariant(CustomDebugStreamableType, )";
qDebug() << v1;
QMetaType::registerConverter<CustomDebugStreamableType, QString>(&CustomDebugStreamableType::toString);
handler.expectedMessage = "QVariant(CustomDebugStreamableType, \"test\")";
qDebug() << v1;
QMetaType::registerDebugStreamOperator<CustomDebugStreamableType>();
handler.expectedMessage = "QVariant(CustomDebugStreamableType, string-content)";
qDebug() << v1;
}
// Compile-time test, it should be possible to register function pointer types
class Undefined;