LeechCraft  0.6.70-9312-g4cc613a2df
Modular cross-platform feature rich live environment.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
futures.h
Go to the documentation of this file.
1 /**********************************************************************
2  * LeechCraft - modular cross-platform feature rich internet client.
3  * Copyright (C) 2006-2014 Georg Rudoy
4  *
5  * Boost Software License - Version 1.0 - August 17th, 2003
6  *
7  * Permission is hereby granted, free of charge, to any person or organization
8  * obtaining a copy of the software and accompanying documentation covered by
9  * this license (the "Software") to use, reproduce, display, distribute,
10  * execute, and transmit the Software, and to prepare derivative works of the
11  * Software, and to permit third-parties to whom the Software is furnished to
12  * do so, all subject to the following:
13  *
14  * The copyright notices in the Software and this entire statement, including
15  * the above license grant, this restriction and the following disclaimer,
16  * must be included in all copies of the Software, in whole or in part, and
17  * all derivative works of the Software, unless such copies or derivative
18  * works are solely in the form of machine-executable object code generated by
19  * a source language processor.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  **********************************************************************/
29 
30 #pragma once
31 
32 #include <type_traits>
33 #include <functional>
34 #include <memory>
35 #include <boost/optional.hpp>
36 #include <QFutureInterface>
37 #include <QFutureWatcher>
38 #include <util/sll/oldcppkludges.h>
39 #include <util/sll/slotclosure.h>
40 #include "threadsconfig.h"
41 #include "concurrentexception.h"
42 
43 namespace LeechCraft
44 {
45 namespace Util
46 {
47  template<typename R, typename F, typename... Args>
48  std::enable_if_t<!std::is_same<R, void>::value>
49  ReportFutureResult (QFutureInterface<R>& iface, F&& f, Args&&... args)
50  {
51  try
52  {
53  const auto result = Invoke (std::forward<F> (f), std::forward<Args> (args)...);
54  iface.reportFinished (&result);
55  }
56  catch (const QtException_t& e)
57  {
58  iface.reportException (e);
59  iface.reportFinished ();
60  }
61  catch (const std::exception& e)
62  {
63  iface.reportException (ConcurrentStdException { e });
64  iface.reportFinished ();
65  }
66  }
67 
68  template<typename F, typename... Args>
69  void ReportFutureResult (QFutureInterface<void>& iface, F&& f, Args&&... args)
70  {
71  try
72  {
73  Invoke (std::forward<F> (f), std::forward<Args> (args)...);
74  }
75  catch (const QtException_t& e)
76  {
77  iface.reportException (e);
78  }
79  catch (const std::exception& e)
80  {
81  iface.reportException (ConcurrentStdException { e });
82  }
83 
84  iface.reportFinished ();
85  }
86 
87  namespace detail
88  {
89  template<typename T>
90  constexpr bool IsCallableImpl (int, std::result_of_t<T ()>* = nullptr)
91  {
92  return true;
93  }
94 
95  template<typename T>
96  constexpr bool IsCallableImpl (float)
97  {
98  return false;
99  }
100 
101  template<typename T>
102  constexpr bool IsCallable ()
103  {
104  return IsCallableImpl<T> (0);
105  }
106  }
107 
108  template<typename R, typename U>
109  std::enable_if_t<std::is_constructible<R, U>::value && !detail::IsCallable<U> ()>
111  {
112  const R result { std::forward<U> (value) };
113  iface.reportFinished (&result);
114  }
115 
116  namespace detail
117  {
118  template<typename T>
120 
121  template<typename T>
123  {
124  using type = T;
125  };
126 
127  template<typename T>
128  struct UnwrapFutureType : UnwrapFutureTypeBase<std::decay_t<T>>
129  {
130  };
131  }
132 
133  template<typename T>
135 
136  namespace detail
137  {
138  template<typename T>
139  struct IsFuture
140  {
141  constexpr static bool Result_ = false;
142  };
143 
144  template<typename T>
145  struct IsFuture<QFuture<T>>
146  {
147  constexpr static bool Result_ = true;
148  };
149 
150  template<typename RetType, typename ResultHandler>
152  {
153  void operator() (const ResultHandler& rh, QFutureWatcher<RetType> *watcher) const
154  {
155  rh (watcher->result ());
156  }
157  };
158 
159  template<typename ResultHandler>
160  struct HandlerInvoker<void, ResultHandler>
161  {
162  void operator() (const ResultHandler& rh, QFutureWatcher<void>*) const
163  {
164  rh ();
165  }
166  };
167 
168  template<typename ResultHandler, typename RetType, typename = std::result_of_t<ResultHandler (RetType)>>
169  constexpr bool IsCompatibleImpl (int)
170  {
171  return true;
172  }
173 
174  template<typename, typename>
175  constexpr bool IsCompatibleImpl (float)
176  {
177  return false;
178  }
179 
180  template<typename ResultHandler, typename = std::result_of_t<ResultHandler ()>>
181  constexpr bool IsCompatibleImplVoid (int)
182  {
183  return true;
184  }
185 
186  template<typename>
187  constexpr bool IsCompatibleImplVoid (float)
188  {
189  return false;
190  }
191 
192  template<typename ResultHandler, typename RetType>
193  constexpr bool IsCompatible ()
194  {
195  return std::is_same<void, RetType>::value ?
196  IsCompatibleImplVoid<ResultHandler> (0) :
197  IsCompatibleImpl<ResultHandler, RetType> (0);
198  }
199  }
200 
224  template<typename Executor, typename ResultHandler, typename... Args>
225  void ExecuteFuture (Executor f, ResultHandler rh, QObject *parent, Args... args)
226  {
227  static_assert (detail::IsFuture<decltype (f (args...))>::Result_,
228  "The passed functor should return a QFuture.");
229 
230  // Don't replace result_of with decltype, this triggers a gcc bug leading to segfault:
231  // http://leechcraft.org:8080/job/leechcraft/=debian_unstable/1998/console
232  using RetType_t = UnwrapFutureType_t<typename std::result_of<Executor (Args...)>::type>;
233 
234  static_assert (detail::IsCompatible<ResultHandler, RetType_t> (),
235  "Executor's watcher type and result handler argument type are not compatible.");
236 
237  const auto watcher = new QFutureWatcher<RetType_t> { parent };
238 
240  {
241  [watcher, rh]
242  {
243  watcher->deleteLater ();
245  },
246  watcher,
247  SIGNAL (finished ()),
248  watcher
249  };
250 
251  watcher->setFuture (f (args...));
252  }
253 
254  namespace detail
255  {
265  template<typename Future>
266  class Sequencer final : public QObject
267  {
268  public:
273  private:
274  Future Future_;
275  QFutureWatcher<RetType_t> BaseWatcher_;
276  QFutureWatcherBase *LastWatcher_ = &BaseWatcher_;
277  public:
283  Sequencer (const Future& future, QObject *parent)
284  : QObject { parent }
285  , Future_ { future }
286  , BaseWatcher_ { this }
287  {
288  }
289 
295  void Start ()
296  {
297  BaseWatcher_.setFuture (Future_);
298  }
299 
320  template<typename RetT, typename ArgT>
321  void Then (const std::function<QFuture<RetT> (ArgT)>& action)
322  {
323  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
324  if (!last)
325  {
326  deleteLater ();
327  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
328  }
329 
330  const auto watcher = new QFutureWatcher<RetT> { this };
331  LastWatcher_ = watcher;
332 
334  {
335  [this, last, watcher, action]
336  {
337  if (static_cast<QObject*> (last) != &BaseWatcher_)
338  last->deleteLater ();
339  watcher->setFuture (action (last->result ()));
340  },
341  last,
342  SIGNAL (finished ()),
343  last
344  };
345  }
346 
367  template<typename ArgT>
368  void Then (const std::function<void (ArgT)>& action)
369  {
370  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
371  if (!last)
372  {
373  deleteLater ();
374  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
375  }
376 
378  {
379  [last, action, this]
380  {
381  action (last->result ());
382  deleteLater ();
383  },
384  LastWatcher_,
385  SIGNAL (finished ()),
386  LastWatcher_
387  };
388  }
389 
390  void Then (const std::function<void ()>& action)
391  {
392  const auto last = dynamic_cast<QFutureWatcher<void>*> (LastWatcher_);
393  if (!last)
394  {
395  deleteLater ();
396  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
397  }
398 
400  {
401  [action, this]
402  {
403  action ();
404  deleteLater ();
405  },
406  LastWatcher_,
407  SIGNAL (finished ()),
408  LastWatcher_
409  };
410  }
411 
412  template<typename Handler>
413  void MultipleResults (const Handler& handler,
414  const std::function<void ()>& finishHandler = {},
415  const std::function<void ()>& startHandler = {})
416  {
417  if (LastWatcher_ != &BaseWatcher_)
418  {
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" };
422  }
423 
424  connect (&BaseWatcher_,
425  &QFutureWatcherBase::resultReadyAt,
426  &BaseWatcher_,
427  [handler, this] (int index) { handler (BaseWatcher_.resultAt (index)); });
428 
429  if (finishHandler)
430  new Util::SlotClosure<Util::DeleteLaterPolicy>
431  {
432  finishHandler,
433  &BaseWatcher_,
434  SIGNAL (finished ()),
435  &BaseWatcher_
436  };
437 
438  if (startHandler)
439  new Util::SlotClosure<Util::DeleteLaterPolicy>
440  {
441  startHandler,
442  &BaseWatcher_,
443  SIGNAL (started ()),
444  &BaseWatcher_
445  };
446 
447  connect (&BaseWatcher_,
448  SIGNAL (finished ()),
449  this,
450  SLOT (deleteLater ()));
451  }
452  };
453 
454  template<typename T>
456 
457  struct EmptyDestructionTag;
458 
459  template<typename T>
460  using IsEmptyDestr_t = std::is_same<EmptyDestructionTag, T>;
461 
462  template<typename Ret, typename DestrType, typename = std::enable_if_t<IsEmptyDestr_t<DestrType>::value>>
463  void InvokeDestructionHandler (const std::function<DestrType ()>&, QFutureInterface<Ret>&, float)
464  {
465  }
466 
467  template<typename Ret, typename DestrType, typename = std::enable_if_t<!IsEmptyDestr_t<DestrType>::value>>
468  void InvokeDestructionHandler (const std::function<DestrType ()>& handler, QFutureInterface<Ret>& iface, int)
469  {
470  const auto res = handler ();
471  iface.reportFinished (&res);
472  }
473 
490  template<typename Ret, typename Future, typename DestructionTag>
492  {
493  template<typename, typename, typename>
494  friend class SequenceProxy;
495 
496  std::shared_ptr<void> ExecuteGuard_;
497  Sequencer<Future> * const Seq_;
498 
499  boost::optional<QFuture<Ret>> ThisFuture_;
500 
501  std::function<DestructionTag ()> DestrHandler_;
502 
503  SequenceProxy (const std::shared_ptr<void>& guard, Sequencer<Future> *seq,
504  const std::function<DestructionTag ()>& destrHandler)
505  : ExecuteGuard_ { guard }
506  , Seq_ { seq }
507  , DestrHandler_ { destrHandler }
508  {
509  }
510  public:
511  using Ret_t = Ret;
512 
519  : ExecuteGuard_ { nullptr, [sequencer] (void*) { sequencer->Start (); } }
520  , Seq_ { sequencer }
521  {
522  }
523 
529  SequenceProxy (const SequenceProxy& proxy) = delete;
530 
536  SequenceProxy (SequenceProxy&& proxy) = default;
537 
552  template<typename F>
553  auto Then (F&& f) -> SequenceProxy<UnwrapFutureType_t<decltype (f (std::declval<Ret> ()))>, Future, DestructionTag>
554  {
555  if (ThisFuture_)
556  throw std::runtime_error { "SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
557 
558  Seq_->template Then<UnwrapFutureType_t<decltype (f (std::declval<Ret> ()))>, Ret> (f);
559  return { ExecuteGuard_, Seq_, DestrHandler_ };
560  }
561 
574  template<typename F>
575  auto Then (F&& f) -> std::enable_if_t<std::is_same<void, decltype (f (std::declval<Ret> ()))>::value>
576  {
577  if (ThisFuture_)
578  throw std::runtime_error { "SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
579 
580  Seq_->template Then<Ret> (f);
581  }
582 
583  template<typename F>
584  auto Then (F&& f) -> std::enable_if_t<std::is_same<void, Ret>::value && std::is_same<void, decltype (f ())>::value>
585  {
586  if (ThisFuture_)
587  throw std::runtime_error { "SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
588 
589  Seq_->Then (std::function<void ()> { f });
590  }
591 
592  template<typename F>
593  auto operator>> (F&& f) -> decltype (this->Then (std::forward<F> (f)))
594  {
595  return Then (std::forward<F> (f));
596  }
597 
598  template<typename F>
600  {
601  static_assert (std::is_same<DestructionTag, EmptyDestructionTag>::value,
602  "Destruction handling function has been already set.");
603 
604  return { ExecuteGuard_, Seq_, std::forward<F> (f) };
605  }
606 
607  template<typename F>
608  void MultipleResults (F&& f)
609  {
610  Seq_->MultipleResults (std::forward<F> (f));
611  }
612 
613  template<typename F, typename Finish>
614  void MultipleResults (F&& f, Finish&& finish)
615  {
616  Seq_->MultipleResults (std::forward<F> (f),
617  std::forward<Finish> (finish));
618  }
619 
620  template<typename F, typename Finish, typename Start>
621  void MultipleResults (F&& f, Finish&& finish, Start&& start)
622  {
623  Seq_->MultipleResults (std::forward<F> (f),
624  std::forward<Finish> (finish),
625  std::forward<Start> (start));
626  }
627 
628  operator QFuture<Ret> ()
629  {
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.");
633 
634  if (ThisFuture_)
635  return *ThisFuture_;
636 
637  QFutureInterface<Ret> iface;
638  iface.reportStarted ();
639 
640  SlotClosure<DeleteLaterPolicy> *deleteGuard = nullptr;
641  if (!isEmptyDestr)
642  {
643  deleteGuard = new SlotClosure<DeleteLaterPolicy>
644  {
645  [destrHandler = DestrHandler_, iface] () mutable
646  {
647  if (iface.isFinished ())
648  return;
649 
650  InvokeDestructionHandler<Ret, DestructionTag> (destrHandler, iface, 0);
651  },
652  Seq_->parent (),
653  SIGNAL (destroyed ()),
654  Seq_
655  };
656  }
657 
658  Then ([deleteGuard, iface] (const Ret& ret) mutable
659  {
660  iface.reportFinished (&ret);
661 
662  delete deleteGuard;
663  });
664 
665  const auto& future = iface.future ();
666  ThisFuture_ = future;
667  return future;
668  }
669  };
670  }
671 
737  template<typename T>
738  detail::SequenceProxy<
739  detail::SequencerRetType_t<QFuture<T>>,
740  QFuture<T>,
741  detail::EmptyDestructionTag
742  >
743  Sequence (QObject *parent, const QFuture<T>& future)
744  {
745  return { new detail::Sequencer<QFuture<T>> { future, parent } };
746  }
747 
759  template<typename T>
761  {
762  QFutureInterface<T> iface;
763  iface.reportStarted ();
764  iface.reportFinished (&t);
765  return iface.future ();
766  }
767 }
768 }
void MultipleResults(const Handler &handler, const std::function< void()> &finishHandler={}, const std::function< void()> &startHandler={})
Definition: futures.h:413
QtConcurrent::Exception QtException_t
A proxy object allowing type-checked sequencing of actions and responsible for starting the initial a...
Definition: futures.h:491
void Start()
Starts the first action in the chain.
Definition: futures.h:295
void MultipleResults(F &&f, Finish &&finish)
Definition: futures.h:614
constexpr bool IsCompatible()
Definition: futures.h:193
std::enable_if_t<!std::is_same< R, void >::value > ReportFutureResult(QFutureInterface< R > &iface, F &&f, Args &&...args)
Definition: futures.h:49
static constexpr bool Result_
Definition: futures.h:141
void Then(const std::function< void()> &action)
Definition: futures.h:390
Sequencer(const Future &future, QObject *parent)
Constructs the sequencer.
Definition: futures.h:283
UnwrapFutureType_t< Future > RetType_t
The type instantinating the QFuture returned by the Executor.
Definition: futures.h:272
constexpr bool IsCompatibleImpl(int)
Definition: futures.h:169
void Then(const std::function< QFuture< RetT >(ArgT)> &action)
Chains the given asynchronous action.
Definition: futures.h:321
Incapsulates the sequencing logic of asynchronous actions.
Definition: futures.h:266
SequenceProxy(Sequencer< Future > *sequencer)
Constructs a sequencer proxy managing the given sequencer.
Definition: futures.h:518
typename Sequencer< T >::RetType_t SequencerRetType_t
Definition: futures.h:455
Executes a given functor upon a signal (or a list of signals).
Definition: slotclosure.h:100
auto Then(F &&f) -> SequenceProxy< UnwrapFutureType_t< decltype(f(std::declval< Ret >()))>, Future, DestructionTag >
Adds the functor f to the chain of actions.
Definition: futures.h:553
typename detail::UnwrapFutureType< T >::type UnwrapFutureType_t
Definition: futures.h:134
auto Then(F &&f) -> std::enable_if_t< std::is_same< void, Ret >::value &&std::is_same< void, decltype(f())>::value >
Definition: futures.h:584
void MultipleResults(F &&f, Finish &&finish, Start &&start)
Definition: futures.h:621
void InvokeDestructionHandler(const std::function< DestrType()> &, QFutureInterface< Ret > &, float)
Definition: futures.h:463
constexpr bool IsCallable()
Definition: futures.h:102
auto operator>>(F &&f) -> decltype(this->Then(std::forward< F >(f)))
Definition: futures.h:593
constexpr bool IsCallableImpl(int, std::result_of_t< T()> *=nullptr)
Definition: futures.h:90
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.
Definition: futures.h:575
SequenceProxy< Ret, Future, std::result_of_t< F()> > DestructionValue(F &&f)
Definition: futures.h:599
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.
Definition: futures.h:225
QFuture< T > MakeReadyFuture(const T &t)
Creates a ready future holding the given value.
Definition: futures.h:760
void operator()(const ResultHandler &rh, QFutureWatcher< RetType > *watcher) const
Definition: futures.h:153
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.
Definition: futures.h:743
auto Invoke(F &&f, Args &&...args) -> decltype(std::forward< F >(f)(std::forward< Args >(args)...))
Definition: oldcppkludges.h:40
void Then(const std::function< void(ArgT)> &action)
Chains the given asynchronous action and closes the chain.
Definition: futures.h:368
constexpr bool IsCompatibleImplVoid(int)
Definition: futures.h:181
std::is_same< EmptyDestructionTag, T > IsEmptyDestr_t
Definition: futures.h:460