summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoman Pasechnik <roman@pasechnik.net>2013-07-02 21:35:21 +0300
committerGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>2014-06-29 23:20:10 +0200
commit90a68926f311ac5bb8f7c53ac8370f9ff7f12c08 (patch)
treeec1f5305fb5cf8cc787c4749a95c06b06ab44ba8
parentbc19ab03b2e1ac71910ac5309e5ada90b5ffa9a5 (diff)
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 <rich@kde.org>
-rw-r--r--src/corelib/tools/qsharedpointer.cpp54
-rw-r--r--src/corelib/tools/qsharedpointer.h8
-rw-r--r--src/corelib/tools/qsharedpointer_impl.h41
-rw-r--r--tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp223
4 files changed, 326 insertions, 0 deletions
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
@@ -372,6 +372,43 @@
*/
/*!
+ \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<T>(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<T> and
+ QSharedPointer<const T>, depending on constness, to \c this:
+
+ \code
+ class Y: public QEnableSharedFromThis<Y>
+ {
+ public:
+ QSharedPointer<Y> f()
+ {
+ return sharedFromThis();
+ }
+ };
+
+ int main()
+ {
+ QSharedPointer<Y> p(new Y());
+ QSharedPointer<Y> y = p->f();
+ Q_ASSERT(p == y); // p and q must share ownership
+ }
+ \endcode
+*/
+
+/*!
\fn QSharedPointer::QSharedPointer()
Creates a QSharedPointer that points to null (0).
@@ -924,6 +961,23 @@
*/
/*!
+ \fn QSharedPointer<T> 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<const T> QEnableSharedFromThis::sharedFromThis() const
+ \overload
+ \since 5.4
+
+ Const overload of sharedFromThis().
+*/
+
+/*!
\fn bool operator==(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &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<T> lock() const;
};
+template <class T>
+class QEnableSharedFromThis
+{
+public:
+ QSharedPointer<T> sharedFromThis();
+ QSharedPointer<const T> sharedFromThis() const;
+};
+
template<class T, class X> bool operator==(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2);
template<class T, class X> bool operator!=(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2);
template<class T, class X> bool operator==(const QSharedPointer<T> &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<typename T> inline void qt_sharedpointer_cast_check(T *) { }
//
template <class T> class QWeakPointer;
template <class T> class QSharedPointer;
+template <class T> class QEnableSharedFromThis;
class QVariant;
@@ -479,6 +480,14 @@ private:
delete d;
}
+ template <class X>
+ inline void enableSharedFromThis(const QEnableSharedFromThis<X> *ptr)
+ {
+ ptr->initializeFromSharedPointer(*this);
+ }
+
+ inline void enableSharedFromThis(...) {}
+
template <typename Deleter>
inline void internalConstruct(T *ptr, Deleter deleter)
{
@@ -499,6 +508,7 @@ private:
internalSafetyCheckAdd(d, ptr);
#endif
d->setQObjectShared(ptr, true);
+ enableSharedFromThis(ptr);
}
template <class X>
@@ -707,6 +717,37 @@ public:
T *value;
};
+template <class T>
+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<T> sharedFromThis() { return QSharedPointer<T>(weakPointer); }
+ inline QSharedPointer<const T> sharedFromThis() const { return QSharedPointer<const T>(weakPointer); }
+
+#ifndef Q_NO_TEMPLATE_FRIENDS
+private:
+ template <class X> friend class QSharedPointer;
+#else
+public:
+#endif
+ template <class X>
+ inline void initializeFromSharedPointer(const QSharedPointer<X> &ptr) const
+ {
+ weakPointer = ptr;
+ }
+
+ mutable QWeakPointer<T> 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<SomeClass>
+{
+public:
+ SomeClass()
+ {
+ }
+
+ QSharedPointer<SomeClass> getSharedPtr()
+ {
+ return sharedFromThis();
+ }
+
+ QSharedPointer<const SomeClass> getSharedPtr() const
+ {
+ return sharedFromThis();
+ }
+
+ Data data;
+};
+
+void tst_QSharedPointer::sharedFromThis()
+{
+ const int generations = Data::generationCounter;
+ const int destructions = Data::destructorCounter;
+
+ {
+ SomeClass sc;
+ QSharedPointer<SomeClass> scp = sc.sharedFromThis();
+ QVERIFY(scp.isNull());
+ QCOMPARE(Data::generationCounter, generations + 1);
+ QCOMPARE(Data::destructorCounter, destructions);
+
+ QSharedPointer<const SomeClass> 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 SomeClass> 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<SomeClass> 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<SomeClass>(sc);
+ QVERIFY(!scp.isNull());
+ QCOMPARE(scp.data(), sc);
+ QCOMPARE(Data::generationCounter, generations + 3);
+ QCOMPARE(Data::destructorCounter, destructions + 2);
+
+ QSharedPointer<SomeClass> 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<const SomeClass> 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<SomeClass> 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<const SomeClass> scp5;
+ QVERIFY(scp5.isNull());
+ QCOMPARE(Data::generationCounter, generations + 3);
+ QCOMPARE(Data::destructorCounter, destructions + 2);
+
+ scp5 = const_cast<const SomeClass *>(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<SomeClass> scp;
+
+ QVERIFY(scp.isNull());
+
+ {
+ QSharedPointer<SomeClass> 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<const SomeClass> 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<SomeClass> 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<const SomeClass> scp2;
+ scp2 = qSharedPointerConstCast<const SomeClass>(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<SomeClass> 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
{