From 90a68926f311ac5bb8f7c53ac8370f9ff7f12c08 Mon Sep 17 00:00:00 2001 From: Roman Pasechnik Date: Tue, 2 Jul 2013 21:35:21 +0300 Subject: Add QEnableSharedFromThis class It enables you to get a valid QSharedPointer instance to 'this', when all you have is 'this'. Task-number: QTBUG-7287 Change-Id: I3ed1c9c4d6b110fe02302312cc3c4a75e9d95a0c Reviewed-by: Richard J. Moore --- src/corelib/tools/qsharedpointer.cpp | 54 +++++ src/corelib/tools/qsharedpointer.h | 8 + src/corelib/tools/qsharedpointer_impl.h | 41 ++++ .../tools/qsharedpointer/tst_qsharedpointer.cpp | 223 +++++++++++++++++++++ 4 files changed, 326 insertions(+) diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index 58a9a021d0..180f45eb56 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -371,6 +371,43 @@ \sa QSharedPointer, QScopedPointer */ +/*! + \class QEnableSharedFromThis + \inmodule QtCore + \brief A base class that allows to obtain a QSharedPointer for an object already managed by a shared pointer + \since 5.4 + + You can inherit this class when you need to create a QSharedPointer + from any instance of a class -- for instance, from within the + object itself. The key point is that the "obvious" technique of + just returning QSharedPointer(this) can not be used, because + this winds up creating multiple distinct QSharedPointer objects + with separate reference counts. For this reason you must never + create more than one QSharedPointer from the same raw pointer. + + QEnableSharedFromThis defines two member functions called + sharedFromThis() that return a QSharedPointer and + QSharedPointer, depending on constness, to \c this: + + \code + class Y: public QEnableSharedFromThis + { + public: + QSharedPointer f() + { + return sharedFromThis(); + } + }; + + int main() + { + QSharedPointer p(new Y()); + QSharedPointer y = p->f(); + Q_ASSERT(p == y); // p and q must share ownership + } + \endcode +*/ + /*! \fn QSharedPointer::QSharedPointer() @@ -923,6 +960,23 @@ may have had to the pointer. */ +/*! + \fn QSharedPointer QEnableSharedFromThis::sharedFromThis() + \since 5.4 + + If \c this (that is, the subclass instance invoking this method) is being + managed by a QSharedPointer, returns a shared pointer instance pointing to + \c this; otherwise returns a QSharedPointer holding a null pointer. +*/ + +/*! + \fn QSharedPointer QEnableSharedFromThis::sharedFromThis() const + \overload + \since 5.4 + + Const overload of sharedFromThis(). +*/ + /*! \fn bool operator==(const QSharedPointer &ptr1, const QSharedPointer &ptr2) \relates QSharedPointer diff --git a/src/corelib/tools/qsharedpointer.h b/src/corelib/tools/qsharedpointer.h index d9de48b7f4..431868ee08 100644 --- a/src/corelib/tools/qsharedpointer.h +++ b/src/corelib/tools/qsharedpointer.h @@ -131,6 +131,14 @@ public: QSharedPointer lock() const; }; +template +class QEnableSharedFromThis +{ +public: + QSharedPointer sharedFromThis(); + QSharedPointer sharedFromThis() const; +}; + template bool operator==(const QSharedPointer &ptr1, const QSharedPointer &ptr2); template bool operator!=(const QSharedPointer &ptr1, const QSharedPointer &ptr2); template bool operator==(const QSharedPointer &ptr1, const X *ptr2); diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 7dafa4981c..fe79aa1db1 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -91,6 +91,7 @@ template inline void qt_sharedpointer_cast_check(T *) { } // template class QWeakPointer; template class QSharedPointer; +template class QEnableSharedFromThis; class QVariant; @@ -479,6 +480,14 @@ private: delete d; } + template + inline void enableSharedFromThis(const QEnableSharedFromThis *ptr) + { + ptr->initializeFromSharedPointer(*this); + } + + inline void enableSharedFromThis(...) {} + template inline void internalConstruct(T *ptr, Deleter deleter) { @@ -499,6 +508,7 @@ private: internalSafetyCheckAdd(d, ptr); #endif d->setQObjectShared(ptr, true); + enableSharedFromThis(ptr); } template @@ -707,6 +717,37 @@ public: T *value; }; +template +class QEnableSharedFromThis +{ +protected: +#ifdef Q_COMPILER_DEFAULT_MEMBERS + QEnableSharedFromThis() = default; +#else + Q_DECL_CONSTEXPR QEnableSharedFromThis() {} +#endif + QEnableSharedFromThis(const QEnableSharedFromThis &) {} + QEnableSharedFromThis &operator=(const QEnableSharedFromThis &) { return *this; } + +public: + inline QSharedPointer sharedFromThis() { return QSharedPointer(weakPointer); } + inline QSharedPointer sharedFromThis() const { return QSharedPointer(weakPointer); } + +#ifndef Q_NO_TEMPLATE_FRIENDS +private: + template friend class QSharedPointer; +#else +public: +#endif + template + inline void initializeFromSharedPointer(const QSharedPointer &ptr) const + { + weakPointer = ptr; + } + + mutable QWeakPointer weakPointer; +}; + // // operator== and operator!= // diff --git a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp index 9bcce60e93..ac37b9af2a 100644 --- a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp @@ -112,6 +112,7 @@ private slots: void invalidConstructs(); void qvariantCast(); + void sharedFromThis(); public slots: void cleanup() { safetyCheck(); } @@ -2141,6 +2142,228 @@ void tst_QSharedPointer::qvariantCast() } } +class SomeClass : public QEnableSharedFromThis +{ +public: + SomeClass() + { + } + + QSharedPointer getSharedPtr() + { + return sharedFromThis(); + } + + QSharedPointer getSharedPtr() const + { + return sharedFromThis(); + } + + Data data; +}; + +void tst_QSharedPointer::sharedFromThis() +{ + const int generations = Data::generationCounter; + const int destructions = Data::destructorCounter; + + { + SomeClass sc; + QSharedPointer scp = sc.sharedFromThis(); + QVERIFY(scp.isNull()); + QCOMPARE(Data::generationCounter, generations + 1); + QCOMPARE(Data::destructorCounter, destructions); + + QSharedPointer const_scp = sc.sharedFromThis(); + QVERIFY(const_scp.isNull()); + QCOMPARE(Data::generationCounter, generations + 1); + QCOMPARE(Data::destructorCounter, destructions); + } + + QCOMPARE(Data::generationCounter, generations + 1); + QCOMPARE(Data::destructorCounter, destructions + 1); + + { + const SomeClass sc; + QSharedPointer const_scp = sc.sharedFromThis(); + QVERIFY(const_scp.isNull()); + QCOMPARE(Data::generationCounter, generations + 2); + QCOMPARE(Data::destructorCounter, destructions + 1); + } + + QCOMPARE(Data::generationCounter, generations + 2); + QCOMPARE(Data::destructorCounter, destructions + 2); + + { + SomeClass *sc = new SomeClass; + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + QSharedPointer scp; + QVERIFY(scp.isNull()); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + scp = sc->sharedFromThis(); + QVERIFY(scp.isNull()); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + scp = QSharedPointer(sc); + QVERIFY(!scp.isNull()); + QCOMPARE(scp.data(), sc); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + QSharedPointer scp2; + QVERIFY(scp2.isNull()); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + scp2 = sc->sharedFromThis(); + QVERIFY(!scp2.isNull()); + QVERIFY(scp == scp2); + QCOMPARE(scp2.data(), sc); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + QSharedPointer scp3; + QVERIFY(scp3.isNull()); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + scp3 = sc->sharedFromThis(); + QVERIFY(!scp3.isNull()); + QVERIFY(scp == scp3); + QVERIFY(scp2 == scp3); + QCOMPARE(scp3.data(), sc); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + QSharedPointer scp4; + QVERIFY(scp4.isNull()); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + scp4 = sc->getSharedPtr(); + QVERIFY(!scp4.isNull()); + QVERIFY(scp == scp4); + QVERIFY(scp2 == scp4); + QVERIFY(scp3 == scp4); + QCOMPARE(scp4.data(), sc); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + QSharedPointer scp5; + QVERIFY(scp5.isNull()); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + + scp5 = const_cast(sc)->getSharedPtr(); + QVERIFY(!scp4.isNull()); + QVERIFY(scp == scp5); + QVERIFY(scp2 == scp5); + QVERIFY(scp3 == scp5); + QVERIFY(scp4 == scp5); + QCOMPARE(scp5.data(), sc); + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 2); + } + + QCOMPARE(Data::generationCounter, generations + 3); + QCOMPARE(Data::destructorCounter, destructions + 3); + + QSharedPointer scp; + + QVERIFY(scp.isNull()); + + { + QSharedPointer scp2(new SomeClass()); + QVERIFY(!scp2.isNull()); + + scp = scp2->sharedFromThis(); + QVERIFY(!scp.isNull()); + + QVERIFY(scp == scp2); + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + } + + + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + QVERIFY(!scp.isNull()); + + { + QSharedPointer scp2; + scp2 = scp->sharedFromThis(); + QVERIFY(!scp2.isNull()); + + QVERIFY(scp == scp2); + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + } + + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + QVERIFY(!scp.isNull()); + + { + QSharedPointer scp2; + scp2 = scp->getSharedPtr(); + QVERIFY(!scp2.isNull()); + + QVERIFY(scp == scp2); + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + } + + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + QVERIFY(!scp.isNull()); + + { + QSharedPointer scp2; + scp2 = qSharedPointerConstCast(scp)->getSharedPtr(); + QVERIFY(!scp2.isNull()); + + QVERIFY(scp == scp2); + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + } + + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + QVERIFY(!scp.isNull()); + + { + QSharedPointer scp2; + scp2 = scp->sharedFromThis(); + QVERIFY(!scp2.isNull()); + + QVERIFY(scp == scp2); + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + + scp2.clear(); + + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + QVERIFY(!scp.isNull()); + QVERIFY(scp2.isNull()); + } + + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 3); + QVERIFY(!scp.isNull()); + + scp.clear(); + + QCOMPARE(Data::generationCounter, generations + 4); + QCOMPARE(Data::destructorCounter, destructions + 4); + +} + namespace ReentrancyWhileDestructing { struct IB { -- cgit v1.2.3