Support QMetaType::equals()

This avoids having to define operator< for types where operator== is
required but operator< doesn't make any sense (e.g. QGeoCoordinate).

Change-Id: I81f6a9d8fc0009a4514c974b5e02b446c50d1e31
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
Alex Blasche 2015-01-09 11:55:43 +01:00
parent 8fdd1bb8cb
commit 0d4485fd78
4 changed files with 152 additions and 4 deletions

View File

@ -577,11 +577,19 @@ Q_GLOBAL_STATIC(QMetaTypeDebugStreamRegistry, customTypesDebugStreamRegistry)
/*!
\fn bool QMetaType::registerComparators()
\since 5.2
Registers comparison operetarors for the user-registered type T. This requires T to have
Registers comparison operators for the user-registered type T. This requires T to have
both an operator== and an operator<.
Returns \c true if the registration succeeded, otherwise false.
*/
/*!
\fn bool QMetaType::registerEqualsComparator()
\since 5.5
Registers equals operator for the user-registered type T. This requires T to have
an operator==.
Returns \c true if the registration succeeded, otherwise false.
*/
#ifndef QT_NO_DEBUG_STREAM
/*!
\fn bool QMetaType::registerDebugStreamOperator()
@ -687,7 +695,7 @@ bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId
/*!
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, otherwiess false.
or greater than \a rhs. Returns \c true, if the comparison succeeded, otherwise \c false.
\since 5.2
*/
bool QMetaType::compare(const void *lhs, const void *rhs, int typeId, int* result)
@ -698,8 +706,29 @@ bool QMetaType::compare(const void *lhs, const void *rhs, int typeId, int* resul
return false;
if (f->equals(f, lhs, rhs))
*result = 0;
else
else if (f->lessThan)
*result = f->lessThan(f, lhs, rhs) ? -1 : 1;
else
return false;
return true;
}
/*!
Compares the objects at \a lhs and \a rhs. Both objects need to be of type \a typeId.
\a result is set to zero, if \a lhs equals to rhs. Returns \c true, if the comparison
succeeded, otherwise \c false.
\since 5.5
*/
bool QMetaType::equals(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 = -1;
return true;
}

View File

@ -291,6 +291,24 @@ struct BuiltInComparatorFunction : public AbstractComparatorFunction
}
};
template<typename T>
struct BuiltInEqualsComparatorFunction : public AbstractComparatorFunction
{
BuiltInEqualsComparatorFunction()
: AbstractComparatorFunction(0, equals, destroy) {}
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<BuiltInEqualsComparatorFunction *>(_this);
}
};
struct AbstractConverterFunction
{
typedef bool (*Converter)(const AbstractConverterFunction *, const void *, void*);
@ -529,6 +547,16 @@ public:
static const QtPrivate::BuiltInComparatorFunction<T> f;
return registerComparatorFunction( &f, typeId);
}
template<typename T>
static bool registerEqualsComparator()
{
Q_STATIC_ASSERT_X((!QMetaTypeId2<T>::IsBuiltIn),
"QMetaType::registerEqualsComparator: The type must be a custom type.");
const int typeId = qMetaTypeId<T>();
static const QtPrivate::BuiltInEqualsComparatorFunction<T> f;
return registerComparatorFunction( &f, typeId);
}
template<typename T>
static bool hasRegisteredComparators()
{
@ -610,6 +638,7 @@ public:
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 equals(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>

View File

@ -3313,7 +3313,7 @@ bool QVariant::cmp(const QVariant &v) const
}
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);

View File

@ -121,6 +121,7 @@ private slots:
void convertCustomType();
void compareCustomType_data();
void compareCustomType();
void compareCustomEqualOnlyType();
void customDebugStream();
};
@ -2003,9 +2004,23 @@ bool operator==(const CustomConvertibleType2 &lhs, const CustomConvertibleType2
bool operator!=(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 &rhs)
{ return !operator==(lhs, rhs); }
struct CustomEqualsOnlyType
{
explicit CustomEqualsOnlyType(int value = 0) : val(value) {}
virtual ~CustomEqualsOnlyType() {}
int val;
};
bool operator==(const CustomEqualsOnlyType &lhs, const CustomEqualsOnlyType &rhs)
{ return lhs.val == rhs.val;}
bool operator!=(const CustomEqualsOnlyType &lhs, const CustomEqualsOnlyType &rhs)
{ return !operator==(lhs, rhs); }
Q_DECLARE_METATYPE(CustomConvertibleType);
Q_DECLARE_METATYPE(CustomConvertibleType2);
Q_DECLARE_METATYPE(CustomDebugStreamableType);
Q_DECLARE_METATYPE(CustomEqualsOnlyType);
template<typename T, typename U>
U convert(const T &t)
@ -2282,6 +2297,81 @@ void tst_QMetaType::compareCustomType()
QCOMPARE(unsorted, sorted);
}
void tst_QMetaType::compareCustomEqualOnlyType()
{
int metaTypeId = qRegisterMetaType<CustomEqualsOnlyType>();
QMetaType::registerEqualsComparator<CustomEqualsOnlyType>();
int result;
CustomEqualsOnlyType val50(50);
CustomEqualsOnlyType val100(100);
CustomEqualsOnlyType val100x(100);
QVariant variant50 = QVariant::fromValue(val50);
QVariant variant100 = QVariant::fromValue(val100);
QVariant variant100x = QVariant::fromValue(val100x);
QVERIFY(variant50 != variant100);
QVERIFY(variant50 != variant100x);
QVERIFY(variant100 != variant50);
QVERIFY(variant100x != variant50);
QVERIFY(variant100 == variant100x);
QVERIFY(variant100 == variant100);
// compare always fails
QVERIFY(!(variant50 < variant50));
QVERIFY(!(variant50 < variant100));
QVERIFY(!(variant100 < variant50));
// check QMetaType::compare works/doesn't crash for equals only comparators
bool wasSuccess = QMetaType::compare(variant50.constData(), variant50.constData(),
metaTypeId, &result);
QCOMPARE(result, 0);
QVERIFY(wasSuccess);
wasSuccess = QMetaType::compare(variant100.constData(), variant100x.constData(),
metaTypeId, &result);
QCOMPARE(result, 0);
QVERIFY(wasSuccess);
wasSuccess = QMetaType::compare(variant50.constData(), variant100.constData(),
metaTypeId, &result);
QVERIFY(!wasSuccess);
// check QMetaType::equals works for equals only comparator
wasSuccess = QMetaType::equals(variant50.constData(), variant50.constData(),
metaTypeId, &result);
QCOMPARE(result, 0);
QVERIFY(wasSuccess);
wasSuccess = QMetaType::equals(variant100.constData(), variant100.constData(),
metaTypeId, &result);
QCOMPARE(result, 0);
QVERIFY(wasSuccess);
wasSuccess = QMetaType::equals(variant100x.constData(), variant100x.constData(),
metaTypeId, &result);
QCOMPARE(result, 0);
QVERIFY(wasSuccess);
wasSuccess = QMetaType::equals(variant100.constData(), variant100x.constData(),
metaTypeId, &result);
QCOMPARE(result, 0);
QVERIFY(wasSuccess);
wasSuccess = QMetaType::equals(variant50.constData(), variant100.constData(),
metaTypeId, &result);
QCOMPARE(result, -1);
QVERIFY(wasSuccess);
wasSuccess = QMetaType::equals(variant50.constData(), variant100x.constData(),
metaTypeId, &result);
QCOMPARE(result, -1);
QVERIFY(wasSuccess);
//check QMetaType::equals for type w/o equals comparator being registered
CustomMovable movable1;
CustomMovable movable2;
wasSuccess = QMetaType::equals(&movable1, &movable2,
qRegisterMetaType<CustomMovable>(), &result);
QVERIFY(!wasSuccess);
}
struct MessageHandlerCustom : public MessageHandler
{
MessageHandlerCustom(const int typeId)