如何在 Qt 中设计异步包装器返回值?

How to design asynchronous wrapper returning values in Qt?

我在 Qt 中为外部可执行文件编写了一个包装器 class。 它有很多方法,其中大部分是:

在这种情况下,同步 包装器非常简单:

class SyncWrapper {
public:
    // Values are fetched *synchronously* in these methods
    QString name() const;
    QStringList files() const;
    bool remove(const QString &file) const;
};

但是,我想使这个包装器异步,像这样:

class AsyncWrapper {
    Q_OBJECT
public:
    // Values are fetched *asynchronously* in these methods
    void name() const;
    void files() const;
    void remove(const QString &file) const;
signals:
    // Values are returned via signals
    void nameReady(const QString &name) const;
    void filesReady(const QStringList &files) const;
    void removeDone(bool success) const;
};

问题

我不确定这个模式是否正确,因为有很多点与我有关:

可能的解决方案

我想到的其他一些想法:

在 Qt 中设计这种异步包装器的正确方法是什么?

我认为命令模式可以在这里工作。

从抽象命令接口开始:

class Command : public QObject
{
    Q_OBJECT
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
signals:
    void done(bool success) const;
};

子类并没有那么复杂,只需给它们一些状态并覆盖 execute,例如

class NameCommand : public Command
{
    QString _name;
public:
    void execute() override
    {
        _name = ... //fetch name
        emit done(true);
    }
    QString name() const { return _name; }
};

class RemoveFileCommand : public Command
{
    QString _filename;
public:
    RemoveFileCommand(const QString filename) : _filename(filename){}
    void execute() override
    {
        //remove _filename

        emit done(true);
    }
};

通过这种方式,您可以构建一组执行多个不同操作的对象,但您可以实现一个命令路由器,并保持 运行 异步或非异步命令的机会:

class CommandRouter : public QObject
{
    Q_OBJECT
public:
    void run(Command * command, std::function<void(bool)> done, bool async = false)
    {
        connect(command, &Command::done, this, done, (async ? Qt::QueuedConnection : Qt::DirectConnection));
        if(async)
        {
            QtConcurrent::run(command, &Command::execute);
        }
        else
        {
            command->execute();
        }
    }
};

所以你最终会得到这样的结果:

    CommandRouter router;

    RemoveFileCommand removecommand("somefile.tar.gz");
    router.run(&removecommand, [](bool success) {

        qDebug() << "REMOVE " << (success ? "SUCCESSFUL" : "FAILED");

    }, true); //this will run asyncrounously

    NameCommand namecommand;
    router.run(&namecommand, [&namecommand](bool success) {

        if(success)
        {
            qDebug() << "Name: " + namecommand.name();
        }
        else
        {
            qDebug() << "FETCH NAME FAILED";
        }

    }; //this will block