diff --git a/src/blackmisc/variant.h b/src/blackmisc/variant.h index 559356065..28bb856b9 100644 --- a/src/blackmisc/variant.h +++ b/src/blackmisc/variant.h @@ -185,6 +185,12 @@ namespace BlackMisc return CVariant(QVariant::fromValue(std::forward(value))); } + //! Call a function and construct a variant from its return value. + template static CVariant fromResultOf(F &&func) + { + return fromResultOfImpl(std::forward(func), typename std::is_void::type()); + } + //! Change the value. template void setValue(T &&value) { m_v.setValue(std::forward(value)); } @@ -289,6 +295,9 @@ namespace BlackMisc static int compareImpl(const CVariant &, const CVariant &); uint getValueHash() const; + + template static CVariant fromResultOfImpl(F &&func, std::true_type) { std::forward(func)(); return {}; } + template static CVariant fromResultOfImpl(F &&func, std::false_type) { return from(std::forward(func)()); } }; namespace Private diff --git a/src/blackmisc/worker.cpp b/src/blackmisc/worker.cpp index 37e90e70c..fc9872ab0 100644 --- a/src/blackmisc/worker.cpp +++ b/src/blackmisc/worker.cpp @@ -13,7 +13,7 @@ namespace BlackMisc { - CWorker *CWorker::fromTask(QObject *owner, const QString &name, std::function task) + CWorker *CWorker::fromTaskImpl(QObject *owner, const QString &name, std::function task) { auto *thread = new CRegularThread(owner); auto *worker = new CWorker(task); @@ -32,7 +32,7 @@ namespace BlackMisc void CWorker::ps_runTask() { - m_task(); + m_result = m_task(); setFinished(); diff --git a/src/blackmisc/worker.h b/src/blackmisc/worker.h index 52a03432f..1938e8c27 100644 --- a/src/blackmisc/worker.h +++ b/src/blackmisc/worker.h @@ -13,6 +13,7 @@ #define BLACKMISC_WORKER_H #include "blackmiscexport.h" +#include "variant.h" #include #include #include @@ -207,17 +208,42 @@ namespace BlackMisc * \param owner Will be the parent of the new thread (the worker has no parent). * \param name A name for the task, which will be used to create a name for the thread. * \param task A function object which will be run by the worker in its thread. + * \todo An MSVC bug prevents perfect-forwarding of task. */ - static CWorker *fromTask(QObject *owner, const QString &name, std::function task); + template + static CWorker *fromTask(QObject *owner, const QString &name, F task) + { + return fromTaskImpl(owner, name, [task]() { return CVariant::fromResultOf(task); }); + } + + //! Connects to a functor to which will be passed the result when the task is finished. + //! \tparam R The return type of the task. + //! \threadsafe + template + void thenWithResult(F functor) { then([this, functor]() { functor(result()); }); } + + //! Connects to a functor or method to which will be passed the result when the task is finished. + //! \tparam R The return type of the task. + //! \threadsafe + template + void thenWithResult(T *context, F functor) { then(context, [this, context, functor]() { invoke(context, functor, result()); }); } + + //! Returns the result of the task, waiting for it to finish if necessary. + //! \tparam R The return type of the task. + //! \threadsafe + template + R result() { waitForFinished(); Q_ASSERT(m_result.canConvert()); return m_result.value(); } private slots: //! Called when the worker has been moved into its new thread. void ps_runTask(); private: - CWorker(std::function task) : m_task(task) {} + CWorker(std::function task) : m_task(task) {} + static CWorker *fromTaskImpl(QObject *owner, const QString &name, std::function task); - std::function m_task; + std::function m_task; + CVariant m_result; }; /*!