Asynchronous programming (and more) with Qt5 and C++11 Dario - - PowerPoint PPT Presentation

asynchronous programming and more with qt5 and c 11
SMART_READER_LITE
LIVE PREVIEW

Asynchronous programming (and more) with Qt5 and C++11 Dario - - PowerPoint PPT Presentation

Asynchronous programming (and more) with Qt5 and C++11 Dario Freddi, Ispirata Qt Developer Days 2013 Hello Hello Hello Hello Qt5 <3 C++11 C++11 in 10 minutes A quick tour C++11 in 10 minutes Main Concepts C++11 in 10 minutes class


slide-1
SLIDE 1

Asynchronous programming (and more) with Qt5 and C++11

Dario Freddi, Ispirata

Qt Developer Days 2013

slide-2
SLIDE 2

Hello

slide-3
SLIDE 3

Hello

slide-4
SLIDE 4

Hello

slide-5
SLIDE 5

Hello

Qt5 <3 C++11

slide-6
SLIDE 6

C++11 in 10 minutes

A quick tour

slide-7
SLIDE 7

C++11 in 10 minutes

Main Concepts

slide-8
SLIDE 8

C++11 in 10 minutes

class A { protected: virtual int myMethod(int arg, char *args); }; class B : public A { protected: virtual void myMethod(int arg, char *args); };

slide-9
SLIDE 9

C++11 in 10 minutes

class A { protected: virtual int myMethod(int arg, char *args); }; class B : public A { protected: void myMethod(int arg, char *args) override; };

slide-10
SLIDE 10

C++11 in 10 minutes

class A { protected: virtual int myMethod(int arg, char *args) final; }; class B : public A { protected: virtual int myMethod(int arg, char *args); };

slide-11
SLIDE 11

C++11 in 10 minutes

class A { protected: void myMethod(int, char *) Q_DECL_FINAL; }; class B : public A { protected: void myMethod(int, char *) Q_DECL_OVERRIDE; };

slide-12
SLIDE 12

C++11 in 10 minutes

#define MULTIPLY(a, b) a*b

slide-13
SLIDE 13

C++11 in 10 minutes

#define MULTIPLY(a, b) a*b constexpr int multiply(int a, int b) { return a*b; }

slide-14
SLIDE 14

C++11 in 10 minutes

constexpr int factorial (int n) { return n > 0 ? n * factorial( n - 1 ) : 1; }

slide-15
SLIDE 15

C++11 in 10 minutes

class Stuff { public: constexpr Stuff (int x, int y) : m_x( x ),m _y( y ) {} constexpr double compute() { return m_x * m_y * 42; } private: int m_x; int m_y; };

slide-16
SLIDE 16

C++11 in 10 minutes

Q_DECL_CONSTEXPR int factorial (int n) { return n > 0 ? n * factorial( n - 1 ) : 1; }

slide-17
SLIDE 17

C++11 in 10 minutes

Q_FOREACH(const QString &element, list) { // code, code, code... }

slide-18
SLIDE 18

C++11 in 10 minutes

for (const QString &element : list) { // code, code, code... }

slide-19
SLIDE 19

C++11 in 10 minutes

enum Stuff { BEANS, QT, OTHER }; enum MoarStuff { PILLOWS, ONIONS, OTHER };

slide-20
SLIDE 20

C++11 in 10 minutes

enum class Stuff { BEANS, QT, OTHER }; enum class MoarStuff { PILLOWS, ONIONS, OTHER };

slide-21
SLIDE 21

C++11 in 10 minutes

enum class Stuff; void wheresMyStuff(Stuff stuff); enum class Stuff : int { BEANS, QT, OTHER };

slide-22
SLIDE 22

C++11 in 10 minutes

void doStuff(int); void doStuff(char *); doStuff(0);

slide-23
SLIDE 23

C++11 in 10 minutes

void doStuff(int); void doStuff(char *); doStuff(nullptr);

slide-24
SLIDE 24

C++11 in 10 minutes

void doStuff(int); void doStuff(char *); doStuff(Q_NULLPTR);

slide-25
SLIDE 25

C++11 in 10 minutes

The main course

slide-26
SLIDE 26

Lambdas

auto areLambdasAmazing = [this] -> bool { return true; }

slide-27
SLIDE 27

Lambdas

return QQmlListProperty<MyObject>(this, 0, [] (QQmlListProperty<MyObject> *list, Object *m) { Controller *c = qobject_cast<Controller *>(list->object); if (m) { c->append(m); } }, [] (QQmlListProperty<MyObject> *list) -> int { return qobject_cast<Controller *> (list->object)->count(); }, [] (QQmlListProperty<MyObject> *list, int at) -> Object* { return qobject_cast<Controller *>(list->object)->at(at); }, [] (QQmlListProperty<MyObject> *list) { Controller *c = qobject_cast<Controller *>(list->object); c->clearObjects(); });

slide-28
SLIDE 28

Lambdas

connect(myobj, &QObject::destroyed, [this] { qDebug() << “oh noes!”; });

slide-29
SLIDE 29

Lambdas in Qt 5.1

  • Tied to the context of the sender
  • Hence, tied to Qt::DirectConnection
  • Little control over the connect

mechanism

slide-30
SLIDE 30

Lambdas

connect(jobManager, &JobManager::jobRemoved, [this] (uint id) { if (d->id == id) { sendSignalAndDestroyObject(); } });

slide-31
SLIDE 31

Lambdas

What is happening:

  • The connection is stored in a

QMetaObject::Connection

  • The sender (context) is never

destroyed

  • The connection will be still alive when

this is destroyed

slide-32
SLIDE 32

Lambdas

connect(jobManager, &JobManager::jobRemoved, [this] (uint id) { if (d->id == id) { sendSignalAndDestroyObject(); } });

slide-33
SLIDE 33

Lambdas in Qt 5.1

  • Can be tied to a different QObject

context

  • Functor connect behaves as standard

connect does (Qt::AutoConnection)

slide-34
SLIDE 34

Lambdas

connect(jobManager, &JobManager::jobRemoved, this, [this] (uint id) { if (d->id == id) { sendSignalAndDestroyObject(); } });

slide-35
SLIDE 35

Lambdas

connect(object, &Object::randomSignal,

  • bjectInDifferentThread, [this, object] {

if (QThread::currentThread() !=

  • bject->thread()) {

// This is definitely not going to happen! } });

slide-36
SLIDE 36

Under the hood

Variadic templates in QObject::connect's implementation

slide-37
SLIDE 37

Under the hood

template <typename Func1, typename Func2> static inline typename QtPrivate::QEnableIf<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0 && !QtPrivate::FunctionPointer<Func2>::IsPointerToMemberFunction, QMetaObject::Connection>::Type connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot, Qt::ConnectionType type = Qt::AutoConnection)

slide-38
SLIDE 38

Under the hood

template<class Obj, typename Ret, typename Arg1> struct FunctionPointer<Ret (Obj::*) (Arg1)> { typedef Obj Object; typedef List<Arg1, void> Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1); enum {ArgumentCount = 1, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)((*reinterpret_cast<typename RemoveRef<typename Args::Car>::Type *>(arg[1]))), ApplyReturnValue<R>(arg[0]); } };

slide-39
SLIDE 39

Under the hood

template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)> { typedef Obj Object; typedef List<Args...> Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Args...); enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; template <typename SignalArgs, typename R> static void call(Function f, Obj *o, void **arg) { FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); } };

slide-40
SLIDE 40

Lambdas

Initializing a chain of asynchronous objects

slide-41
SLIDE 41

Async initialization chains

class AsyncInitObject : public QObject { Q_OBJECT public: void init(); protected: virtual void initImpl() = 0; void setReady(bool status); signals: void ready(); void error(); };

slide-42
SLIDE 42

Async initialization chains

{ connect(anObject, SIGNAL(ready()), SLOT(initNext())); } { prepare(); connect(otherObject, SIGNAL(ready()), SLOT(initNextAgain())); }

slide-43
SLIDE 43

Async initialization chains

{ connect(anObject, &AsyncInitObject::ready, [this, otherObject] { prepare();

  • therObject->init();

connect(otherObject, &ASIO::ready, [this] { // More stuff... }); }); }

slide-44
SLIDE 44

Async initialization chains

{ connect(otherObject, &AsyncInitObject::ready), [this] { /* finalize init here... */ }); connect(anObject, &AsyncInitObject::ready),

  • therObject, &AsyncInitObject::init);

}

slide-45
SLIDE 45

Async initialization chains

{ connect(anObject, &AsyncInitObject::ready, [this, otherObject] { prepare();

  • therObject->init();

connect(otherObject, &ASIO::ready, [this] { // More stuff... }, Qt::QueuedConnection); }, Qt::QueuedConnection); }

slide-46
SLIDE 46

Lambdas

Handling UNIX Signals and application lifecycle

slide-47
SLIDE 47

main.cpp on steroids

static int sighupFd[2]; static int sigtermFd[2]; static void hupSignalHandler(int) { char a = 1; ::write(sighupFd[0], &a, sizeof(a)); } static void termSignalHandler(int) { char a = 1; ::write(sigtermFd[0], &a, sizeof(a)); }

slide-48
SLIDE 48

main.cpp on steroids

static int setup_unix_signal_handlers() { struct sigaction hup, term; hup.sa_handler = hupSignalHandler; sigemptyset(&hup.sa_mask); hup.sa_flags = 0; hup.sa_flags |= SA_RESTART; if (sigaction(SIGHUP, &hup, 0) > 0) { return 1; } term.sa_handler = termSignalHandler; sigemptyset(&term.sa_mask); term.sa_flags |= SA_RESTART; if (sigaction(SIGTERM, &term, 0) > 0) { return 2; } return 0; }

slide-49
SLIDE 49

main.cpp on steroids

{ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighupFd)) qFatal("Couldn't create HUP socketpair"); if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigtermFd)) qFatal("Couldn't create TERM socketpair"); snHup = new QSocketNotifier(sighupFd[1], QSocketNotifier::Read, this); connect(snHup, SIGNAL(activated(int)), this, SLOT(handleSigHup())); snTerm = new QSocketNotifier(sigtermFd[1], QSocketNotifier::Read, this); connect(snTerm, SIGNAL(activated(int)), this, SLOT(handleSigTerm())); ... }

slide-50
SLIDE 50

main.cpp on steroids

{ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighupFd)) qFatal("Couldn't create HUP socketpair"); if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigtermFd)) qFatal("Couldn't create TERM socketpair"); snHup = new QSocketNotifier(sighupFd[1], QSocketNotifier::Read, this); connect(snHup, SIGNAL(activated(int)), [this] { /* handle */ }); snTerm = new QSocketNotifier(sigtermFd[1], QSocketNotifier::Read, this); connect(snTerm, SIGNAL(activated(int)), [this] { /* handle */ }); ... }

slide-51
SLIDE 51

main.cpp on steroids

auto startItUp = [&] () { core = new Core; Operation *op = core->init(); QObject::connect(op, &Operation::finished, [core, op] { if (op->isError()) { qFatal("Initialization of the core failed.”); } else { // Do stuff here // Notify system here } }); };

slide-52
SLIDE 52

main.cpp on steroids

auto shutItDown = [&] () { // Destroy stuff here // More stuff here QObject::connect(core->lastObject(), &QObject::destroyed, core, &QObject::deleteLater); };

slide-53
SLIDE 53

main.cpp on steroids

QSocketNotifier snHup(sighupFd[1], QSocketNotifier::Read); QObject::connect(&snHup, &QSocketNotifier::activated, [&] () { // Handle SIGHUP here snHup.setEnabled(false); char tmp; ::read(sighupFd[1], &tmp, sizeof(tmp)); qDebug() << "Reloading application..."; // Destroy & create QObject::connect(core, &QObject::destroyed, [&] { startItUp(); snHup.setEnabled(true); }); shutItDown(); });

slide-54
SLIDE 54

main.cpp on steroids

QSocketNotifier snHup(sighupFd[1], QSocketNotifier::Read); QObject::connect(&snHup, &QSocketNotifier::activated, [&] () { // Handle SIGHUP here }); QSocketNotifier snTerm(sigtermFd[1], QSocketNotifier::Read); QObject::connect(&snTerm, &QSocketNotifier::activated, [&] () { // Handle SIGTERM here }); if (setup_unix_signal_handlers() != 0) { qFatal("Couldn't register UNIX signal handler"); return -1; } // Start the application startItUp(); return app.exec();

slide-55
SLIDE 55

Lambdas

Inline management of stateful objects

slide-56
SLIDE 56

Lambdas

QDBusPendingCallWatcher

slide-57
SLIDE 57

Lambdas

QDBusPendingReply<QDBusObjectPath> jobPath; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(jobPath);

slide-58
SLIDE 58

Lambdas

auto onJobPathFinished = [this] (QDBusPendingCallWatcher *watcher) { QDBusPendingReply<QDBusObjectPath> reply = *watcher; if (reply.isError()) { setFinishedWithError(reply.error()); return; } // Stuff... }; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(jobPath); if (watcher->isFinished()) {

  • nJobPathFinished(watcher)

} else { connect(watcher, &QDBusPendingCallWatcher::finished,

  • nJobPathFinished);

}

slide-59
SLIDE 59

QTimer

Inline asynchronous code

slide-60
SLIDE 60

QTimer

QMetaObject::invokeMethod(obj, “myMethod”); QTimer::singleShot(0, obj, SLOT(myMethod());

slide-61
SLIDE 61

QTimer

QTimer::singleShot(0, [this] { /* doStuff */ });

slide-62
SLIDE 62

QTimer

{ int value = computeValue(); QTimer::singleShot(0, [this, value] { doSthLater(value); }); // More important things... }

slide-63
SLIDE 63

QTimer

qAsync([this] { qDebug() << “Just a shorter way of ” “doing the same thing.” });

slide-64
SLIDE 64

QtConcurrent

Inline asynchronous (multithreaded) code

slide-65
SLIDE 65

QtConcurrent

{ int value = computeValue(); QFuture<int>QtConcurrent::run([this, value] -> int { int result = veryHeavyComputation(value); result = result + 42; return result; }); // More important things... }

slide-66
SLIDE 66

QtConcurrent

{ int value = computeValue(); QFuture<int>QtConcurrent::run([this] (int value) -> int { int result = veryHeavyComputation(value); result = result + 42; return result; }, value); // More important things... }

slide-67
SLIDE 67

QTimer

{ int value = computeValue(); QTimer::singleShot(0, m_objectInOtherThread, [this, value] { doSthLater(value); }); // More important things... }

slide-68
SLIDE 68

Caveats

Context: QThread vs. QObject

slide-69
SLIDE 69

Caveats

{ QObject *obj = new QObject; QThread *thread = new QThread;

  • bj->moveToThread(thread);

thread->start(); ... QTimer::singleShot(0, thread, [] { qDebug() << QThread::currentThread(); }); QTimer::singleShot(0, obj, [] { qDebug() << QThread::currentThread(); }); }

slide-70
SLIDE 70

Recap

  • Look forward to an even better C++11

experience in Qt 5.2/Qt 5.3

  • Use lambdas carefully, and remember

context matters

  • Upgrade to GCC 4.8.1!
slide-71
SLIDE 71

Thank you!

Any questions?

... in case you have some, but you're too shy: Dario Freddi dario@ispirata.com