From 358745d7ded0492cc8c61fa46d7c0928f584c184 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Tue, 24 Oct 2023 21:13:36 +0200 Subject: [PATCH] QSP/QWP: introduce owner_before, owner_equal, owner_hash While at the moment we don't have aliasing support in QSharedPointer, introduce owner-based comparisons and hashing. This also fulfills some use cases in lieu of operator== for QWeakPointer (which got deprecated by bb23a05905d7dc0e416a646e40592436daa939f2). I'm using C++26/P1901's spelling of owner_equal, instead of Boost.SmartPtr's spelling (owner_equal*s*). Given the niche use case, the lack of interoperability with Qt's own containers, as well as the Standard comparison objects' semantics (std::owner_less, std::owner_equal), I don't think we should be giving these a Qt-ish name as it would be pretty useless. [ChangeLog][QtCore][QSharedPointer] Added owner_before, owner_equal, owner_hash. [ChangeLog][QtCore][QWeakPointer] Added owner_before, owner_equal, owner_hash. Done-with: Fabian Kosmale Change-Id: I8b792ae79f14cd518ba4a006edaa17786a8352a0 Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale --- src/corelib/tools/qsharedpointer.cpp | 43 ++++++ src/corelib/tools/qsharedpointer.h | 26 ++++ src/corelib/tools/qsharedpointer_impl.h | 34 +++++ .../qsharedpointer/tst_qsharedpointer.cpp | 136 ++++++++++++++++++ 4 files changed, 239 insertions(+) diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index 7ed3847182..6daf1e3fad 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -710,6 +710,49 @@ \snippet code/src_corelib_tools_qsharedpointer.cpp 7 */ +/*! + \fn template template bool QSharedPointer::owner_before(const QSharedPointer &other) const noexcept + \fn template template bool QSharedPointer::owner_before(const QWeakPointer &other) const noexcept + \fn template template bool QWeakPointer::owner_before(const QSharedPointer &other) const noexcept + \fn template template bool QWeakPointer::owner_before(const QWeakPointer &other) const noexcept + \since 6.7 + + Returns \c true if and only if this smart pointer precedes \a other + in an implementation-defined owner-based ordering. The ordering is such + that two smart pointers are considered equivalent if they are both + empty or if they both own the same object (even if their apparent type + and pointer are different). + + \sa owner_equal +*/ + +/*! + \fn template template bool QSharedPointer::owner_equal(const QSharedPointer &other) const noexcept + \fn template template bool QSharedPointer::owner_equal(const QWeakPointer &other) const noexcept + \fn template template bool QWeakPointer::owner_equal(const QSharedPointer &other) const noexcept + \fn template template bool QWeakPointer::owner_equal(const QWeakPointer &other) const noexcept + + \since 6.7 + + Returns \c true if and only if this smart pointer and \a other + share ownership. + + \sa owner_before, owner_hash +*/ + +/*! + \fn template size_t QSharedPointer::owner_hash() const noexcept + \fn template size_t QWeakPointer::owner_hash() const noexcept + + \since 6.7 + + Returns a owner-based hash value for this smart pointer object. + Smart pointers that compare equal (as per \c{owner_equal}) will + have an identical owner-based hash. + + \sa owner_equal +*/ + /*! \fn template QWeakPointer::QWeakPointer() diff --git a/src/corelib/tools/qsharedpointer.h b/src/corelib/tools/qsharedpointer.h index 2a60f3ca5e..116c9afa00 100644 --- a/src/corelib/tools/qsharedpointer.h +++ b/src/corelib/tools/qsharedpointer.h @@ -72,6 +72,19 @@ public: template static inline QSharedPointer create(Args &&... args); + + // owner-based comparisons + template + bool owner_before(const QSharedPointer &other) const noexcept; + template + bool owner_before(const QWeakPointer &other) const noexcept; + + template + bool owner_equal(const QSharedPointer &other) const noexcept; + template + bool owner_equal(const QWeakPointer &other) const noexcept; + + size_t owner_hash() const noexcept; }; template @@ -108,6 +121,19 @@ public: QSharedPointer toStrongRef() const; QSharedPointer lock() const; + + // owner-based comparisons + template + bool owner_before(const QWeakPointer &other) const noexcept; + template + bool owner_before(const QSharedPointer &other) const noexcept; + + template + bool owner_equal(const QWeakPointer &other) const noexcept; + template + bool owner_equal(const QSharedPointer &other) const noexcept; + + size_t owner_hash() const noexcept; }; template diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 5dfc4614f9..73e39f0b91 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -444,6 +444,23 @@ public: #undef DECLARE_TEMPLATE_COMPARE_SET #undef DECLARE_COMPARE_SET + template + bool owner_before(const QSharedPointer &other) const noexcept + { return std::less<>()(d, other.d); } + template + bool owner_before(const QWeakPointer &other) const noexcept + { return std::less<>()(d, other.d); } + + template + bool owner_equal(const QSharedPointer &other) const noexcept + { return d == other.d; } + template + bool owner_equal(const QWeakPointer &other) const noexcept + { return d == other.d; } + + size_t owner_hash() const noexcept + { return std::hash()(d); } + private: Q_NODISCARD_CTOR explicit QSharedPointer(Qt::Initialization) {} @@ -684,6 +701,23 @@ public: friend bool operator!=(std::nullptr_t, const QWeakPointer &p) { return !p.isNull(); } + template + bool owner_before(const QWeakPointer &other) const noexcept + { return std::less<>()(d, other.d); } + template + bool owner_before(const QSharedPointer &other) const noexcept + { return std::less<>()(d, other.d); } + + template + bool owner_equal(const QWeakPointer &other) const noexcept + { return d == other.d; } + template + bool owner_equal(const QSharedPointer &other) const noexcept + { return d == other.d; } + + size_t owner_hash() const noexcept + { return std::hash()(d); } + private: friend struct QtPrivate::EnableInternalData; template friend class QSharedPointer; diff --git a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp index 5c4514eeb4..93cab2f4d9 100644 --- a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp @@ -91,6 +91,7 @@ private slots: void invalidConstructs_data(); void invalidConstructs(); #endif + void ownerComparisons(); // let invalidConstructs be the last test, because it's the slowest; // add new tests above this block @@ -2841,5 +2842,140 @@ void tst_QSharedPointer::overloads() weakOverloaded.test(); } +void tst_QSharedPointer::ownerComparisons() +{ + using SP = QSharedPointer; + using WP = QWeakPointer; + +#define CHECK_EQ(a, b) \ + do { \ + QVERIFY(a.owner_equal(b)); \ + QVERIFY(b.owner_equal(a)); \ + QVERIFY(!a.owner_before(b)); \ + QVERIFY(!b.owner_before(a)); \ + QVERIFY(a.owner_hash() == b.owner_hash()); \ + } while (false) + +#define CHECK_NOT_EQ(a, b) \ + do { \ + QVERIFY(!a.owner_equal(b)); \ + QVERIFY(!b.owner_equal(a)); \ + QVERIFY(a.owner_before(b) || b.owner_before(a)); \ + } while (false) + + // null + { + SP sp1; + SP sp2; + WP wp1 = sp1; + WP wp2; + + CHECK_EQ(sp1, sp1); + CHECK_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_EQ(wp1, wp2); + CHECK_EQ(wp2, wp2); + } + + // same owner + { + SP sp1 = SP::create(123); + SP sp2 = sp1; + WP wp1 = sp1; + SP wp2 = sp2; + + CHECK_EQ(sp1, sp1); + CHECK_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_EQ(wp1, wp2); + } + + // owning vs null + { + SP sp1 = SP::create(123); + SP sp2; + WP wp1 = sp1; + WP wp2 = sp2; + + CHECK_EQ(sp1, sp1); + CHECK_NOT_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + } + + // different owners + { + SP sp1 = SP::create(123); + SP sp2 = SP::create(456); + WP wp1 = sp1; + WP wp2 = sp2; + + CHECK_EQ(sp1, sp1); + CHECK_NOT_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + } + + // reset vs. null + { + SP sp1 = SP::create(123); + SP sp2; + WP wp1 = sp1; + WP wp2; + + CHECK_EQ(sp1, sp1); + CHECK_NOT_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_NOT_EQ(sp1, wp2); + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + + sp1.reset(); + + CHECK_EQ(sp1, sp1); + CHECK_EQ(sp1, sp2); + CHECK_NOT_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + } + + // expired weak pointers + { + WP wp1 = SP::create(123); + WP wp2; + + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + } + + { + WP wp1 = SP::create(123); + WP wp2 = wp1; + + CHECK_EQ(wp1, wp1); + CHECK_EQ(wp1, wp2); + } + + { + WP wp1 = SP::create(123); + WP wp2 = SP::create(456); + + CHECK_EQ(wp1, wp1); + CHECK_EQ(wp2, wp2); + CHECK_NOT_EQ(wp1, wp2); + } +#undef CHECK_EQ +#undef CHECK_NOT_EQ +} + QTEST_MAIN(tst_QSharedPointer) #include "tst_qsharedpointer.moc"