Qt异步动作序列 [英] Qt asynchronous action sequence

查看:341
本文介绍了Qt异步动作序列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++ / Qt程序中,我需要运行一些具有完成信号的异步任务(例如网络下载, QProcess 等)

In a C++/Qt program I need to run a few asynchronous tasks with "done" signals (for example network downloads, QProcess, etc.) in sequence, each after the last finishes.

我可以想到的唯一方法是为每个步骤都有一个单独的状态类(非常冗长,就像为每一行都有一个单独的类)同步程序),或者具有一个具有状态枚举的大类和用于保存不同步骤所需的所有可能对象(不灵活,难以维护)的字段。有什么好的解决方案吗?

The only ways I can think of are to have a separate state class for each step (extremely verbose, like having a separate class for each line in a synchronous program), or to have one big class with a state enum and fields to hold all possible objects needed for the different steps (inflexible, difficult to maintain). Are there any good solutions for this? It seems like it should be a common problem, but I'm having trouble finding anything.

推荐答案

有很多方法可以做它。一个基本模式是将函数连接到 done()信号:

There are many ways of doing it. One basic pattern is to connect functors to the done() signals:

struct Task : QObject {
   Q_SLOT void start();
   Q_SIGNAL void done();
   Q_OBJECT
};

int main(int argc, char ** argv) {
   QCoreApplication app{argc, argv};
   using QObject = Q;
   Task task1, task2, task3;
   Q::connect(&task1, &QObject::done, &task2, [&]{ task2.start(); });
   Q::connect(&task2, &QObject::done, &task3, [&]{ task3.start(); });
   Q::connect(&task3, &QObject::done, &app, [&]{ app.quit(); });
   return app.exec();
}



我们可以将有关 / code>信号:

We can factor out the knowledge about the done signal of a particular class:

template <typename F> void onDone(QProcess * process, QObject * dst, F && f) {
   QObject::connect(process, 
                    static_cast<void(QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished), 
                    dst, f);
}

template <typename F> void onDone(QNetworkReply * reply, QObject * dst, F && f) {
   QObject::connect(reply, &QNetworkReply::finished, dst, f);
}

int main(int argc, char ** argv) {
   QCoreApplication app{argc, argv};
   QNetworkAccessManager mgr;
   auto download = mgr.get(QNetworkRequest{QUrl{"http://www.google.com"}});
   QProcess process;

   onDone(download, &process, [&]{ process.start(); });
   onDone(&process, &app, [&]{ app.quit(); });

   return app.exec();
}

如果某个类有常见的特定行为,他们,你可以把他们也。 traits类有助于防止由于多种可能的配对引起的组合爆炸:

If there are particular behaviors that are common on a class, or a pair of them, you can factor them out as well. The traits classes help prevent the combinatorial explosion due to multiple possible pairings:

// https://github.com/KubaO/stackoverflown/tree/master/questions/task-sequence-37903585
#include <QtCore>
#include <QtNetwork>
#include <type_traits>

template <typename T> struct SourceAction;
template<> struct SourceAction<QProcess> {
   typedef void(QProcess::* signal_type)(int,QProcess::ExitStatus);
   static constexpr signal_type source(QProcess*) { return static_cast<signal_type>(&QProcess::finished); }
};
template<> struct SourceAction<QNetworkReply> {
   typedef void(QNetworkReply::* signal_type)();
   static constexpr signal_type source(QNetworkReply*) { return &QNetworkReply::finished; }
};

template <typename T> struct TargetAction;
template<> struct TargetAction<QProcess> {
   struct slot_type {
      QProcess * process;
      void operator()() { process->start(); }
      slot_type(QProcess* process) : process(process) {}
   };
   static slot_type destination(QProcess * process) { return slot_type(process); }
};
template<> struct TargetAction<QCoreApplication> {
   typedef void(*slot_type)();
   static constexpr slot_type destination(QCoreApplication*) { return &QCoreApplication::quit; }
};

// SFINAE
template <typename Src, typename Dst>
void proceed(Src * src, Dst * dst) {
   QObject::connect(src, SourceAction<Src>::source(src),
                    dst, TargetAction<Dst>::destination(dst));
}
template <typename Src, typename F>
void proceed(Src * src, F && f) {
   QObject::connect(src, SourceAction<Src>::source(src), std::move(f));
}

QNetworkReply * download(QNetworkAccessManager * mgr, const QUrl & url) {
   return mgr->get(QNetworkRequest{url});
}
QProcess * setup(QProcess * process, const QString & program, const QStringList & args) {
   process->setProgram(program);
   process->setArguments(args);
   return process;
}

int main(int argc, char ** argv) {
   QCoreApplication app{argc, argv};
   if (app.arguments().count() > 1) return 0;

   QNetworkAccessManager mgr;
   QProcess process;

   proceed(download(&mgr, {"http://www.google.com"}), &process);
   proceed(setup(&process, app.applicationFilePath(), {"dummy"}), &app);
   proceed(&process, []{ qDebug() << "quitting"; });
   return app.exec();
}

您也可以以类似的声明方式利用状态机系统

这篇关于Qt异步动作序列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆