对于任何感兴趣的人,一个小助手类允许创建具有自动可变参数推导的工人。请注意,C++17 是必需的(即不需要模板参数的规范)。完整的源代码见:https ://github.com/Broekman/Qt5_template
工人.hpp
#ifndef QT5_UI_WORKER_HPP
#define QT5_UI_WORKER_HPP
#include <QObject>
#include <QString>
#include <functional>
#include <tuple>
namespace ui
{
class worker_object :
public QObject
{
Q_OBJECT
public:
inline worker_object() = default;
inline ~worker_object() = default;
public slots:
inline virtual void run() { /*...*/ };
signals:
void finished();
void error(const QString& err_msg);
};
namespace helper
{
template <int... Is>
struct index {};
template <int N, int... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
template <int... Is>
struct gen_seq<0, Is...> : index<Is...> {};
}
template<typename... Ts>
class worker :
public worker_object
{
public: /* Functions */
template<typename Func, typename... Args>
inline worker(Func&& fn, Args&& ... args) :
fn_(std::forward<Func>(fn)),
args_(std::forward<Args>(args)...)
{ /*...*/ }
inline ~worker() = default;
inline void run() override
{
func(args_);
emit finished();
}
private: /* Functions */
template<typename... Args, int... Is>
inline void func(std::tuple<Args...>& tup, helper::index<Is...>)
{
fn_(std::get<Is>(tup)...);
}
template<typename... Args>
inline void func(std::tuple<Args...>& tup)
{
func(tup, helper::gen_seq<static_cast<int>(sizeof...(Args))>{});
}
private: /* Class members */
std::function<void(Ts...)> fn_;
std::tuple<Ts...> args_;
};
/**
* @brief Helper function to create a worker by which specification of the template arguments aren't necessary.
*/
template<typename Func, typename... Args>
worker<Args...> make_worker(Func&& fn, Args&& ... args)
{
return worker<Args...>(std::forward<Func>(fn), std::forward<Args>(args)...);
}
}
#endif
主窗口.cpp
void main_window::start_worker(worker_object *thread_worker, const worker_callback& on_finish,
const worker_error_callback& on_error)
{
auto *worker_thread = new QThread;
thread_worker->moveToThread(worker_thread);
connect(thread_worker, &worker_object::error, this, on_error);
connect(worker_thread, &QThread::started, thread_worker, &worker_object::run);
connect(thread_worker, &worker_object::finished, worker_thread, &QThread::quit);
connect(thread_worker, &worker_object::finished, this, on_finish);
connect(thread_worker, &worker_object::finished, thread_worker, &worker_object::deleteLater);
connect(worker_thread, &QThread::finished, worker_thread, &QThread::deleteLater);
worker_thread->start();
}
main_window.cpp 示例 1:无参数
void main_window::service_example()
{
//STEP 1: specify a (synchronous) task for the worker.
auto *work = new worker(make_worker([this]()
{
auto task_len_ms = 2500; //ms
logger_->info("Doing some concurrent work for " + std::to_string(task_len_ms) + " milliseconds...");
QThread::msleep((unsigned)task_len_ms);
}));
//STEP 2: specify the completion handler. Called upon a worker_object::finished signal.
auto on_finish_callback = [this]()
{
logger_->info("Concurrent work finished!");
};
//STEP 3: specify an error handler. Called upon a worker_object::error(const QString&) signal.
auto on_error_callback = [this](const QString& err_msg)
{
logger_->error(err_msg.toStdString());
};
//STEP 4: start the worker.
start_worker(work, on_finish_callback, on_error_callback);
}
main_window.cpp 示例 2:一些参数
//STEP 1: specify a (synchronous) task for the worker.
auto *work = new worker(make_worker([this](const std::string& personal_msg, unsigned long wait_time)
{
logger_->info(personal_msg);
QThread::msleep((unsigned)wait_time);
}, "Hello, world?", 2500));
//STEP 2: specify the completion handler. Called upon a worker_object::finished signal.
//STEP 3: specify an error handler. Called upon a worker_object::error(const QString&) signal.
//STEP 4: start the worker.