Automatically register comparison operators in QMetaType

This removes the fully manual registration of comparison operators in
QMetaType and replaces it with an automatic registration through
Q_DECLARE_METATYPE().

[ChangeLog][QMetaType] The QMetaType::registerComparator() and
QMetaType::registerEqualsComparator() have been removed.
Q_DECLARE_METATYPE() now automatically registers any
operator==() and/or operator<() for a type visible where
it is used on that type, as part of declaring its meta-type.

Change-Id: I3df451b652b735c093533838bf32f3cc785439f8
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Fabian Kosmale 2020-06-29 14:26:36 +02:00 committed by Lars Knoll
parent 0e2cfdedf2
commit 986d89c2ee
8 changed files with 225 additions and 240 deletions

View File

@ -657,6 +657,98 @@ void QMetaType::destruct(void *data) const
}
}
/*!
Compares the objects at \a lhs and \a rhs for ordering.
Returns an empty optional if comparison is not supported or the values are unordered.
Otherwise, returns -1, 0 or +1 according as \a lhs is less than, equal to or greater
than \a rhs.
Both objects must be of the type described by this metatype. If either \a lhs
or \a rhs is \nullptr, the values are unordered. Comparison is only supported
if the type's less than operator was visible to the metatype declaration.
If the type's equality operator was also visible, values will only compare equal if the
equality operator says they are. In the absence of an equality operator, when neither
value is less than the other, values are considered equal; if equality is also available
and two such values are not equal, they are considered unordered, just as NaN (not a
number) values of a floating point type lie outside its ordering.
\note If no less than operator was visible to the metatype declaration, values are
unordered even if an equality operator visible to the declaration considers them equal:
\s{compare() == 0} only agrees with equals() if the less than operator was visible.
\since 6.0
\sa equals(), isOrdered()
*/
std::optional<int> QMetaType::compare(const void *lhs, const void *rhs) const
{
if (!lhs || !rhs)
return std::optional<int>{};
if (d_ptr && d_ptr->lessThan) {
if (d_ptr->equals && d_ptr->equals(d_ptr, lhs, rhs))
return 0;
if (d_ptr->lessThan(d_ptr, lhs, rhs))
return -1;
if (d_ptr->lessThan(d_ptr, rhs, lhs))
return 1;
if (!d_ptr->equals)
return 0;
}
return std::optional<int>{};
}
/*!
Compares the objects at \a lhs and \a rhs for equality.
Both objects must be of the type described by this metatype. Can only compare the
two objects if a less than or equality operator for the type was visible to the
metatype declaration. Otherwise, the metatype never considers values equal. When
an equality operator was visible to the metatype declaration, it is authoritative;
otherwise, if less than is visible, when neither value is less than the other, the
two are considered equal. If values are unordered (see compare() for details) they
are not equal.
Returns true if the two objects compare equal, otherwise false.
\since 6.0
\sa isEqualityComparable(), compare()
*/
bool QMetaType::equals(const void *lhs, const void *rhs) const
{
if (!lhs || !rhs)
return false;
if (d_ptr) {
if (d_ptr->equals)
return d_ptr->equals(d_ptr, lhs, rhs);
if (d_ptr->lessThan && !d_ptr->lessThan(d_ptr, lhs, rhs) && !d_ptr->lessThan(d_ptr, rhs, lhs))
return true;
}
return false;
}
/*!
Returns \c true if a less than or equality operator for the type described by
this metatype was visible to the metatype declaration, otherwise \c false.
\sa equals(), isOrdered()
*/
bool QMetaType::isEqualityComparable() const
{
return d_ptr && (d_ptr->equals != nullptr || d_ptr->lessThan != nullptr);
}
/*!
Returns \c true if a less than operator for the type described by this metatype
was visible to the metatype declaration, otherwise \c false.
\sa compare(), isEqualityComparable()
*/
bool QMetaType::isOrdered() const
{
return d_ptr && d_ptr->lessThan != nullptr;
}
void QtMetaTypePrivate::derefAndDestroy(NS(QtPrivate::QMetaTypeInterface) *d_ptr)
{
if (d_ptr && !d_ptr->ref.deref()) {
@ -789,13 +881,10 @@ private:
typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractConverterFunction,QPair<int,int> >
QMetaTypeConverterRegistry;
typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractComparatorFunction,int>
QMetaTypeComparatorRegistry;
typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractDebugStreamFunction,int>
QMetaTypeDebugStreamRegistry;
Q_GLOBAL_STATIC(QMetaTypeConverterRegistry, customTypesConversionRegistry)
Q_GLOBAL_STATIC(QMetaTypeComparatorRegistry, customTypesComparatorRegistry)
Q_GLOBAL_STATIC(QMetaTypeDebugStreamRegistry, customTypesDebugStreamRegistry)
/*!
@ -829,22 +918,6 @@ Q_GLOBAL_STATIC(QMetaTypeDebugStreamRegistry, customTypesDebugStreamRegistry)
to type To in the meta type system. Returns \c true if the registration succeeded, otherwise false.
*/
/*!
\fn bool QMetaType::registerComparators()
\since 5.2
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()
@ -883,30 +956,6 @@ 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 \c true, if the meta type system has registered comparators for type T.
\since 5.2
*/
/*!
Returns \c 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)
@ -948,44 +997,24 @@ bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId
}
/*!
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.
\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 if (f->lessThan)
*result = f->lessThan(f, lhs, rhs) ? -1 : 1;
else
return false;
return true;
}
/*!
bool QMetaType::equals(const void *lhs, const void *rhs, int typeId, int *result)
\deprecated Use the non-static equals 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 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;
}
/*!
Streams the object at \a rhs of type \a typeId to the debug stream \a dbg. Returns \c true

View File

@ -56,6 +56,7 @@
#include <vector>
#include <list>
#include <map>
#include <optional>
#ifdef Bool
#error qmetatype.h must be included before any header file that defines Bool
@ -296,62 +297,6 @@ struct BuiltInDebugStreamFunction : public AbstractDebugStreamFunction
};
#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 = nullptr, Equals e = nullptr, Destroy d = nullptr)
: 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);
}
};
template<typename T>
struct BuiltInEqualsComparatorFunction : public AbstractComparatorFunction
{
BuiltInEqualsComparatorFunction()
: AbstractComparatorFunction(nullptr, 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*);
@ -569,6 +514,11 @@ public:
void destroy(void *data) const;
void *construct(void *where, const void *copy = nullptr) const;
void destruct(void *data) const;
std::optional<int> compare(const void *lhs, const void *rhs) const;
bool equals(const void *lhs, const void *rhs) const;
bool isEqualityComparable() const;
bool isOrdered() const;
template<typename T>
static QMetaType fromType();
@ -577,33 +527,6 @@ public:
friend bool operator!=(const QMetaType &a, const QMetaType &b) { return !(a == b); }
public:
template<typename T>
static bool registerComparators()
{
static_assert((!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 registerEqualsComparator()
{
static_assert((!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()
{
return hasRegisteredComparators(qMetaTypeId<T>());
}
static bool hasRegisteredComparators(int typeId);
#ifndef QT_NO_DEBUG_STREAM
template<typename T>
@ -680,8 +603,30 @@ 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 equals(const void *lhs, const void *rhs, int typeId, int* result);
#if QT_DEPRECATED_SINCE(6, 0)
QT_DEPRECATED_VERSION_6_0
static bool compare(const void *lhs, const void *rhs, int typeId, int *result)
{
QMetaType t(typeId);
auto c = t.compare(lhs, rhs);
if (!c) {
*result = 0;
return false;
}
*result = *c;
return true;
}
QT_DEPRECATED_VERSION_6_0
static bool equals(const void *lhs, const void *rhs, int typeId, int *result)
{
QMetaType t(typeId);
if (!t.isEqualityComparable())
return false;
*result = t.equals(lhs, rhs) ? 0 : -1;
return true;
}
#endif
static bool debugStream(QDebug& dbg, const void *rhs, int typeId);
template<typename From, typename To>
@ -692,7 +637,6 @@ public:
static bool hasRegisteredConverterFunction(int fromTypeId, int toTypeId);
static bool registerComparatorFunction(const QtPrivate::AbstractComparatorFunction *f, int type);
#ifndef QT_NO_DEBUG_STREAM
static bool registerDebugStreamOperatorFunction(const QtPrivate::AbstractDebugStreamFunction *f, int type);
#endif
@ -2297,6 +2241,10 @@ public:
MoveCtrFn moveCtr;
using DtorFn = void (*)(const QMetaTypeInterface *, void *);
DtorFn dtor;
using EqualsFn = bool (*)(const QMetaTypeInterface *, const void *, const void *);
EqualsFn equals;
using LessThanFn = bool (*)(const QMetaTypeInterface *, const void *, const void *);
LessThanFn lessThan;
using LegacyRegisterOp = void (*)();
LegacyRegisterOp legacyRegisterOp;
@ -2679,6 +2627,32 @@ struct BuiltinMetaType<T, std::enable_if_t<QMetaTypeId2<T>::IsBuiltIn>>
{
};
template<typename T, bool = QTypeTraits::has_operator_equal_v<T>>
struct QEqualityOperatorForType
{
static bool equals(const QMetaTypeInterface *, const void *a, const void *b)
{ return *reinterpret_cast<const T *>(a) == *reinterpret_cast<const T *>(b); }
};
template<typename T>
struct QEqualityOperatorForType <T, false>
{
static constexpr QMetaTypeInterface::EqualsFn equals = nullptr;
};
template<typename T, bool = QTypeTraits::has_operator_less_than_v<T>>
struct QLessThanOperatorForType
{
static bool lessThan(const QMetaTypeInterface *, const void *a, const void *b)
{ return *reinterpret_cast<const T *>(a) < *reinterpret_cast<const T *>(b); }
};
template<typename T>
struct QLessThanOperatorForType <T, false>
{
static constexpr QMetaTypeInterface::LessThanFn lessThan = nullptr;
};
template<typename S>
class QMetaTypeForType
{
@ -2765,6 +2739,8 @@ QMetaTypeInterface QMetaTypeForType<T>::metaType = {
/*.copyCtr=*/ getCopyCtr<T>(),
/*.moveCtr=*/ getMoveCtr<T>(),
/*.dtor=*/ getDtor<T>(),
/*.equals=*/ QEqualityOperatorForType<T>::equals,
/*.lessThan=*/ QLessThanOperatorForType<T>::lessThan,
/*.legacyRegisterOp=*/ getLegacyRegister<T>()
};
@ -2792,6 +2768,8 @@ public:
/*.copyCtr=*/ nullptr,
/*.moveCtr=*/ nullptr,
/*.dtor=*/ nullptr,
/*.equals=*/ nullptr,
/*.lessThan=*/ nullptr,
/*.legacyRegisterOp=*/ nullptr
};
};

View File

@ -123,11 +123,10 @@ bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged()
QVariant resultVariant(metaType.id(), nullptr);
evalError = evaluationFunction(metaType, resultVariant.data());
if (evalError.type() == QPropertyBindingError::NoError) {
int compareResult = 0;
bool updateAllowed = true;
if (hasStaticObserver && staticGuardCallback)
updateAllowed = staticGuardCallback(staticObserver, resultVariant.data());
if (updateAllowed && (!QMetaType::compare(propertyDataPtr, resultVariant.constData(), metaType.id(), &compareResult) || compareResult != 0)) {
if (updateAllowed && !metaType.equals(propertyDataPtr, resultVariant.constData())) {
changed = true;
metaType.destruct(propertyDataPtr);
metaType.construct(propertyDataPtr, resultVariant.constData());

View File

@ -3738,21 +3738,14 @@ bool QVariant::convert(const int type, void *ptr) const
The result of the function is not affected by the result of QVariant::isNull,
which means that two values can be equal even if one of them is null and
another is not.
\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)
\relates QVariant
Returns \c false if \a v1 and \a v2 are equal; otherwise returns \c true.
\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
@ -3764,10 +3757,6 @@ bool QVariant::convert(const int type, void *ptr) const
check for equality. QVariant will try to convert() \a v if its
type is not the same as this variant's type. See canConvert() for
a list of possible conversions.
\warning To make this function work with a custom type registered with
qRegisterMetaType(), its comparison operator must be registered using
QMetaType::registerComparators().
*/
/*!
@ -3775,10 +3764,6 @@ bool QVariant::convert(const int type, void *ptr) const
Compares this QVariant with \a v and returns \c true if they are not
equal; otherwise returns \c false.
\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)
@ -3928,12 +3913,9 @@ bool QVariant::cmp(const QVariant &v) const
{
auto cmp_helper = [](const QVariant::Private &d1, const QVariant::Private &d2) {
Q_ASSERT(d1.type() == d2.type());
if (d1.type().id() >= QMetaType::User) {
int result;
if (QMetaType::equals(QT_PREPEND_NAMESPACE(constData(d1)),
QT_PREPEND_NAMESPACE(constData(d2)), d1.type().id(), &result))
return result == 0;
}
auto metatype = d1.type();
if (metatype.id() >= QMetaType::User)
return metatype.equals(QT_PREPEND_NAMESPACE(constData(d1)), QT_PREPEND_NAMESPACE(constData(d2)));
return handlerManager[d1.type().id()]->compare(&d1, &d2);
};

View File

@ -139,7 +139,7 @@ static int registerComplexDBusType(const QByteArray &typeName)
[](QtPrivate::QMetaTypeInterface *self) {
delete static_cast<QDBusRawTypeHandler *>(self);
},
nullptr, nullptr, nullptr, nullptr, nullptr
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
},
name(name)
{}

View File

@ -249,9 +249,6 @@ QPageSetupWidget::QPageSetupWidget(QWidget *parent)
{
m_ui.setupUi(this);
if (!QMetaType::hasRegisteredComparators<QPageSize>())
QMetaType::registerEqualsComparator<QPageSize>();
QVBoxLayout *lay = new QVBoxLayout(m_ui.preview);
m_pagePreview = new QPagePreview(m_ui.preview);
m_pagePreview->setPagePreviewLayout(1, 1);

View File

@ -411,6 +411,8 @@ void tst_QMetaType::registerGadget(const char *name, const QList<GadgetPropertyT
[](const TypeInfo *self, void *where, const void *copy) { GadgetTypedConstructor(self->typeId, where, copy); },
[](const TypeInfo *self, void *where, void *copy) { GadgetTypedConstructor(self->typeId, where, copy); },
[](const TypeInfo *self, void *ptr) { GadgetTypedDestructor(self->typeId, ptr); },
nullptr,
nullptr,
nullptr };
QMetaType gadgetMetaType(typeInfo);
dynamicGadgetProperties->m_metatype = gadgetMetaType;
@ -1277,7 +1279,7 @@ void tst_QMetaType::typedConstruct()
[](const TypeInfo *self, void *where, const void *copy) { GadgetTypedConstructor(self->typeId, where, copy); },
[](const TypeInfo *self, void *where, void *copy) { GadgetTypedConstructor(self->typeId, where, copy); },
[](const TypeInfo *self, void *ptr) { GadgetTypedDestructor(self->typeId, ptr); },
nullptr };
nullptr, nullptr, nullptr };
QMetaType metatype(typeInfo);
dynamicGadgetProperties->m_metatype = metatype;
int podTypeId = metatype.id();
@ -2252,6 +2254,9 @@ bool operator==(const CustomEqualsOnlyType &lhs, const CustomEqualsOnlyType &rhs
bool operator!=(const CustomEqualsOnlyType &lhs, const CustomEqualsOnlyType &rhs)
{ return !operator==(lhs, rhs); }
static_assert(QTypeTraits::has_operator_equal_v<CustomEqualsOnlyType>);
static_assert(!QTypeTraits::has_operator_less_than_v<CustomEqualsOnlyType>);
Q_DECLARE_METATYPE(CustomConvertibleType);
Q_DECLARE_METATYPE(CustomConvertibleType2);
Q_DECLARE_METATYPE(CustomDebugStreamableType);
@ -2497,9 +2502,7 @@ void tst_QMetaType::convertCustomType()
void tst_QMetaType::compareCustomEqualOnlyType()
{
int metaTypeId = qRegisterMetaType<CustomEqualsOnlyType>();
QMetaType::registerEqualsComparator<CustomEqualsOnlyType>();
int result;
QMetaType type = QMetaType::fromType<CustomEqualsOnlyType>();
CustomEqualsOnlyType val50(50);
CustomEqualsOnlyType val100(100);
@ -2517,52 +2520,26 @@ void tst_QMetaType::compareCustomEqualOnlyType()
QCOMPARE(variant100, variant100);
// 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);
auto cmp = type.compare(variant50.constData(), variant50.constData());
QVERIFY(!cmp);
bool equals = type.equals(variant50.constData(), variant50.constData());
QVERIFY(equals);
wasSuccess = QMetaType::compare(variant50.constData(), variant100.constData(),
metaTypeId, &result);
QVERIFY(!wasSuccess);
cmp = type.compare(variant100.constData(), variant100x.constData());
QVERIFY(!cmp);
equals = type.equals(variant100.constData(), variant100x.constData());
QVERIFY(equals);
// 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);
cmp = type.compare(variant50.constData(), variant100.constData());
QVERIFY(!cmp);
equals = type.equals(variant50.constData(), variant100.constData());
QVERIFY(!equals);
//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);
type = QMetaType::fromType<CustomMovable>();
equals = type.equals(&movable1, &movable2);
}
struct MessageHandlerCustom : public MessageHandler

View File

@ -1826,6 +1826,12 @@ struct MyType
{
++instanceCount;
}
MyType &operator=(const MyType &other)
{
number = other.number;
text = other.text;
return *this;
}
~MyType()
{
--instanceCount;
@ -1833,6 +1839,8 @@ struct MyType
int number;
const char *text;
};
bool operator==(const MyType &a, const MyType &b) { return a.number == b.number && a.text == b.text; }
static_assert(QTypeTraits::has_operator_equal_v<MyType>);
Q_DECLARE_METATYPE(MyType)
Q_DECLARE_METATYPE(MyType*)
@ -2837,28 +2845,43 @@ void tst_QVariant::invalidDate() const
struct WontCompare
{
int x,y,z,q,w,e,r,t;
int x;
};
Q_DECLARE_METATYPE(WontCompare);
struct WillCompare
{
int x;
};
bool operator==(const WillCompare &a, const WillCompare &b) { return a.x == b.x; }
Q_DECLARE_METATYPE(WillCompare);
void tst_QVariant::compareCustomTypes() const
{
qRegisterMetaType<WontCompare>("WontCompare");
{
WontCompare f1{0};
const QVariant variant1(QVariant::fromValue(f1));
WontCompare f1 = {};
f1.x = 0;
const QVariant variant1(QVariant::fromValue(f1));
WontCompare f2{1};
const QVariant variant2(QVariant::fromValue(f2));
WontCompare f2 = {};
f2.x = 1;
const QVariant variant2(QVariant::fromValue(f2));
/* No comparison operator exists. */
QVERIFY(variant1 != variant2);
QVERIFY(variant1 != variant1);
QVERIFY(variant2 != variant2);
}
{
WillCompare f1{0};
const QVariant variant1(QVariant::fromValue(f1));
/* We compare via memcmp. */
QVERIFY(variant1 != variant2);
QCOMPARE(variant1, variant1);
QCOMPARE(variant2, variant2);
WillCompare f2 {1};
const QVariant variant2(QVariant::fromValue(f2));
QVERIFY(variant1 != variant2);
QCOMPARE(variant1, variant1);
QCOMPARE(variant2, variant2);
}
}
void tst_QVariant::timeToDateTime() const
{
const QVariant val(QTime::currentTime());