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 bb23a05905).

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 <fabian.kosmale@qt.io>
Change-Id: I8b792ae79f14cd518ba4a006edaa17786a8352a0
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Giuseppe D'Angelo 2023-10-24 21:13:36 +02:00
parent 1dcc14f2d0
commit 358745d7de
4 changed files with 239 additions and 0 deletions

View File

@ -710,6 +710,49 @@
\snippet code/src_corelib_tools_qsharedpointer.cpp 7
*/
/*!
\fn template <class T> template <class X> bool QSharedPointer<T>::owner_before(const QSharedPointer<X> &other) const noexcept
\fn template <class T> template <class X> bool QSharedPointer<T>::owner_before(const QWeakPointer<X> &other) const noexcept
\fn template <class T> template <class X> bool QWeakPointer<T>::owner_before(const QSharedPointer<X> &other) const noexcept
\fn template <class T> template <class X> bool QWeakPointer<T>::owner_before(const QWeakPointer<X> &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 <class T> template <class X> bool QSharedPointer<T>::owner_equal(const QSharedPointer<X> &other) const noexcept
\fn template <class T> template <class X> bool QSharedPointer<T>::owner_equal(const QWeakPointer<X> &other) const noexcept
\fn template <class T> template <class X> bool QWeakPointer<T>::owner_equal(const QSharedPointer<X> &other) const noexcept
\fn template <class T> template <class X> bool QWeakPointer<T>::owner_equal(const QWeakPointer<X> &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 <class T> size_t QSharedPointer<T>::owner_hash() const noexcept
\fn template <class T> size_t QWeakPointer<T>::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 <class T> QWeakPointer<T>::QWeakPointer()

View File

@ -72,6 +72,19 @@ public:
template <typename... Args>
static inline QSharedPointer<T> create(Args &&... args);
// owner-based comparisons
template <typename X>
bool owner_before(const QSharedPointer<X> &other) const noexcept;
template <typename X>
bool owner_before(const QWeakPointer<X> &other) const noexcept;
template <typename X>
bool owner_equal(const QSharedPointer<X> &other) const noexcept;
template <typename X>
bool owner_equal(const QWeakPointer<X> &other) const noexcept;
size_t owner_hash() const noexcept;
};
template <class T>
@ -108,6 +121,19 @@ public:
QSharedPointer<T> toStrongRef() const;
QSharedPointer<T> lock() const;
// owner-based comparisons
template <typename X>
bool owner_before(const QWeakPointer<X> &other) const noexcept;
template <typename X>
bool owner_before(const QSharedPointer<X> &other) const noexcept;
template <typename X>
bool owner_equal(const QWeakPointer<X> &other) const noexcept;
template <typename X>
bool owner_equal(const QSharedPointer<X> &other) const noexcept;
size_t owner_hash() const noexcept;
};
template <class T>

View File

@ -444,6 +444,23 @@ public:
#undef DECLARE_TEMPLATE_COMPARE_SET
#undef DECLARE_COMPARE_SET
template <typename X>
bool owner_before(const QSharedPointer<X> &other) const noexcept
{ return std::less<>()(d, other.d); }
template <typename X>
bool owner_before(const QWeakPointer<X> &other) const noexcept
{ return std::less<>()(d, other.d); }
template <typename X>
bool owner_equal(const QSharedPointer<X> &other) const noexcept
{ return d == other.d; }
template <typename X>
bool owner_equal(const QWeakPointer<X> &other) const noexcept
{ return d == other.d; }
size_t owner_hash() const noexcept
{ return std::hash<Data *>()(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 <typename X>
bool owner_before(const QWeakPointer<X> &other) const noexcept
{ return std::less<>()(d, other.d); }
template <typename X>
bool owner_before(const QSharedPointer<X> &other) const noexcept
{ return std::less<>()(d, other.d); }
template <typename X>
bool owner_equal(const QWeakPointer<X> &other) const noexcept
{ return d == other.d; }
template <typename X>
bool owner_equal(const QSharedPointer<X> &other) const noexcept
{ return d == other.d; }
size_t owner_hash() const noexcept
{ return std::hash<Data *>()(d); }
private:
friend struct QtPrivate::EnableInternalData;
template <class X> friend class QSharedPointer;

View File

@ -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<int>;
using WP = QWeakPointer<int>;
#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"