32 #include <type_traits>
35 #include <boost/optional.hpp>
36 #include <QFutureInterface>
37 #include <QFutureWatcher>
47 template<
typename R,
typename F,
typename... Args>
48 std::enable_if_t<!std::is_same<R, void>::value>
53 const auto result =
Invoke (std::forward<F> (f), std::forward<Args> (args)...);
54 iface.reportFinished (&result);
58 iface.reportException (e);
59 iface.reportFinished ();
61 catch (
const std::exception& e)
64 iface.reportFinished ();
68 template<
typename F,
typename... Args>
73 Invoke (std::forward<F> (f), std::forward<Args> (args)...);
77 iface.reportException (e);
79 catch (
const std::exception& e)
84 iface.reportFinished ();
104 return IsCallableImpl<T> (0);
108 template<
typename R,
typename U>
109 std::enable_if_t<std::is_constructible<R, U>::value && !detail::IsCallable<U> ()>
112 const R result { std::forward<U> (value) };
113 iface.reportFinished (&result);
150 template<
typename RetType,
typename ResultHandler>
153 void operator() (
const ResultHandler& rh, QFutureWatcher<RetType> *watcher)
const
155 rh (watcher->result ());
159 template<
typename ResultHandler>
162 void operator() (
const ResultHandler& rh, QFutureWatcher<void>*)
const
168 template<
typename ResultHandler,
typename RetType,
typename = std::result_of_t<ResultHandler (RetType)>>
174 template<
typename,
typename>
180 template<
typename ResultHandler,
typename = std::result_of_t<ResultHandler ()>>
192 template<
typename ResultHandler,
typename RetType>
195 return std::is_same<void, RetType>::value ?
196 IsCompatibleImplVoid<ResultHandler> (0) :
197 IsCompatibleImpl<ResultHandler, RetType> (0);
224 template<
typename Executor,
typename ResultHandler,
typename... Args>
228 "The passed functor should return a QFuture.");
232 using RetType_t =
UnwrapFutureType_t<
typename std::result_of<Executor (Args...)>::type>;
234 static_assert (detail::IsCompatible<ResultHandler, RetType_t> (),
235 "Executor's watcher type and result handler argument type are not compatible.");
237 const auto watcher =
new QFutureWatcher<RetType_t> { parent };
243 watcher->deleteLater ();
247 SIGNAL (finished ()),
251 watcher->setFuture (f (args...));
265 template<
typename Future>
275 QFutureWatcher<RetType_t> BaseWatcher_;
276 QFutureWatcherBase *LastWatcher_ = &BaseWatcher_;
286 , BaseWatcher_ {
this }
297 BaseWatcher_.setFuture (Future_);
320 template<
typename RetT,
typename ArgT>
323 const auto last =
dynamic_cast<QFutureWatcher<ArgT>*
> (LastWatcher_);
327 throw std::runtime_error { std::string {
"invalid type in " } + Q_FUNC_INFO };
330 const auto watcher =
new QFutureWatcher<RetT> {
this };
331 LastWatcher_ = watcher;
335 [
this, last, watcher, action]
337 if (static_cast<QObject*> (last) != &BaseWatcher_)
338 last->deleteLater ();
339 watcher->setFuture (action (last->result ()));
342 SIGNAL (finished ()),
367 template<
typename ArgT>
368 void Then (
const std::function<
void (ArgT)>& action)
370 const auto last =
dynamic_cast<QFutureWatcher<ArgT>*
> (LastWatcher_);
374 throw std::runtime_error { std::string {
"invalid type in " } + Q_FUNC_INFO };
381 action (last->result ());
385 SIGNAL (finished ()),
390 void Then (
const std::function<
void ()>& action)
392 const auto last =
dynamic_cast<QFutureWatcher<void>*
> (LastWatcher_);
396 throw std::runtime_error { std::string {
"invalid type in " } + Q_FUNC_INFO };
407 SIGNAL (finished ()),
412 template<
typename Handler>
414 const std::function<
void ()>& finishHandler = {},
415 const std::function<void ()>& startHandler = {})
417 if (LastWatcher_ != &BaseWatcher_)
419 qWarning () << Q_FUNC_INFO
420 <<
"multiple results handler should be chained directly to the source";
421 throw std::runtime_error {
"invalid multiple results handler chaining" };
424 connect (&BaseWatcher_,
425 &QFutureWatcherBase::resultReadyAt,
427 [handler,
this] (
int index) { handler (BaseWatcher_.resultAt (index)); });
430 new Util::SlotClosure<Util::DeleteLaterPolicy>
434 SIGNAL (finished ()),
439 new Util::SlotClosure<Util::DeleteLaterPolicy>
447 connect (&BaseWatcher_,
448 SIGNAL (finished ()),
450 SLOT (deleteLater ()));
457 struct EmptyDestructionTag;
462 template<typename Ret, typename DestrType, typename = std::enable_if_t<IsEmptyDestr_t<DestrType>::value>>
467 template<typename Ret, typename DestrType, typename = std::enable_if_t<!IsEmptyDestr_t<DestrType>::value>>
470 const auto res = handler ();
471 iface.reportFinished (&res);
490 template<
typename Ret,
typename Future,
typename DestructionTag>
493 template<
typename,
typename,
typename>
496 std::shared_ptr<void> ExecuteGuard_;
499 boost::optional<QFuture<Ret>> ThisFuture_;
501 std::function<DestructionTag ()> DestrHandler_;
504 const std::function<DestructionTag ()>& destrHandler)
505 : ExecuteGuard_ { guard }
507 , DestrHandler_ { destrHandler }
519 : ExecuteGuard_ {
nullptr, [sequencer] (
void*) { sequencer->
Start (); } }
556 throw std::runtime_error {
"SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
558 Seq_->template Then<UnwrapFutureType_t<decltype (f (std::declval<Ret> ()))>, Ret> (f);
559 return { ExecuteGuard_, Seq_, DestrHandler_ };
575 auto Then (F&& f) -> std::enable_if_t<std::is_same<void, decltype (f (std::declval<Ret> ()))>::value>
578 throw std::runtime_error {
"SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
580 Seq_->template Then<Ret> (f);
584 auto Then (F&& f) -> std::enable_if_t<std::is_same<void, Ret>::value && std::is_same<void, decltype (f ())>::value>
587 throw std::runtime_error {
"SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
589 Seq_->Then (std::function<
void ()> { f });
595 return Then (std::forward<F> (f));
601 static_assert (std::is_same<DestructionTag, EmptyDestructionTag>::value,
602 "Destruction handling function has been already set.");
604 return { ExecuteGuard_, Seq_, std::forward<F> (f) };
610 Seq_->MultipleResults (std::forward<F> (f));
613 template<
typename F,
typename Finish>
616 Seq_->MultipleResults (std::forward<F> (f),
617 std::forward<Finish> (finish));
620 template<
typename F,
typename Finish,
typename Start>
623 Seq_->MultipleResults (std::forward<F> (f),
624 std::forward<Finish> (finish),
625 std::forward<Start> (start));
630 constexpr
bool isEmptyDestr = std::is_same<DestructionTag, EmptyDestructionTag>::value;
631 static_assert (std::is_same<DestructionTag, Ret>::value || isEmptyDestr,
632 "Destruction handler's return type doesn't match expected future type.");
638 iface.reportStarted ();
645 [destrHandler = DestrHandler_, iface] ()
mutable
647 if (iface.isFinished ())
650 InvokeDestructionHandler<Ret, DestructionTag> (destrHandler, iface, 0);
653 SIGNAL (destroyed ()),
658 Then ([deleteGuard, iface] (
const Ret& ret)
mutable
660 iface.reportFinished (&ret);
665 const auto& future = iface.future ();
666 ThisFuture_ = future;
738 detail::SequenceProxy<
739 detail::SequencerRetType_t<QFuture<T>>,
741 detail::EmptyDestructionTag
763 iface.reportStarted ();
764 iface.reportFinished (&t);
765 return iface.future ();
void MultipleResults(const Handler &handler, const std::function< void()> &finishHandler={}, const std::function< void()> &startHandler={})
QtConcurrent::Exception QtException_t
A proxy object allowing type-checked sequencing of actions and responsible for starting the initial a...
void Start()
Starts the first action in the chain.
void MultipleResults(F &&f, Finish &&finish)
constexpr bool IsCompatible()
std::enable_if_t<!std::is_same< R, void >::value > ReportFutureResult(QFutureInterface< R > &iface, F &&f, Args &&...args)
static constexpr bool Result_
void Then(const std::function< void()> &action)
Sequencer(const Future &future, QObject *parent)
Constructs the sequencer.
UnwrapFutureType_t< Future > RetType_t
The type instantinating the QFuture returned by the Executor.
constexpr bool IsCompatibleImpl(int)
void Then(const std::function< QFuture< RetT >(ArgT)> &action)
Chains the given asynchronous action.
Incapsulates the sequencing logic of asynchronous actions.
SequenceProxy(Sequencer< Future > *sequencer)
Constructs a sequencer proxy managing the given sequencer.
typename Sequencer< T >::RetType_t SequencerRetType_t
Executes a given functor upon a signal (or a list of signals).
auto Then(F &&f) -> SequenceProxy< UnwrapFutureType_t< decltype(f(std::declval< Ret >()))>, Future, DestructionTag >
Adds the functor f to the chain of actions.
typename detail::UnwrapFutureType< T >::type UnwrapFutureType_t
auto Then(F &&f) -> std::enable_if_t< std::is_same< void, Ret >::value &&std::is_same< void, decltype(f())>::value >
void MultipleResults(F &&f)
void MultipleResults(F &&f, Finish &&finish, Start &&start)
void InvokeDestructionHandler(const std::function< DestrType()> &, QFutureInterface< Ret > &, float)
constexpr bool IsCallable()
auto operator>>(F &&f) -> decltype(this->Then(std::forward< F >(f)))
constexpr bool IsCallableImpl(int, std::result_of_t< T()> *=nullptr)
auto Then(F &&f) -> std::enable_if_t< std::is_same< void, decltype(f(std::declval< Ret >()))>::value >
Adds the funtor f to the chain of actions and closes the chain.
SequenceProxy< Ret, Future, std::result_of_t< F()> > DestructionValue(F &&f)
A concurrent exception that plays nicely with Qt.
void ExecuteFuture(Executor f, ResultHandler rh, QObject *parent, Args...args)
Runs a QFuture-returning function and feeding the future to a handler when it is ready.
friend class SequenceProxy
QFuture< T > MakeReadyFuture(const T &t)
Creates a ready future holding the given value.
void operator()(const ResultHandler &rh, QFutureWatcher< RetType > *watcher) const
detail::SequenceProxy< detail::SequencerRetType_t< QFuture< T > >, QFuture< T >, detail::EmptyDestructionTag > Sequence(QObject *parent, const QFuture< T > &future)
Creates a sequencer that allows chaining multiple futures.
auto Invoke(F &&f, Args &&...args) -> decltype(std::forward< F >(f)(std::forward< Args >(args)...))
void Then(const std::function< void(ArgT)> &action)
Chains the given asynchronous action and closes the chain.
constexpr bool IsCompatibleImplVoid(int)
std::is_same< EmptyDestructionTag, T > IsEmptyDestr_t