/* * This file Copyright (C) 2016 Mnemosyne LLC * * It may be used under the GNU GPL versions 2 or 3 * or any future license endorsed by Mnemosyne LLC. * */ #pragma once #include #include #include #include #include #include #include #include #include "Macros.h" #include "RpcClient.h" class RpcQueue : public QObject { Q_OBJECT TR_DISABLE_COPY_MOVE(RpcQueue) public: explicit RpcQueue(QObject* parent = nullptr); void setTolerateErrors(bool tolerate_errors = true) { tolerate_errors_ = tolerate_errors; } template void add(Func func) { queue_.enqueue(qMakePair(normalizeFunc(func), ErrorHandlerFunction())); } template void add(Func func, ErrorHandler error_handler) { queue_.enqueue(qMakePair(normalizeFunc(func), normalizeErrorHandler(error_handler))); } // The first function in queue is ran synchronously // (hence it may be e. g. a lambda capturing local variables by reference). void run(); using Tag = uint64_t; Tag tag() const { return tag_; } private: // Internally queued function. Takes the last response future, makes a // request and returns a new response future. using QueuedFunction = std::function; // Internally stored error handler function. Takes the last response future and returns nothing. using ErrorHandlerFunction = std::function; void runNext(RpcResponseFuture const& response); // These overloads convert various forms of input closures to what we store internally. // normal closure, takes response and returns new future template, RpcResponseFuture> >::type* = nullptr> QueuedFunction normalizeFunc(Func const& func) const { return [func](RpcResponseFuture const& r) { return func(r.result()); }; } // closure without argument (first step), takes nothing and returns new future template, RpcResponseFuture> >::type* = nullptr> QueuedFunction normalizeFunc(Func const& func) const { return [func](RpcResponseFuture const&) { return func(); }; } // closure without return value ("auxiliary"), takes response and returns nothing -- internally we reuse the last future template, void> >::type* = nullptr> QueuedFunction normalizeFunc(Func const& func) const { return [func](RpcResponseFuture const& r) { func(r.result()); return r; }; } // closure without argument and return value, takes nothing and returns nothing -- next function will also get nothing template, void> >::type* = nullptr> QueuedFunction normalizeFunc(Func const& func) const { return [func](RpcResponseFuture const& r) { func(); return r; }; } // normal error handler, takes last response template, void> >::type* = nullptr> ErrorHandlerFunction normalizeErrorHandler(Func const& func) const { return [func](RpcResponseFuture const& r) { func(r.result()); }; } // error handler without an argument, takes nothing template, void> >::type* = nullptr> ErrorHandlerFunction normalizeErrorHandler(Func const& func) const { return [func](RpcResponseFuture const&) { func(); }; } Tag const tag_; static Tag next_tag; bool tolerate_errors_ = {}; QFutureInterface promise_; QQueue> queue_; ErrorHandlerFunction next_error_handler_; QFutureWatcher future_watcher_; private slots: void stepFinished(); };