LeechCraft  0.6.70-10870-g558588d6ec
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 <util/sll/slotclosure.h>
39 #include <util/sll/detector.h>
40 #include "threadsconfig.h"
41 #include "concurrentexception.h"
42 
43 namespace LeechCraft
44 {
45 namespace Util
46 {
47  namespace detail
48  {
49  template<typename T, typename... Args>
50  using CallableDetector_t = std::result_of_t<T (Args...)>; // C++17
51  }
52 
53  template<typename R, typename F, typename... Args>
54  void ReportFutureResult (QFutureInterface<R>& iface, F&& f, Args&&... args)
55  {
56  try
57  {
58  constexpr bool isVoid = std::is_same<R, void> {}; // C++17
59  if constexpr (!isVoid && !IsDetected_v<detail::CallableDetector_t, std::decay_t<F>, Args...>)
60  {
61  static_assert (std::is_constructible<R, F> {}); // C++17
62  static_assert (sizeof... (Args) == 0,
63  "Extra args when a value is passed. Perhaps you wanted to pass in a function?");
64 
65  const R result { std::forward<F> (f) };
66  iface.reportResult (result);
67  }
68  else if constexpr (!isVoid)
69  {
70  const auto result = std::invoke (std::forward<F> (f), std::forward<Args> (args)...);
71  iface.reportResult (result);
72  }
73  else
74  std::invoke (std::forward<F> (f), std::forward<Args> (args)...);
75  }
76  catch (const QtException_t& e)
77  {
78  iface.reportException (e);
79  }
80  catch (const std::exception& e)
81  {
82  iface.reportException (ConcurrentStdException { e });
83  }
84 
85  iface.reportFinished ();
86  }
87 
88  namespace detail
89  {
90  template<typename T>
91  struct UnwrapFutureTypeBase {};
92 
93  template<typename T>
94  struct UnwrapFutureTypeBase<QFuture<T>>
95  {
96  using type = T;
97  };
98 
99  template<typename T>
100  struct UnwrapFutureType : UnwrapFutureTypeBase<std::decay_t<T>>
101  {
102  };
103  }
104 
105  template<typename T>
106  using UnwrapFutureType_t = typename detail::UnwrapFutureType<T>::type;
107 
108  namespace detail
109  {
119  template<typename Future>
120  class Sequencer final : public QObject
121  {
122  public:
126  using RetType_t = UnwrapFutureType_t<Future>;
127  private:
128  Future Future_;
129  QFutureWatcher<RetType_t> BaseWatcher_;
130  QFutureWatcherBase *LastWatcher_ = &BaseWatcher_;
131  public:
137  Sequencer (const Future& future, QObject *parent)
138  : QObject { parent }
139  , Future_ { future }
140  , BaseWatcher_ { this }
141  {
142  }
143 
149  void Start ()
150  {
151  connect (LastWatcher_,
152  &QFutureWatcherBase::finished,
153  this,
154  &QObject::deleteLater);
155  BaseWatcher_.setFuture (Future_);
156  }
157 
178  template<typename RetT, typename ArgT>
179  void Then (const std::function<QFuture<RetT> (ArgT)>& action)
180  {
181  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
182  if (!last)
183  {
184  deleteLater ();
185  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
186  }
187 
188  const auto watcher = new QFutureWatcher<RetT> { this };
189  LastWatcher_ = watcher;
190 
191  new SlotClosure<DeleteLaterPolicy>
192  {
193  [this, last, watcher, action]
194  {
195  if (static_cast<QObject*> (last) != &BaseWatcher_)
196  last->deleteLater ();
197  watcher->setFuture (action (last->result ()));
198  },
199  last,
200  SIGNAL (finished ()),
201  last
202  };
203  }
204 
225  template<typename ArgT>
226  void Then (const std::function<void (ArgT)>& action)
227  {
228  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
229  if (!last)
230  {
231  deleteLater ();
232  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
233  }
234 
235  new SlotClosure<DeleteLaterPolicy>
236  {
237  [last, action]
238  {
239  action (last->result ());
240  },
241  LastWatcher_,
242  SIGNAL (finished ()),
243  LastWatcher_
244  };
245  }
246 
247  void Then (const std::function<void ()>& action)
248  {
249  const auto last = dynamic_cast<QFutureWatcher<void>*> (LastWatcher_);
250  if (!last)
251  {
252  deleteLater ();
253  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
254  }
255 
256  new SlotClosure<DeleteLaterPolicy>
257  {
258  action,
259  LastWatcher_,
260  SIGNAL (finished ()),
261  LastWatcher_
262  };
263  }
264 
265  template<typename Handler>
266  void MultipleResults (const Handler& handler,
267  const std::function<void ()>& finishHandler = {},
268  const std::function<void ()>& startHandler = {})
269  {
270  if (LastWatcher_ != &BaseWatcher_)
271  {
272  qWarning () << Q_FUNC_INFO
273  << "multiple results handler should be chained directly to the source";
274  throw std::runtime_error { "invalid multiple results handler chaining" };
275  }
276 
277  connect (&BaseWatcher_,
278  &QFutureWatcherBase::resultReadyAt,
279  &BaseWatcher_,
280  [handler, this] (int index) { handler (BaseWatcher_.resultAt (index)); });
281 
282  if (finishHandler)
283  new Util::SlotClosure<Util::DeleteLaterPolicy>
284  {
285  finishHandler,
286  &BaseWatcher_,
287  SIGNAL (finished ()),
288  &BaseWatcher_
289  };
290 
291  if (startHandler)
292  new Util::SlotClosure<Util::DeleteLaterPolicy>
293  {
294  startHandler,
295  &BaseWatcher_,
296  SIGNAL (started ()),
297  &BaseWatcher_
298  };
299 
300  connect (&BaseWatcher_,
301  SIGNAL (finished ()),
302  this,
303  SLOT (deleteLater ()));
304  }
305  };
306 
307  template<typename T>
308  using SequencerRetType_t = typename Sequencer<T>::RetType_t;
309 
310  struct EmptyDestructionTag;
311 
328  template<typename Ret, typename Future, typename DestructionTag>
329  class SequenceProxy
330  {
331  template<typename, typename, typename>
332  friend class SequenceProxy;
333 
334  std::shared_ptr<void> ExecuteGuard_;
335  Sequencer<Future> * const Seq_;
336 
337  boost::optional<QFuture<Ret>> ThisFuture_;
338 
339  std::function<DestructionTag ()> DestrHandler_;
340 
341  SequenceProxy (const std::shared_ptr<void>& guard, Sequencer<Future> *seq,
342  const std::function<DestructionTag ()>& destrHandler)
343  : ExecuteGuard_ { guard }
344  , Seq_ { seq }
345  , DestrHandler_ { destrHandler }
346  {
347  }
348 
349  template<typename F1, typename Ret1>
350  using ReturnsFutureDetector_t = UnwrapFutureType_t<std::result_of_t<F1 (Ret1)>>;
351 
352  template<typename F, typename... Args>
353  using ReturnsVoidDetector_t = std::result_of_t<F (Args...)>;
354  public:
355  using Ret_t = Ret;
356 
362  SequenceProxy (Sequencer<Future> *sequencer)
363  : ExecuteGuard_ { nullptr, [sequencer] (void*) { sequencer->Start (); } }
364  , Seq_ { sequencer }
365  {
366  }
367 
373  SequenceProxy (const SequenceProxy& proxy) = delete;
374 
380  SequenceProxy (SequenceProxy&& proxy) = default;
381 
388  template<typename F>
389  auto Then (F&& f)
390  {
391  if (ThisFuture_)
392  throw std::runtime_error { "SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
393 
394  if constexpr (IsDetected_v<ReturnsFutureDetector_t, F, Ret>)
395  {
396  using Next_t = UnwrapFutureType_t<decltype (f (std::declval<Ret> ()))>;
397  Seq_->template Then<Next_t, Ret> (f);
398  return SequenceProxy<Next_t, Future, DestructionTag> { ExecuteGuard_, Seq_, DestrHandler_ };
399  }
400  else if constexpr (std::is_same<IsDetected_t<struct Dummy, ReturnsVoidDetector_t, F, Ret>, void> {})
401  Seq_->template Then<Ret> (f);
402  else if constexpr (std::is_same<void, Ret>::value &&
403  std::is_same<IsDetected_t<struct Dummy, ReturnsVoidDetector_t, F>, void> {})
404  Seq_->Then (std::function<void ()> { f });
405  else
406  static_assert (std::is_same<F, struct Dummy> {}, "Invalid functor passed to SequenceProxy::Then()");
407  }
408 
409  template<typename F>
410  auto operator>> (F&& f) -> decltype (this->Then (std::forward<F> (f)))
411  {
412  return Then (std::forward<F> (f));
413  }
414 
415  template<typename F>
416  SequenceProxy<Ret, Future, std::result_of_t<F ()>> DestructionValue (F&& f)
417  {
418  static_assert (std::is_same<DestructionTag, EmptyDestructionTag>::value,
419  "Destruction handling function has been already set.");
420 
421  return { ExecuteGuard_, Seq_, std::forward<F> (f) };
422  }
423 
424  template<typename F>
425  void MultipleResults (F&& f)
426  {
427  Seq_->MultipleResults (std::forward<F> (f));
428  }
429 
430  template<typename F, typename Finish>
431  void MultipleResults (F&& f, Finish&& finish)
432  {
433  Seq_->MultipleResults (std::forward<F> (f),
434  std::forward<Finish> (finish));
435  }
436 
437  template<typename F, typename Finish, typename Start>
438  void MultipleResults (F&& f, Finish&& finish, Start&& start)
439  {
440  Seq_->MultipleResults (std::forward<F> (f),
441  std::forward<Finish> (finish),
442  std::forward<Start> (start));
443  }
444 
445  operator QFuture<Ret> ()
446  {
447  constexpr bool isEmptyDestr = std::is_same<DestructionTag, EmptyDestructionTag>::value;
448  static_assert (std::is_same<DestructionTag, Ret>::value || isEmptyDestr,
449  "Destruction handler's return type doesn't match expected future type.");
450 
451  if (ThisFuture_)
452  return *ThisFuture_;
453 
454  QFutureInterface<Ret> iface;
455  iface.reportStarted ();
456 
457  SlotClosure<DeleteLaterPolicy> *deleteGuard = nullptr;
458  if constexpr (!isEmptyDestr)
459  {
460  deleteGuard = new SlotClosure<DeleteLaterPolicy>
461  {
462  [destrHandler = DestrHandler_, iface] () mutable
463  {
464  if (iface.isFinished ())
465  return;
466 
467  const auto res = destrHandler ();
468  iface.reportFinished (&res);
469  },
470  Seq_->parent (),
471  SIGNAL (destroyed ()),
472  Seq_
473  };
474  }
475 
476  Then ([deleteGuard, iface] (const Ret& ret) mutable
477  {
478  iface.reportFinished (&ret);
479 
480  delete deleteGuard;
481  });
482 
483  const auto& future = iface.future ();
484  ThisFuture_ = future;
485  return future;
486  }
487  };
488  }
489 
555  template<typename T>
556  detail::SequenceProxy<
557  detail::SequencerRetType_t<QFuture<T>>,
558  QFuture<T>,
559  detail::EmptyDestructionTag
560  >
561  Sequence (QObject *parent, const QFuture<T>& future)
562  {
563  return { new detail::Sequencer<QFuture<T>> { future, parent } };
564  }
565 
577  template<typename T>
578  QFuture<T> MakeReadyFuture (const T& t)
579  {
580  QFutureInterface<T> iface;
581  iface.reportStarted ();
582  iface.reportFinished (&t);
583  return iface.future ();
584  }
585 }
586 }
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition: oral.h:931
auto operator>>(const MV &value, const F &f) -> decltype(Bind(value, f))
Definition: monad.h:91
constexpr bool IsDetected_v
Definition: detector.h:56
std::conditional_t< std::is_same_v< detail::RetTypeRaw_t< F >, detail::ReturnsVoid >, void, detail::RetTypeRaw_t< F > > RetType_t
Definition: typegetter.h:77
Util::ConcurrentException< Util::NewType< std::exception, 0, __LINE__ > > ConcurrentStdException