From e1d77e0124b6b2504c6740d432fae76639e0af25 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Mon, 19 Mar 2012 19:36:52 +0100 Subject: [PATCH] Add support for QSharedPointer in Closure. --- ext/libclementine-common/core/closure.cpp | 3 ++ ext/libclementine-common/core/closure.h | 53 +++++++++++++++++++++++ tests/closure_test.cpp | 27 ++++++++++++ 3 files changed, 83 insertions(+) diff --git a/ext/libclementine-common/core/closure.cpp b/ext/libclementine-common/core/closure.cpp index 0688903de..f8e51274d 100644 --- a/ext/libclementine-common/core/closure.cpp +++ b/ext/libclementine-common/core/closure.cpp @@ -50,6 +50,9 @@ Closure::Closure(QObject* sender, Connect(sender, signal); } +Closure::~Closure() { +} + void Closure::Connect(QObject* sender, const char* signal) { bool success = connect(sender, signal, SLOT(Invoked())); Q_ASSERT(success); diff --git a/ext/libclementine-common/core/closure.h b/ext/libclementine-common/core/closure.h index 6f9c3626f..319155ba4 100644 --- a/ext/libclementine-common/core/closure.h +++ b/ext/libclementine-common/core/closure.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -62,6 +63,8 @@ class Closure : public QObject, boost::noncopyable { Closure(QObject* sender, const char* signal, std::tr1::function callback); + virtual ~Closure(); + private slots: void Invoked(); void Cleanup(); @@ -78,6 +81,46 @@ class Closure : public QObject, boost::noncopyable { boost::scoped_ptr val3_; }; +class SharedPointerWrapper { + public: + virtual ~SharedPointerWrapper() {} + virtual void* data() const = 0; +}; + +template +class SharedPointer : public SharedPointerWrapper { + public: + SharedPointer(QSharedPointer ptr) + : ptr_(ptr) { + } + void* data() const { + return ptr_.data(); + } + private: + QSharedPointer ptr_; +}; + +// For use with a QSharedPointer as a sender. +class SharedClosure : public Closure { + Q_OBJECT + + public: + SharedClosure(SharedPointerWrapper* sender, const char* signal, + QObject* receiver, const char* slot, + const ClosureArgumentWrapper* val0 = 0, + const ClosureArgumentWrapper* val1 = 0, + const ClosureArgumentWrapper* val2 = 0, + const ClosureArgumentWrapper* val3 = 0) + : Closure(reinterpret_cast(sender->data()), signal, + receiver, slot, + val0, val1, val2, val3), + shared_sender_(sender) { + } + + private: + boost::scoped_ptr shared_sender_; +}; + #define C_ARG(type, data) new ClosureArgument(data) Closure* NewClosure( @@ -86,6 +129,16 @@ Closure* NewClosure( QObject* receiver, const char* slot); +template +Closure* NewClosure( + QSharedPointer sender, + const char* signal, + QObject* receiver, + const char* slot) { + Q_ASSERT(sender.data()->metaObject()); // Static check for something QObject-like. + return new SharedClosure(new SharedPointer(sender), signal, receiver, slot); +} + template Closure* NewClosure( QObject* sender, diff --git a/tests/closure_test.cpp b/tests/closure_test.cpp index 80e3bf8e7..2cb3abb1d 100644 --- a/tests/closure_test.cpp +++ b/tests/closure_test.cpp @@ -1,6 +1,8 @@ #include "gtest/gtest.h" #include +#include +#include #include #include "core/closure.h" @@ -38,3 +40,28 @@ TEST_F(ClosureTest, ClosureDeletesSelf) { loop.exec(); EXPECT_EQ(1, spy.count()); } + +TEST_F(ClosureTest, ClosureDoesNotCrashWithSharedPointerSender) { + TestQObject receiver; + TestQObject* sender; + boost::scoped_ptr spy; + QPointer closure; + { + QSharedPointer sender_shared(new TestQObject); + sender = sender_shared.data(); + closure = QPointer(NewClosure( + sender_shared, SIGNAL(Emitted()), + &receiver, SLOT(Invoke()))); + spy.reset(new QSignalSpy(sender, SIGNAL(destroyed()))); + } + EXPECT_EQ(0, receiver.invoked()); + sender->Emit(); + EXPECT_EQ(1, receiver.invoked()); + + EXPECT_EQ(0, spy->count()); + QEventLoop loop; + QObject::connect(sender, SIGNAL(destroyed()), &loop, SLOT(quit())); + loop.exec(); + EXPECT_EQ(1, spy->count()); + EXPECT_TRUE(closure.isNull()); +}