Skip to content

Instantly share code, notes, and snippets.

@micjabbour
Last active April 13, 2017 16:24
Show Gist options
  • Save micjabbour/b8b3cfd1b566a041b8ad279a139e8362 to your computer and use it in GitHub Desktop.
Save micjabbour/b8b3cfd1b566a041b8ad279a139e8362 to your computer and use it in GitHub Desktop.
Some useful QThread utilities, that can be used to call functors in other threads (and possibly grab their return value)
#include "qthreadutils.h"
#include <QtCore>
//the following file shows an example usage for CallByWorker
//a thread that can be destroyed at any time
//see http://stackoverflow.com/a/25230470
class SafeThread : public QThread{
using QThread::run;
public:
explicit SafeThread(QObject* parent= nullptr):QThread(parent){}
~SafeThread(){ quit(); wait(); }
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QObject worker;
SafeThread thread;
thread.start();
worker.moveToThread(&thread);
auto workerThread = CallByWorker(&worker, []{ return QThread::currentThread(); });
Q_ASSERT(workerThread == worker.thread()); //lambda is executed in the worker's thread
qDebug() << "main thread:\t" << QThread::currentThread();
qDebug() << "worker thread:\t" << workerThread;
QTimer::singleShot(1000, &a, &QCoreApplication::quit);
return a.exec();
}
#ifndef QTHREADUTILS_H
#define QTHREADUTILS_H
#include <QObject>
#include <QThread>
//FunctorTraits is used to get the return type of a lambda expression
//see http://stackoverflow.com/a/7943765
template <typename T>
struct FunctorTraits : public FunctorTraits<decltype(&T::operator())> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct FunctorTraits< ReturnType (ClassType::*)(Args...) const> {
typedef ReturnType return_t;
};
//The function queues a functor to get executed in a specified worker's thread
//the connectionType argument determines if the function needs to wait for the functor to finish (By default it does NOT)
template <typename Func>
void PostToWorker(QObject* worker, Func&& f, Qt::ConnectionType connectionType = Qt::QueuedConnection) {
//see http://stackoverflow.com/a/21653558
QObject temporaryObject;
QObject::connect(&temporaryObject, &QObject::destroyed,
worker, std::forward<Func>(f), connectionType);
}
//The function executes a functor in a specified worker's thread
//it waits for the functor to finish, and returns its result to the caller in the current thread
template <typename Func> //for functors returning non-void
typename std::enable_if<!std::is_void<typename FunctorTraits<Func>::return_t>::value, typename FunctorTraits<Func>::return_t>::type
CallByWorker(QObject* worker, Func&& f) {
Qt::ConnectionType blockingConnectionType = QThread::currentThread() == worker->thread() ?
Qt::DirectConnection : Qt::BlockingQueuedConnection;
typename FunctorTraits<Func>::return_t returnValue;
auto myFunctor = [&]{
returnValue= std::forward<Func>(f)();
};
PostToWorker(worker, myFunctor, blockingConnectionType);
return returnValue;
}
//if the functor returns void, no need to use a custom functor like above
template <typename Func> //for functors returning void
typename std::enable_if<std::is_void<typename FunctorTraits<Func>::return_t>::value, void>::type
CallByWorker(QObject* worker, Func&& f) {
Qt::ConnectionType blockingConnectionType = QThread::currentThread() == worker->thread() ?
Qt::DirectConnection : Qt::BlockingQueuedConnection;
PostToWorker(worker, std::forward<Func>(f), blockingConnectionType);
}
#endif // QTHREADUTILS_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment