From e115c60b6da0db7013229e678720f36632c2e614 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Mon, 26 May 2025 13:22:07 +0200 Subject: [PATCH] Q(ObjectBindable)Property: Properly support comparisons Before, it was possible to do some comparisons due to the implicit conversion to T. However, that does not work in all contexts. Fix this by explicitly declaring the comparison operators. We need to a) use the helper macro directly, as the operartors are only available if the underlying type has them and b) mark the operator as noexcept(false), as capturing a property in a binding might allocate. Fixes: QTBUG-134921 Change-Id: I2855964ba481b9e7778a4a7076528593549910fe Reviewed-by: Ivan Solovev --- src/corelib/kernel/qproperty.cpp | 6 ++++ src/corelib/kernel/qproperty.h | 32 +++++++++++++++++ .../corelib/kernel/qproperty/CMakeLists.txt | 1 + .../kernel/qproperty/tst_qproperty.cpp | 36 +++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index fdb82baf86e..384253265be 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -1277,6 +1277,9 @@ QString QPropertyBindingError::description() const \inmodule QtCore \brief The QProperty class is a template class that enables automatic property bindings. \since 6.0 + \compares equality + \compareswith equality T + \endcompareswith \ingroup tools @@ -1486,6 +1489,9 @@ QString QPropertyBindingError::description() const automatic property bindings for property data stored in QObject derived classes. \since 6.0 + \compares equality + \compareswith equality T + \endcompareswith \ingroup tools diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 8c4b13cf5e8..4a144911771 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -384,6 +384,10 @@ public: #endif ~QProperty() = default; + QT_DECLARE_EQUALITY_OPERATORS_HELPER(QProperty, QProperty, /* non-constexpr */, noexcept(false), template >* = nullptr>) + QT_DECLARE_EQUALITY_OPERATORS_HELPER(QProperty, T, /* non-constexpr */, noexcept(false), template >* = nullptr>) + QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(QProperty, T, /* non-constexpr */, noexcept(false), template >* = nullptr>) + parameter_type value() const { d.registerWithCurrentlyEvaluatingBinding(); @@ -504,6 +508,18 @@ public: const QtPrivate::QPropertyBindingData &bindingData() const { return d; } private: + template >* = nullptr> + friend bool comparesEqual(const QProperty &lhs, const QProperty &rhs) + { + return lhs.value() == rhs.value(); + } + + template >* = nullptr> + friend bool comparesEqual(const QProperty &lhs, const T &rhs) + { + return lhs.value() == rhs; + } + void notify() { d.notifyObservers(this); @@ -1068,6 +1084,10 @@ public: explicit QObjectBindableProperty(Functor &&f); #endif + QT_DECLARE_EQUALITY_OPERATORS_HELPER(QObjectBindableProperty, QObjectBindableProperty, /* non-constexpr */, noexcept(false), template >* = nullptr>) + QT_DECLARE_EQUALITY_OPERATORS_HELPER(QObjectBindableProperty, T, /* non-constexpr */, noexcept(false), template >* = nullptr>) + QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(QObjectBindableProperty, T, /* non-constexpr */, noexcept(false), template >* = nullptr>) + parameter_type value() const { qGetBindingStorage(owner())->registerDependency(this); @@ -1208,6 +1228,18 @@ public: return *storage->bindingData(const_cast(this), true); } private: + template >* = nullptr> + friend bool comparesEqual(const QObjectBindableProperty &lhs, const QObjectBindableProperty &rhs) + { + return lhs.value() == rhs.value(); + } + + template >* = nullptr> + friend bool comparesEqual(const QObjectBindableProperty &lhs, const T &rhs) + { + return lhs.value() == rhs; + } + void notify(const QtPrivate::QPropertyBindingData *binding) { if (binding) diff --git a/tests/auto/corelib/kernel/qproperty/CMakeLists.txt b/tests/auto/corelib/kernel/qproperty/CMakeLists.txt index 177465d2eec..04e5e73a65f 100644 --- a/tests/auto/corelib/kernel/qproperty/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qproperty/CMakeLists.txt @@ -16,4 +16,5 @@ qt_internal_add_test(tst_qproperty tst_qproperty.cpp LIBRARIES Qt::CorePrivate + Qt::TestPrivate ) diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index c85ac17ce9b..53a477edc22 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #if __has_include() && __cplusplus >= 202002L && !defined(Q_QDOC) #include @@ -82,6 +83,8 @@ private slots: void compatPropertyNoDobuleNotification(); void compatPropertySignals(); + void compareAgainstValueType(); + void noFakeDependencies(); #if QT_CONFIG(thread) void threadSafety(); @@ -1735,6 +1738,39 @@ void tst_QProperty::compatPropertySignals() QCOMPARE(arguments.at(0).toInt(), 42); } +struct CompareTestObject : QObject{ + Q_OBJECT +public: + CompareTestObject(const QVariantList &l) { varList = l; } + Q_OBJECT_BINDABLE_PROPERTY(CompareTestObject, QVariantList, varList) +}; + + +void tst_QProperty::compareAgainstValueType() +{ + { + // compile time checks + QTestPrivate::testEqualityOperatorsCompile>(); + QTestPrivate::testEqualityOperatorsCompile, QVariantList>(); + + using ObjectBindableProperty = decltype(std::declval().varList); + + QTestPrivate::testEqualityOperatorsCompile(); + QTestPrivate::testEqualityOperatorsCompile(); + } + + QVariantList vl {1, QString(), QByteArray {}}; + QProperty vlProp { vl }; + CompareTestObject o { vl }; + + QCOMPARE_EQ(vl, vlProp); + QCOMPARE_EQ(vl, o.varList); + + vl.pop_back(); + QCOMPARE_NE(vl, vlProp); + QCOMPARE_NE(vl, o.varList); +} + class FakeDependencyCreator : public QObject { Q_OBJECT