QExplicitlySharedDataPointer: Use new comparison helper macros

Provide the new comparesEqual() helper function as an implementation of
the (in)equality operators and compareThreeWay() helper function for
the rest of the relational operators.
Use Q_DECLARE_STRONGLY_ORDERED to provide all relational operators.

Use the new Qt::totally_ordered_wrapper to wrap the "d" pointer to
avoid UB when performing comparisons of QExplicitlySharedDataPointer.

Add some comparisons related tests for QExplicitlySharedDataPointer.
Use QT_TEST_ALL_COMPARISON_OPS macros in unit-tests.

Task-number: QTBUG-120306
Change-Id: I275484a96a57da2df92712ac97e237a88a889da5
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Rym Bouabid 2024-04-03 18:52:59 +02:00
parent 7131240754
commit 793f3d8642
6 changed files with 97 additions and 47 deletions

View File

@ -443,6 +443,10 @@ QT_BEGIN_NAMESPACE
\since 4.4 \since 4.4
\reentrant \reentrant
\compares strong
\compareswith strong T* std::nullptr_t
\endcompareswith
QExplicitlySharedDataPointer\<T\> makes writing your own explicitly QExplicitlySharedDataPointer\<T\> makes writing your own explicitly
shared classes easy. QExplicitlySharedDataPointer implements shared classes easy. QExplicitlySharedDataPointer implements
\l {thread-safe} reference counting, ensuring that adding \l {thread-safe} reference counting, ensuring that adding
@ -520,8 +524,8 @@ QT_BEGIN_NAMESPACE
\since 5.2 \since 5.2
*/ */
/*! \fn template <class T> bool QExplicitlySharedDataPointer<T>::operator==(const T* ptr, const QExplicitlySharedDataPointer<T>& rhs) /*! \fn template <class T> bool QExplicitlySharedDataPointer<T>::operator==(const T* const &lhs, const QExplicitlySharedDataPointer<T>& rhs)
Returns \c true if the \e{d pointer} of \a rhs is \a ptr. Returns \c true if the \e{d pointer} of \a rhs is \a lhs.
*/ */
/*! \fn template <class T> bool QExplicitlySharedDataPointer<T>::operator!=(const QExplicitlySharedDataPointer<T>& lhs, const QExplicitlySharedDataPointer<T>& rhs) /*! \fn template <class T> bool QExplicitlySharedDataPointer<T>::operator!=(const QExplicitlySharedDataPointer<T>& lhs, const QExplicitlySharedDataPointer<T>& rhs)
@ -529,8 +533,8 @@ QT_BEGIN_NAMESPACE
\e{d pointer}. \e{d pointer}.
*/ */
/*! \fn template <class T> bool QExplicitlySharedDataPointer<T>::operator!=(const T* ptr, const QExplicitlySharedDataPointer<T>& rhs) /*! \fn template <class T> bool QExplicitlySharedDataPointer<T>::operator!=(const T* const &lhs, const QExplicitlySharedDataPointer<T>& rhs)
Returns \c true if the \e{d pointer} of \a rhs is \e not \a ptr. Returns \c true if the \e{d pointer} of \a rhs is \e not \a lhs.
*/ */
/*! \fn template <class T> QExplicitlySharedDataPointer<T>::QExplicitlySharedDataPointer() /*! \fn template <class T> QExplicitlySharedDataPointer<T>::QExplicitlySharedDataPointer()

View File

@ -134,21 +134,21 @@ public:
typedef T Type; typedef T Type;
typedef T *pointer; typedef T *pointer;
T &operator*() const { return *d; } T &operator*() const { return *(d.get()); }
T *operator->() noexcept { return d; } T *operator->() noexcept { return d.get(); }
T *operator->() const noexcept { return d; } T *operator->() const noexcept { return d.get(); }
explicit operator T *() { return d; } explicit operator T *() { return d.get(); }
explicit operator const T *() const noexcept { return d; } explicit operator const T *() const noexcept { return d.get(); }
T *data() const noexcept { return d; } T *data() const noexcept { return d.get(); }
T *get() const noexcept { return d; } T *get() const noexcept { return d.get(); }
const T *constData() const noexcept { return d; } const T *constData() const noexcept { return d.get(); }
T *take() noexcept { return std::exchange(d, nullptr); } T *take() noexcept { return std::exchange(d, nullptr).get(); }
void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }
Q_NODISCARD_CTOR Q_NODISCARD_CTOR
QExplicitlySharedDataPointer() noexcept : d(nullptr) { } QExplicitlySharedDataPointer() noexcept : d(nullptr) { }
~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d; } ~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d.get(); }
Q_NODISCARD_CTOR Q_NODISCARD_CTOR
explicit QExplicitlySharedDataPointer(T *data) noexcept : d(data) explicit QExplicitlySharedDataPointer(T *data) noexcept : d(data)
@ -174,7 +174,7 @@ public:
if (ptr != d) { if (ptr != d) {
if (ptr) if (ptr)
ptr->ref.ref(); ptr->ref.ref();
T *old = std::exchange(d, ptr); T *old = std::exchange(d, Qt::totally_ordered_wrapper(ptr)).get();
if (old && !old->ref.deref()) if (old && !old->ref.deref())
delete old; delete old;
} }
@ -182,7 +182,7 @@ public:
QExplicitlySharedDataPointer &operator=(const QExplicitlySharedDataPointer &o) noexcept QExplicitlySharedDataPointer &operator=(const QExplicitlySharedDataPointer &o) noexcept
{ {
reset(o.d); reset(o.d.get());
return *this; return *this;
} }
QExplicitlySharedDataPointer &operator=(T *o) noexcept QExplicitlySharedDataPointer &operator=(T *o) noexcept
@ -200,35 +200,36 @@ public:
void swap(QExplicitlySharedDataPointer &other) noexcept void swap(QExplicitlySharedDataPointer &other) noexcept
{ qt_ptr_swap(d, other.d); } { qt_ptr_swap(d, other.d); }
#define DECLARE_COMPARE_SET(T1, A1, T2, A2) \
friend bool operator<(T1, T2) noexcept \
{ return std::less<T*>{}(A1, A2); } \
friend bool operator<=(T1, T2) noexcept \
{ return !std::less<T*>{}(A2, A1); } \
friend bool operator>(T1, T2) noexcept \
{ return std::less<T*>{}(A2, A1); } \
friend bool operator>=(T1, T2) noexcept \
{ return !std::less<T*>{}(A1, A2); } \
friend bool operator==(T1, T2) noexcept \
{ return A1 == A2; } \
friend bool operator!=(T1, T2) noexcept \
{ return A1 != A2; } \
DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, const QExplicitlySharedDataPointer &p2, p2.d)
DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, const T *ptr, ptr)
DECLARE_COMPARE_SET(const T *ptr, ptr, const QExplicitlySharedDataPointer &p2, p2.d)
DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, std::nullptr_t, nullptr)
DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QExplicitlySharedDataPointer &p2, p2.d)
#undef DECLARE_COMPARE_SET
protected: protected:
T *clone(); T *clone();
private: private:
friend bool comparesEqual(const QExplicitlySharedDataPointer &lhs,
const QExplicitlySharedDataPointer &rhs) noexcept
{ return lhs.d == rhs.d; }
friend Qt::strong_ordering
compareThreeWay(const QExplicitlySharedDataPointer &lhs,
const QExplicitlySharedDataPointer &rhs) noexcept
{ return Qt::compareThreeWay(lhs.d, rhs.d); }
Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer)
friend bool comparesEqual(const QExplicitlySharedDataPointer &lhs, const T *rhs) noexcept
{ return lhs.d == rhs; }
friend Qt::strong_ordering
compareThreeWay(const QExplicitlySharedDataPointer &lhs, const T *rhs) noexcept
{ return Qt::compareThreeWay(lhs.d, rhs); }
Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer, const T*)
friend bool comparesEqual(const QExplicitlySharedDataPointer &lhs, std::nullptr_t) noexcept
{ return lhs.d == nullptr; }
friend Qt::strong_ordering
compareThreeWay(const QExplicitlySharedDataPointer &lhs, std::nullptr_t) noexcept
{ return Qt::compareThreeWay(lhs.d, nullptr); }
Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer, std::nullptr_t)
void detach_helper(); void detach_helper();
T *d; Qt::totally_ordered_wrapper<T *> d;
}; };
// Declared here and as Q_OUTOFLINE_TEMPLATE to work-around MSVC bug causing missing symbols at link time. // Declared here and as Q_OUTOFLINE_TEMPLATE to work-around MSVC bug causing missing symbols at link time.
@ -251,7 +252,7 @@ Q_OUTOFLINE_TEMPLATE void QSharedDataPointer<T>::detach_helper()
template <typename T> template <typename T>
Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone() Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone()
{ {
return new T(*d); return new T(*d.get());
} }
template <typename T> template <typename T>
@ -260,8 +261,8 @@ Q_OUTOFLINE_TEMPLATE void QExplicitlySharedDataPointer<T>::detach_helper()
T *x = clone(); T *x = clone();
x->ref.ref(); x->ref.ref();
if (!d->ref.deref()) if (!d->ref.deref())
delete d; delete d.get();
d = x; d.reset(x);
} }
template <typename T> template <typename T>
@ -309,7 +310,7 @@ template<typename T> Q_DECLARE_TYPEINFO_BODY(QExplicitlySharedDataPointer<T>, Q_
template<> QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer() \ template<> QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer() \
{ \ { \
if (d && !d->ref.deref()) \ if (d && !d->ref.deref()) \
delete d; \ delete d.get(); \
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -89,9 +89,7 @@ public:
QAtomicInt fd; QAtomicInt fd;
}; };
template<> inline QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QDBusUnixFileDescriptorPrivate)
QExplicitlySharedDataPointer<QDBusUnixFileDescriptorPrivate>::~QExplicitlySharedDataPointer()
{ if (d && !d->ref.deref()) delete d; }
/*! /*!
Constructs a QDBusUnixFileDescriptor without a wrapped file descriptor. Constructs a QDBusUnixFileDescriptor without a wrapped file descriptor.

View File

@ -802,7 +802,7 @@ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QHttpHeadersPrivate)
template <> void QExplicitlySharedDataPointer<QHttpHeadersPrivate>::detach() template <> void QExplicitlySharedDataPointer<QHttpHeadersPrivate>::detach()
{ {
if (!d) { if (!d) {
d = new QHttpHeadersPrivate(); d.reset(new QHttpHeadersPrivate());
d->ref.ref(); d->ref.ref();
} else if (d->ref.loadRelaxed() != 1) { } else if (d->ref.loadRelaxed() != 1) {
detach_helper(); detach_helper();

View File

@ -14,4 +14,6 @@ endif()
qt_internal_add_test(tst_qexplicitlyshareddatapointer qt_internal_add_test(tst_qexplicitlyshareddatapointer
SOURCES SOURCES
tst_qexplicitlyshareddatapointer.cpp tst_qexplicitlyshareddatapointer.cpp
LIBRARIES
Qt::TestPrivate
) )

View File

@ -3,6 +3,8 @@
#include <QTest> #include <QTest>
#include <QtTest/private/qcomparisontesthelper_p.h>
#include <QtCore/QSharedData> #include <QtCore/QSharedData>
/*! /*!
@ -24,11 +26,19 @@ private Q_SLOTS:
void data() const; void data() const;
void reset() const; void reset() const;
void swap() const; void swap() const;
void compareCompiles() const;
void compare() const;
}; };
class MyClass : public QSharedData class MyClass : public QSharedData
{ {
public: public:
MyClass() = default;
MyClass(int v) : m_value(v)
{
ref.ref();
};
int m_value;
void mutating() void mutating()
{ {
} }
@ -220,6 +230,41 @@ void tst_QExplicitlySharedDataPointer::swap() const
QVERIFY(!p2.data()); QVERIFY(!p2.data());
} }
void tst_QExplicitlySharedDataPointer::compareCompiles() const
{
QTestPrivate::testAllComparisonOperatorsCompile<QExplicitlySharedDataPointer<MyClass>>();
QTestPrivate::testAllComparisonOperatorsCompile<QExplicitlySharedDataPointer<MyClass>,
MyClass*>();
QTestPrivate::testAllComparisonOperatorsCompile<QExplicitlySharedDataPointer<MyClass>,
std::nullptr_t>();
}
void tst_QExplicitlySharedDataPointer::compare() const
{
const QExplicitlySharedDataPointer<MyClass> ptr;
const QExplicitlySharedDataPointer<MyClass> ptr2;
QT_TEST_ALL_COMPARISON_OPS(ptr, nullptr, Qt::strong_ordering::equal);
QT_TEST_ALL_COMPARISON_OPS(ptr2, nullptr, Qt::strong_ordering::equal);
QT_TEST_ALL_COMPARISON_OPS(ptr, ptr2, Qt::strong_ordering::equal);
const QExplicitlySharedDataPointer<MyClass> copy(ptr);
QT_TEST_ALL_COMPARISON_OPS(ptr, copy, Qt::strong_ordering::equal);
MyClass* pointer = nullptr;
QT_TEST_ALL_COMPARISON_OPS(pointer, ptr, Qt::strong_ordering::equal);
const QExplicitlySharedDataPointer<MyClass> pointer2(new MyClass());
QT_TEST_ALL_COMPARISON_OPS(pointer, pointer2, Qt::strong_ordering::less);
std::array<MyClass, 3> myArray {MyClass(2), MyClass(1), MyClass(0)};
const QExplicitlySharedDataPointer<const MyClass> val0(&myArray[0]);
const QExplicitlySharedDataPointer<const MyClass> val1(&myArray[1]);
QT_TEST_ALL_COMPARISON_OPS(val0, val1, Qt::strong_ordering::less);
QT_TEST_ALL_COMPARISON_OPS(val0, &myArray[1], Qt::strong_ordering::less);
QT_TEST_ALL_COMPARISON_OPS(val1, val0, Qt::strong_ordering::greater);
QT_TEST_ALL_COMPARISON_OPS(&myArray[1], val0, Qt::strong_ordering::greater);
}
QTEST_MAIN(tst_QExplicitlySharedDataPointer) QTEST_MAIN(tst_QExplicitlySharedDataPointer)
#include "tst_qexplicitlyshareddatapointer.moc" #include "tst_qexplicitlyshareddatapointer.moc"