Port Closure to variadic templates.

This commit is contained in:
John Maguire 2012-11-22 15:15:07 +01:00
parent d6b84558f6
commit 8171192df5
11 changed files with 185 additions and 249 deletions

View File

@ -19,80 +19,47 @@
#include <QTimer>
#include "core/logging.h"
#include "core/timeconstants.h"
namespace _detail {
Closure::Closure(QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const ClosureArgumentWrapper* val0,
const ClosureArgumentWrapper* val1,
const ClosureArgumentWrapper* val2,
const ClosureArgumentWrapper* val3)
: QObject(receiver),
callback_(NULL),
val0_(val0),
val1_(val1),
val2_(val2),
val3_(val3) {
const QMetaObject* meta_receiver = receiver->metaObject();
QByteArray normalised_slot = QMetaObject::normalizedSignature(slot + 1);
const int index = meta_receiver->indexOfSlot(normalised_slot.constData());
Q_ASSERT(index != -1);
slot_ = meta_receiver->method(index);
Connect(sender, signal);
ClosureBase::ClosureBase(ObjectHelper* helper)
: helper_(helper) {
}
Closure::Closure(QObject* sender,
const char* signal,
std::tr1::function<void()> callback)
: callback_(callback) {
Connect(sender, signal);
ClosureBase::~ClosureBase() {
}
Closure::~Closure() {
ObjectHelper* ClosureBase::helper() const {
return helper_;
}
void Closure::Connect(QObject* sender, const char* signal) {
bool success = connect(sender, signal, SLOT(Invoked()));
Q_ASSERT(success);
success = connect(sender, SIGNAL(destroyed()), SLOT(Cleanup()));
Q_ASSERT(success);
Q_UNUSED(success);
ObjectHelper::ObjectHelper(
QObject* sender,
const char* signal,
ClosureBase* closure)
: closure_(closure) {
connect(sender, signal, SLOT(Invoked()));
connect(sender, SIGNAL(destroyed()), SLOT(deleteLater()));
}
void Closure::Invoked() {
if (callback_) {
callback_();
} else {
slot_.invoke(
parent(),
val0_ ? val0_->arg() : QGenericArgument(),
val1_ ? val1_->arg() : QGenericArgument(),
val2_ ? val2_->arg() : QGenericArgument(),
val3_ ? val3_->arg() : QGenericArgument());
}
void ObjectHelper::Invoked() {
closure_->Invoke();
deleteLater();
}
void Closure::Cleanup() {
disconnect();
deleteLater();
}
template<>
void Arg<boost::tuples::tuple<>>(
const boost::tuples::tuple<>&,
QList<QGenericArgument>* list) {}
template<>
void Arg<boost::tuples::null_type>(
const boost::tuples::null_type&,
QList<QGenericArgument>* list) {}
} // namespace _detail
_detail::Closure* NewClosure(
QObject* sender, const char* signal,
QObject* receiver, const char* slot) {
return new _detail::Closure(sender, signal, receiver, slot);
}
void DoAfter(QObject* receiver, const char* slot, int msec) {
QTimer::singleShot(msec, receiver, slot);
}

View File

@ -18,211 +18,169 @@
#ifndef CLOSURE_H
#define CLOSURE_H
#include <tr1/functional>
#include <QMetaMethod>
#include <QObject>
#include <QSharedPointer>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include "core/logging.h"
#include <boost/tuple/tuple.hpp>
namespace _detail {
class ClosureArgumentWrapper {
public:
virtual ~ClosureArgumentWrapper() {}
class ObjectHelper;
virtual QGenericArgument arg() const = 0;
// Interface for ObjectHelper to call on signal emission.
class ClosureBase : boost::noncopyable {
public:
virtual ~ClosureBase();
virtual void Invoke() = 0;
// Tests only.
ObjectHelper* helper() const;
protected:
explicit ClosureBase(ObjectHelper*);
ObjectHelper* helper_;
};
template<typename T>
class ClosureArgument : public ClosureArgumentWrapper {
public:
explicit ClosureArgument(const T& data) : data_(data) {}
virtual QGenericArgument arg() const {
return Q_ARG(T, data_);
}
private:
T data_;
};
class Closure : public QObject, boost::noncopyable {
// QObject helper as templated QObjects do not work.
// Connects to the given signal and invokes the closure when called.
// Deletes itself and the Closure after being invoked.
class ObjectHelper : public QObject {
Q_OBJECT
public:
Closure(QObject* 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(QObject* sender, const char* signal,
std::tr1::function<void()> callback);
virtual ~Closure();
ObjectHelper(
QObject* parent,
const char* signal,
ClosureBase* closure);
private slots:
void Invoked();
void Cleanup();
private:
void Connect(QObject* sender, const char* signal);
QMetaMethod slot_;
std::tr1::function<void()> callback_;
boost::scoped_ptr<const ClosureArgumentWrapper> val0_;
boost::scoped_ptr<const ClosureArgumentWrapper> val1_;
boost::scoped_ptr<const ClosureArgumentWrapper> val2_;
boost::scoped_ptr<const ClosureArgumentWrapper> val3_;
};
class SharedPointerWrapper {
public:
virtual ~SharedPointerWrapper() {}
virtual QObject* data() const = 0;
boost::scoped_ptr<ClosureBase> closure_;
Q_DISABLE_COPY(ObjectHelper);
};
// Helper to unpack a tuple into a QList.
template<typename T>
class SharedPointer : public SharedPointerWrapper {
void Arg(const T& tail, QList<QGenericArgument>* list) {
typedef decltype(tail.get_head()) HeadType;
list->append(Q_ARG(HeadType, tail.get_head()));
Arg(tail.get_tail(), list);
}
// Specialisation for starting with an empty tuple, ie. no arguments.
template<>
void Arg<boost::tuples::tuple<>>(
const boost::tuples::tuple<>&,
QList<QGenericArgument>* list);
// Specialisation for the end of a tuple, where get_tail() returns null_type.
template<>
void Arg<boost::tuples::null_type>(
const boost::tuples::null_type&,
QList<QGenericArgument>* list);
template <typename... Args>
class Closure : public ClosureBase {
public:
explicit SharedPointer(QSharedPointer<T> ptr)
: ptr_(ptr) {
Closure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const Args&... args)
: ClosureBase(new ObjectHelper(sender, signal, this)),
// boost::bind is the easiest way to store an argument list.
function_(boost::bind(&Closure<Args...>::Call, this, args...)),
receiver_(receiver) {
const QMetaObject* meta_receiver = receiver->metaObject();
QByteArray normalised_slot = QMetaObject::normalizedSignature(slot + 1);
const int index = meta_receiver->indexOfSlot(normalised_slot.constData());
Q_ASSERT(index != -1);
slot_ = meta_receiver->method(index);
}
QObject* data() const {
return ptr_.data();
virtual void Invoke() {
function_();
}
private:
QSharedPointer<T> ptr_;
void Call(const Args&... args) {
auto t = boost::make_tuple(args...);
QList<QGenericArgument> arg_list;
Arg(t, &arg_list);
slot_.invoke(
receiver_,
arg_list.size() > 0 ? arg_list[0] : QGenericArgument(),
arg_list.size() > 1 ? arg_list[1] : QGenericArgument(),
arg_list.size() > 2 ? arg_list[2] : QGenericArgument(),
arg_list.size() > 3 ? arg_list[3] : QGenericArgument(),
arg_list.size() > 4 ? arg_list[4] : QGenericArgument(),
arg_list.size() > 5 ? arg_list[5] : QGenericArgument(),
arg_list.size() > 6 ? arg_list[6] : QGenericArgument(),
arg_list.size() > 7 ? arg_list[7] : QGenericArgument(),
arg_list.size() > 8 ? arg_list[8] : QGenericArgument(),
arg_list.size() > 9 ? arg_list[9] : QGenericArgument());
}
boost::function<void()> function_;
QObject* receiver_;
QMetaMethod slot_;
};
// For use with a QSharedPointer as a sender.
class SharedClosure : public Closure {
Q_OBJECT
template <typename T, typename... Args>
class SharedClosure : public Closure<Args...> {
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(sender->data(), signal,
receiver, slot,
val0, val1, val2, val3),
shared_sender_(sender) {
SharedClosure(
QSharedPointer<T> sender,
const char* signal,
QObject* receiver,
const char* slot,
const Args&... args)
: Closure<Args...>(
sender.data(),
signal,
receiver,
slot,
args...),
data_(sender) {
}
private:
boost::scoped_ptr<SharedPointerWrapper> shared_sender_;
QSharedPointer<T> data_;
};
} // namespace _detail
#define C_ARG(type, data) new _detail::ClosureArgument<type>(data)
_detail::Closure* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot);
template <typename T>
_detail::Closure* NewClosure(
template <typename... Args>
_detail::ClosureBase* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const T& val0) {
return new _detail::Closure(
sender, signal, receiver, slot,
C_ARG(T, val0));
const Args&... args) {
return new _detail::Closure<Args...>(
sender, signal, receiver, slot, args...);
}
template <typename T0, typename T1>
_detail::Closure* NewClosure(
QObject* sender,
// QSharedPointer variant
template <typename T, typename... Args>
_detail::ClosureBase* NewClosure(
QSharedPointer<T> sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0,
const T1& val1) {
return new _detail::Closure(
sender, signal, receiver, slot,
C_ARG(T0, val0), C_ARG(T1, val1));
const Args&... args) {
return new _detail::SharedClosure<T, Args...>(
sender, signal, receiver, slot, args...);
}
template <typename T0, typename T1, typename T2>
_detail::Closure* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0,
const T1& val1,
const T2& val2) {
return new _detail::Closure(
sender, signal, receiver, slot,
C_ARG(T0, val0), C_ARG(T1, val1), C_ARG(T2, val2));
}
template <typename T0, typename T1, typename T2, typename T3>
_detail::Closure* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0,
const T1& val1,
const T2& val2,
const T3& val3) {
return new _detail::Closure(
sender, signal, receiver, slot,
C_ARG(T0, val0), C_ARG(T1, val1), C_ARG(T2, val2), C_ARG(T3, val3));
}
template <typename TP>
_detail::Closure* NewClosure(
QSharedPointer<TP> sender,
const char* signal,
QObject* receiver,
const char* slot) {
return new _detail::SharedClosure(
new _detail::SharedPointer<TP>(sender), signal, receiver, slot);
}
template <typename TP, typename T0>
_detail::Closure* NewClosure(
QSharedPointer<TP> sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0) {
return new _detail::SharedClosure(
new _detail::SharedPointer<TP>(sender), signal, receiver, slot,
C_ARG(T0, val0));
}
template <typename TP, typename T0, typename T1>
_detail::Closure* NewClosure(
QSharedPointer<TP> sender,
const char* signal,
QObject* receiver,
const char* slot,
const T0& val0,
const T1& val1) {
return new _detail::SharedClosure(
new _detail::SharedPointer<TP>(sender), signal, receiver, slot,
C_ARG(T0, val0), C_ARG(T1, val1));
}
void DoAfter(QObject* receiver, const char* slot, int msec);
void DoInAMinuteOrSo(QObject* receiver, const char* slot);

View File

@ -1,27 +1,29 @@
/* This file is part of Clementine.
Copyright 2012, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "googledriveclient.h"
#include "oauthenticator.h"
#include "core/closure.h"
#include "core/network.h"
#include <qjson/parser.h>
#include "oauthenticator.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/network.h"
using namespace google_drive;
const char* File::kFolderMimeType = "application/vnd.google-apps.folder";

View File

@ -12,6 +12,7 @@
#include <qjson/parser.h>
#include "core/closure.h"
#include "core/logging.h"
namespace {

View File

@ -16,11 +16,8 @@
*/
#include "moodbarloader.h"
#include "moodbarpipeline.h"
#include "core/application.h"
#include "core/closure.h"
#include "core/qhash_qurl.h"
#include "core/utilities.h"
#include <boost/scoped_ptr.hpp>
#include <QCoreApplication>
#include <QDir>
@ -30,7 +27,12 @@
#include <QThread>
#include <QUrl>
#include <boost/scoped_ptr.hpp>
#include "moodbarpipeline.h"
#include "core/application.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/qhash_qurl.h"
#include "core/utilities.h"
MoodbarLoader::MoodbarLoader(Application* app, QObject* parent)
: QObject(parent),

View File

@ -189,7 +189,7 @@ void GPodderSync::DeviceUpdatesFinished(mygpo::DeviceUpdatesPtr reply) {
// have a list of the episodes.
PodcastUrlLoaderReply* loader_reply = loader_->Load(url);
NewClosure(loader_reply, SIGNAL(Finished(bool)),
this, SLOT(NewPodcastLoaded(PodcastUrlLoaderReply*,QUrl,QList<QSharedPointer<mygpo::Episode> >)),
this, SLOT(NewPodcastLoaded(PodcastUrlLoaderReply*,QUrl,QList<mygpo::EpisodePtr>)),
loader_reply, url, episodes_by_podcast[url]);
}

View File

@ -1,28 +1,30 @@
/* This file is part of Clementine.
Copyright 2012, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "podcastparser.h"
#include "podcasturlloader.h"
#include "core/closure.h"
#include "core/network.h"
#include "core/utilities.h"
#include <QNetworkReply>
#include "podcastparser.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/network.h"
#include "core/utilities.h"
const int PodcastUrlLoader::kMaxRedirects = 5;

View File

@ -26,6 +26,7 @@
#include <qjson/parser.h>
#include "core/closure.h"
#include "core/logging.h"
#include "songkickconcertwidget.h"
const char* SongkickConcerts::kSongkickArtistBucket = "id:songkick";

View File

@ -15,18 +15,20 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "songinfotextview.h"
#include "songkickconcertwidget.h"
#include "ui_songkickconcertwidget.h"
#include "core/closure.h"
#include "core/network.h"
#include "core/utilities.h"
#include <QDate>
#include <QDesktopServices>
#include <QMouseEvent>
#include <QTextDocument>
#include "songinfotextview.h"
#include "ui_songkickconcertwidget.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/network.h"
#include "core/utilities.h"
const int SongKickConcertWidget::kStaticMapWidth = 100;
const int SongKickConcertWidget::kStaticMapHeight = 100;

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 2.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -U__STRICT_ANSI__ -fpermissive")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -fpermissive -U__STRICT_ANSI__")
if(USE_SYSTEM_GMOCK)
include_directories(${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS})

View File

@ -11,7 +11,7 @@
TEST(ClosureTest, ClosureInvokesReceiver) {
TestQObject sender;
TestQObject receiver;
_detail::Closure* closure = NewClosure(
_detail::ClosureBase* closure = NewClosure(
&sender, SIGNAL(Emitted()),
&receiver, SLOT(Invoke()));
EXPECT_EQ(0, receiver.invoked());
@ -22,17 +22,18 @@ TEST(ClosureTest, ClosureInvokesReceiver) {
TEST(ClosureTest, ClosureDeletesSelf) {
TestQObject sender;
TestQObject receiver;
_detail::Closure* closure = NewClosure(
_detail::ClosureBase* closure = NewClosure(
&sender, SIGNAL(Emitted()),
&receiver, SLOT(Invoke()));
QSignalSpy spy(closure, SIGNAL(destroyed()));
_detail::ObjectHelper* helper = closure->helper();
QSignalSpy spy(helper, SIGNAL(destroyed()));
EXPECT_EQ(0, receiver.invoked());
sender.Emit();
EXPECT_EQ(1, receiver.invoked());
EXPECT_EQ(0, spy.count());
QEventLoop loop;
QObject::connect(closure, SIGNAL(destroyed()), &loop, SLOT(quit()));
QObject::connect(helper, SIGNAL(destroyed()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1, spy.count());
}
@ -41,23 +42,23 @@ TEST(ClosureTest, ClosureDoesNotCrashWithSharedPointerSender) {
TestQObject receiver;
TestQObject* sender;
boost::scoped_ptr<QSignalSpy> spy;
QPointer<_detail::Closure> closure;
QPointer<_detail::ObjectHelper> closure;
{
QSharedPointer<TestQObject> sender_shared(new TestQObject);
sender = sender_shared.data();
closure = QPointer<_detail::Closure>(NewClosure(
closure = QPointer<_detail::ObjectHelper>(NewClosure(
sender_shared, SIGNAL(Emitted()),
&receiver, SLOT(Invoke())));
&receiver, SLOT(Invoke()))->helper());
spy.reset(new QSignalSpy(sender, SIGNAL(destroyed())));
}
EXPECT_EQ(0, receiver.invoked());
ASSERT_EQ(0, receiver.invoked());
sender->Emit();
EXPECT_EQ(1, receiver.invoked());
ASSERT_EQ(1, receiver.invoked());
EXPECT_EQ(0, spy->count());
ASSERT_EQ(0, spy->count());
QEventLoop loop;
QObject::connect(sender, SIGNAL(destroyed()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1, spy->count());
ASSERT_EQ(1, spy->count());
EXPECT_TRUE(closure.isNull());
}