LeechCraft  0.6.70-6645-gcd10d7e
Modular cross-platform feature rich live environment.
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 <QtConcurrentRun>
39 #include <util/sll/oldcppkludges.h>
40 #include <util/sll/slotclosure.h>
41 #include "concurrentexception.h"
42 
43 namespace LeechCraft
44 {
45 namespace Util
46 {
47  template<typename R, typename F, typename... Args>
48  EnableIf_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>
91 
92  template<typename T>
94  {
95  using type = T;
96  };
97 
98  template<typename T>
100  {
101  };
102 
103  template<typename T>
105 
106  template<typename T>
107  struct IsFuture
108  {
109  constexpr static bool Result_ = false;
110  };
111 
112  template<typename T>
113  struct IsFuture<QFuture<T>>
114  {
115  constexpr static bool Result_ = true;
116  };
117 
118  template<typename RetType, typename ResultHandler>
120  {
121  void operator() (const ResultHandler& rh, QFutureWatcher<RetType> *watcher) const
122  {
123  rh (watcher->result ());
124  }
125  };
126 
127  template<typename ResultHandler>
128  struct HandlerInvoker<void, ResultHandler>
129  {
130  void operator() (const ResultHandler& rh, QFutureWatcher<void>*) const
131  {
132  rh ();
133  }
134  };
135 
136  template<typename ResultHandler, typename RetType, typename = ResultOf_t<ResultHandler (RetType)>>
137  constexpr bool IsCompatibleImpl (int)
138  {
139  return true;
140  }
141 
142  template<typename, typename>
143  constexpr bool IsCompatibleImpl (float)
144  {
145  return false;
146  }
147 
148  template<typename ResultHandler, typename = ResultOf_t<ResultHandler ()>>
149  constexpr bool IsCompatibleImplVoid (int)
150  {
151  return true;
152  }
153 
154  template<typename>
155  constexpr bool IsCompatibleImplVoid (float)
156  {
157  return false;
158  }
159 
160  template<typename ResultHandler, typename RetType>
161  constexpr bool IsCompatible ()
162  {
163  return std::is_same<void, RetType>::value ?
164  IsCompatibleImplVoid<ResultHandler> (0) :
165  IsCompatibleImpl<ResultHandler, RetType> (0);
166  }
167  }
168 
192  template<typename Executor, typename ResultHandler, typename... Args>
193  void ExecuteFuture (Executor f, ResultHandler rh, QObject *parent, Args... args)
194  {
195  static_assert (detail::IsFuture<decltype (f (args...))>::Result_,
196  "The passed functor should return a QFuture.");
197 
198  // Don't replace result_of with decltype, this triggers a gcc bug leading to segfault:
199  // http://leechcraft.org:8080/job/leechcraft/=debian_unstable/1998/console
200  using RetType_t = detail::UnwrapFutureType_t<typename std::result_of<Executor (Args...)>::type>;
201 
202  static_assert (detail::IsCompatible<ResultHandler, RetType_t> (),
203  "Executor's watcher type and result handler argument type are not compatible.");
204 
205  const auto watcher = new QFutureWatcher<RetType_t> { parent };
206 
208  {
209  [watcher, rh]
210  {
211  watcher->deleteLater ();
213  },
214  watcher,
215  SIGNAL (finished ()),
216  watcher
217  };
218 
219  watcher->setFuture (f (args...));
220  }
221 
222  namespace detail
223  {
236  template<typename Future>
237  class Sequencer : public QObject
238  {
239  public:
244  private:
245  Future Future_;
246  QFutureWatcher<RetType_t> BaseWatcher_;
247  QObject *LastWatcher_ = &BaseWatcher_;
248  public:
255  Sequencer (const Future& future, QObject *parent)
256  : QObject { parent }
257  , Future_ { future }
258  , BaseWatcher_ { this }
259  {
260  }
261 
267  void Start ()
268  {
269  BaseWatcher_.setFuture (Future_);
270  }
271 
292  template<typename RetT, typename ArgT>
293  void Then (const std::function<QFuture<RetT> (ArgT)>& action)
294  {
295  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
296  if (!last)
297  {
298  deleteLater ();
299  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
300  }
301 
302  const auto watcher = new QFutureWatcher<RetT> { this };
303  LastWatcher_ = watcher;
304 
306  {
307  [this, last, watcher, action]
308  {
309  if (static_cast<QObject*> (last) != &BaseWatcher_)
310  last->deleteLater ();
311  watcher->setFuture (action (last->result ()));
312  },
313  last,
314  SIGNAL (finished ()),
315  last
316  };
317  }
318 
339  template<typename ArgT>
340  void Then (const std::function<void (ArgT)>& action)
341  {
342  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
343  if (!last)
344  {
345  deleteLater ();
346  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
347  }
348 
350  {
351  [last, action, this]
352  {
353  action (last->result ());
354  deleteLater ();
355  },
356  LastWatcher_,
357  SIGNAL (finished ()),
358  LastWatcher_
359  };
360  }
361 
362  void Then (const std::function<void ()>& action)
363  {
364  const auto last = dynamic_cast<QFutureWatcher<void>*> (LastWatcher_);
365  if (!last)
366  {
367  deleteLater ();
368  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
369  }
370 
372  {
373  [last, action, this]
374  {
375  action ();
376  deleteLater ();
377  },
378  LastWatcher_,
379  SIGNAL (finished ()),
380  LastWatcher_
381  };
382  }
383  };
384 
385  template<typename T>
387 
388  struct EmptyDestructionTag;
389 
390  template<typename T>
391  using IsEmptyDestr_t = std::is_same<EmptyDestructionTag, T>;
392 
393  template<typename Ret, typename DestrType, typename = EnableIf_t<IsEmptyDestr_t<DestrType>::value>>
394  void InvokeDestructionHandler (const std::function<DestrType ()>&, QFutureInterface<Ret>&, float)
395  {
396  }
397 
398  template<typename Ret, typename DestrType, typename = EnableIf_t<!IsEmptyDestr_t<DestrType>::value>>
399  void InvokeDestructionHandler (const std::function<DestrType ()>& handler, QFutureInterface<Ret>& iface, int)
400  {
401  const auto res = handler ();
402  iface.reportFinished (&res);
403  }
404 
421  template<typename Ret, typename Future, typename DestructionTag>
423  {
424  template<typename, typename, typename>
425  friend class SequenceProxy;
426 
427  std::shared_ptr<void> ExecuteGuard_;
428  Sequencer<Future> * const Seq_;
429 
430  boost::optional<QFuture<Ret>> ThisFuture_;
431 
432  std::function<DestructionTag ()> DestrHandler_;
433 
434  SequenceProxy (const std::shared_ptr<void>& guard, Sequencer<Future> *seq,
435  const std::function<DestructionTag ()>& destrHandler)
436  : ExecuteGuard_ { guard }
437  , Seq_ { seq }
438  , DestrHandler_ { destrHandler }
439  {
440  }
441  public:
442  using Ret_t = Ret;
443 
450  : ExecuteGuard_ { nullptr, [sequencer] (void*) { sequencer->Start (); } }
451  , Seq_ { sequencer }
452  {
453  }
454 
460  SequenceProxy (const SequenceProxy& proxy) = delete;
461 
467  SequenceProxy (SequenceProxy&& proxy) = default;
468 
483  template<typename F>
484  auto Then (F&& f) -> SequenceProxy<UnwrapFutureType_t<decltype (f (std::declval<Ret> ()))>, Future, DestructionTag>
485  {
486  if (ThisFuture_)
487  throw std::runtime_error { "SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
488 
489  Seq_->template Then<UnwrapFutureType_t<decltype (f (std::declval<Ret> ()))>, Ret> (f);
490  return { ExecuteGuard_, Seq_, DestrHandler_ };
491  }
492 
505  template<typename F>
507  {
508  if (ThisFuture_)
509  throw std::runtime_error { "SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
510 
511  Seq_->template Then<Ret> (f);
512  }
513 
514  template<typename F>
515  auto Then (F&& f) -> EnableIf_t<std::is_same<void, Ret>::value && std::is_same<void, decltype (f ())>::value>
516  {
517  if (ThisFuture_)
518  throw std::runtime_error { "SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
519 
520  Seq_->Then (std::function<void ()> { f });
521  }
522 
523  template<typename F>
524  auto operator>> (F&& f) -> decltype (this->Then (std::forward<F> (f)))
525  {
526  return Then (std::forward<F> (f));
527  }
528 
529  template<typename F>
531  {
532  static_assert (std::is_same<DestructionTag, EmptyDestructionTag>::value,
533  "Destruction handling function has been already set.");
534 
535  return { ExecuteGuard_, Seq_, std::forward<F> (f) };
536  }
537 
538  operator QFuture<Ret> ()
539  {
540  constexpr bool isEmptyDestr = std::is_same<DestructionTag, EmptyDestructionTag>::value;
541  static_assert (std::is_same<DestructionTag, Ret>::value || isEmptyDestr,
542  "Destruction handler's return type doesn't match expected future type.");
543 
544  if (ThisFuture_)
545  return *ThisFuture_;
546 
547  QFutureInterface<Ret> iface;
548  iface.reportStarted ();
549 
550  SlotClosure<DeleteLaterPolicy> *deleteGuard = nullptr;
551  if (!isEmptyDestr)
552  {
553  // TODO C++14 capture the copy directly
554  const auto destrHandler = DestrHandler_;
555  deleteGuard = new SlotClosure<DeleteLaterPolicy>
556  {
557  [destrHandler, iface] () mutable
558  {
559  if (iface.isFinished ())
560  return;
561 
562  InvokeDestructionHandler<Ret, DestructionTag> (destrHandler, iface, 0);
563  },
564  Seq_->parent (),
565  SIGNAL (destroyed ()),
566  Seq_
567  };
568  }
569 
570  Then ([deleteGuard, iface] (const Ret& ret) mutable
571  {
572  iface.reportFinished (&ret);
573 
574  delete deleteGuard;
575  });
576 
577  const auto& future = iface.future ();
578  ThisFuture_ = future;
579  return future;
580  }
581  };
582  }
583 
651  template<typename T>
654  QFuture<T>,
655  detail::EmptyDestructionTag
656  >
657  Sequence (QObject *parent, const QFuture<T>& future)
658  {
659  return { new detail::Sequencer<QFuture<T>> { future, parent } };
660  }
661 
673  template<typename T>
674  QFuture<T> MakeReadyFuture (const T& t)
675  {
676  QFutureInterface<T> iface;
677  iface.reportStarted ();
678  iface.reportFinished (&t);
679  return iface.future ();
680  }
681 }
682 }
QtConcurrent::Exception QtException_t
A proxy object allowing type-checked sequencing of actions and responsible for starting the initial a...
Definition: futures.h:422
void Start()
Starts the first action in the chain.
Definition: futures.h:267
auto operator>>(const MV &value, const F &f) -> decltype(Bind(value, f))
Definition: monad.h:92
EnableIf_t<!std::is_same< R, void >::value > ReportFutureResult(QFutureInterface< R > &iface, F &&f, Args &&...args)
Definition: futures.h:49
typename UnwrapFutureType< T >::type UnwrapFutureType_t
Definition: futures.h:104
constexpr bool IsCompatible()
Definition: futures.h:161
void Then(const std::function< void()> &action)
Definition: futures.h:362
Sequencer(const Future &future, QObject *parent)
Constructs the sequencer.
Definition: futures.h:255
auto Then(F &&f) -> EnableIf_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:506
UnwrapFutureType_t< Future > RetType_t
The type instantinating the QFuture returned by the Executor.
Definition: futures.h:243
SequenceProxy< Ret, Future, ResultOf_t< F()> > DestructionValue(F &&f)
Definition: futures.h:530
constexpr bool IsCompatibleImpl(int)
Definition: futures.h:137
void Then(const std::function< QFuture< RetT >(ArgT)> &action)
Chains the given asynchronous action.
Definition: futures.h:293
Incapsulates the sequencing logic of asynchronous actions.
Definition: futures.h:237
SequenceProxy(Sequencer< Future > *sequencer)
Constructs a sequencer proxy managing the given sequencer.
Definition: futures.h:449
typename Sequencer< T >::RetType_t SequencerRetType_t
Definition: futures.h:386
typename std::enable_if< B, T >::type EnableIf_t
Definition: oldcppkludges.h:67
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:484
void InvokeDestructionHandler(const std::function< DestrType()> &, QFutureInterface< Ret > &, float)
Definition: futures.h:394
auto Then(F &&f) -> EnableIf_t< std::is_same< void, Ret >::value &&std::is_same< void, decltype(f())>::value >
Definition: futures.h:515
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:193
QFuture< T > MakeReadyFuture(const T &t)
Creates a ready future holding the given value.
Definition: futures.h:674
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:657
auto Invoke(F &&f, Args &&...args) -> decltype(std::forward< F >(f)(std::forward< Args >(args)...))
Definition: oldcppkludges.h:39
void Then(const std::function< void(ArgT)> &action)
Chains the given asynchronous action and closes the chain.
Definition: futures.h:340
constexpr bool IsCompatibleImplVoid(int)
Definition: futures.h:149
std::is_same< EmptyDestructionTag, T > IsEmptyDestr_t
Definition: futures.h:391