LeechCraft  0.6.70-13729-g7046a9d2a7
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 LC
44 {
45 namespace Util
46 {
47  template<typename R, typename F, typename... Args>
48  void ReportFutureResult (QFutureInterface<R>& iface, F&& f, Args&&... args)
49  {
50  try
51  {
52  constexpr bool isVoid = std::is_same_v<R, void>;
53  if constexpr (!isVoid && !std::is_invocable_v<std::decay_t<F>, Args...>)
54  {
55  static_assert (std::is_constructible_v<R, F>);
56  static_assert (sizeof... (Args) == 0,
57  "Extra args when a value is passed. Perhaps you wanted to pass in a function?");
58 
59  const R result { std::forward<F> (f) };
60  iface.reportResult (result);
61  }
62  else if constexpr (!isVoid)
63  {
64  const auto result = std::invoke (std::forward<F> (f), std::forward<Args> (args)...);
65  iface.reportResult (result);
66  }
67  else
68  std::invoke (std::forward<F> (f), std::forward<Args> (args)...);
69  }
70  catch (const QtException_t& e)
71  {
72  iface.reportException (e);
73  }
74  catch (const std::exception& e)
75  {
76  iface.reportException (ConcurrentStdException { e });
77  }
78 
79  iface.reportFinished ();
80  }
81 
82  namespace detail
83  {
84  template<typename T>
85  struct UnwrapFutureTypeBase {};
86 
87  template<typename T>
88  struct UnwrapFutureTypeBase<QFuture<T>>
89  {
90  using type = T;
91  };
92 
93  template<typename T>
94  struct UnwrapFutureType : UnwrapFutureTypeBase<std::decay_t<T>>
95  {
96  };
97  }
98 
99  template<typename T>
100  using UnwrapFutureType_t = typename detail::UnwrapFutureType<T>::type;
101 
102  namespace detail
103  {
113  template<typename Future>
114  class Sequencer final : public QObject
115  {
116  public:
120  using RetType_t = UnwrapFutureType_t<Future>;
121  private:
122  Future Future_;
123  QFutureWatcher<RetType_t> BaseWatcher_;
124  QFutureWatcherBase *LastWatcher_ = &BaseWatcher_;
125  public:
131  Sequencer (const Future& future, QObject *parent)
132  : QObject { parent }
133  , Future_ { future }
134  , BaseWatcher_ { this }
135  {
136  }
137 
143  void Start ()
144  {
145  connect (LastWatcher_,
146  &QFutureWatcherBase::finished,
147  this,
148  &QObject::deleteLater);
149  BaseWatcher_.setFuture (Future_);
150  }
151 
172  template<typename RetT, typename ArgT>
173  void Then (const std::function<QFuture<RetT> (ArgT)>& action)
174  {
175  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
176  if (!last)
177  {
178  deleteLater ();
179  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
180  }
181 
182  const auto watcher = new QFutureWatcher<RetT> { this };
183  LastWatcher_ = watcher;
184 
185  new SlotClosure<DeleteLaterPolicy>
186  {
187  [this, last, watcher, action]
188  {
189  if (static_cast<QObject*> (last) != &BaseWatcher_)
190  last->deleteLater ();
191  watcher->setFuture (action (last->result ()));
192  },
193  last,
194  SIGNAL (finished ()),
195  last
196  };
197  }
198 
219  template<typename ArgT>
220  void Then (const std::function<void (ArgT)>& action)
221  {
222  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
223  if (!last)
224  {
225  deleteLater ();
226  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
227  }
228 
229  new SlotClosure<DeleteLaterPolicy>
230  {
231  [last, action]
232  {
233  action (last->result ());
234  },
235  LastWatcher_,
236  SIGNAL (finished ()),
237  LastWatcher_
238  };
239  }
240 
241  void Then (const std::function<void ()>& action)
242  {
243  const auto last = dynamic_cast<QFutureWatcher<void>*> (LastWatcher_);
244  if (!last)
245  {
246  deleteLater ();
247  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
248  }
249 
250  new SlotClosure<DeleteLaterPolicy>
251  {
252  action,
253  LastWatcher_,
254  SIGNAL (finished ()),
255  LastWatcher_
256  };
257  }
258 
259  template<typename Handler>
260  void MultipleResults (const Handler& handler,
261  const std::function<void ()>& finishHandler = {},
262  const std::function<void ()>& startHandler = {})
263  {
264  if (LastWatcher_ != &BaseWatcher_)
265  {
266  qWarning () << Q_FUNC_INFO
267  << "multiple results handler should be chained directly to the source";
268  throw std::runtime_error { "invalid multiple results handler chaining" };
269  }
270 
271  connect (&BaseWatcher_,
272  &QFutureWatcherBase::resultReadyAt,
273  &BaseWatcher_,
274  [handler, this] (int index) { handler (BaseWatcher_.resultAt (index)); });
275 
276  if (finishHandler)
277  new Util::SlotClosure<Util::DeleteLaterPolicy>
278  {
279  finishHandler,
280  &BaseWatcher_,
281  SIGNAL (finished ()),
282  &BaseWatcher_
283  };
284 
285  if (startHandler)
286  new Util::SlotClosure<Util::DeleteLaterPolicy>
287  {
288  startHandler,
289  &BaseWatcher_,
290  SIGNAL (started ()),
291  &BaseWatcher_
292  };
293 
294  connect (&BaseWatcher_,
295  SIGNAL (finished ()),
296  this,
297  SLOT (deleteLater ()));
298  }
299  };
300 
301  template<typename T>
302  using SequencerRetType_t = typename Sequencer<T>::RetType_t;
303 
304  struct EmptyDestructionTag;
305 
322  template<typename Ret, typename Future, typename DestructionTag>
323  class SequenceProxy
324  {
325  template<typename, typename, typename>
326  friend class SequenceProxy;
327 
328  std::shared_ptr<void> ExecuteGuard_;
329  Sequencer<Future> * const Seq_;
330 
331  boost::optional<QFuture<Ret>> ThisFuture_;
332 
333  std::function<DestructionTag ()> DestrHandler_;
334 
335  SequenceProxy (const std::shared_ptr<void>& guard, Sequencer<Future> *seq,
336  const std::function<DestructionTag ()>& destrHandler)
337  : ExecuteGuard_ { guard }
338  , Seq_ { seq }
339  , DestrHandler_ { destrHandler }
340  {
341  }
342 
343  template<typename F1, typename Ret1>
344  using ReturnsFutureDetector_t = UnwrapFutureType_t<std::result_of_t<F1 (Ret1)>>;
345 
346  template<typename F, typename... Args>
347  using ReturnsVoidDetector_t = std::result_of_t<F (Args...)>;
348  public:
349  using Ret_t = Ret;
350 
356  SequenceProxy (Sequencer<Future> *sequencer)
357  : ExecuteGuard_ { nullptr, [sequencer] (void*) { sequencer->Start (); } }
358  , Seq_ { sequencer }
359  {
360  }
361 
367  SequenceProxy (const SequenceProxy& proxy) = delete;
368 
374  SequenceProxy (SequenceProxy&& proxy) = default;
375 
382  template<typename F>
383  auto Then (F&& f)
384  {
385  if (ThisFuture_)
386  throw std::runtime_error { "SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
387 
388  if constexpr (IsDetected_v<ReturnsFutureDetector_t, F, Ret>)
389  {
390  using Next_t = UnwrapFutureType_t<decltype (f (std::declval<Ret> ()))>;
391  Seq_->template Then<Next_t, Ret> (f);
392  return SequenceProxy<Next_t, Future, DestructionTag> { ExecuteGuard_, Seq_, DestrHandler_ };
393  }
394  else if constexpr (std::is_same<IsDetected_t<struct Dummy, ReturnsVoidDetector_t, F, Ret>, void> {})
395  Seq_->template Then<Ret> (f);
396  else if constexpr (std::is_same<void, Ret>::value &&
397  std::is_same<IsDetected_t<struct Dummy, ReturnsVoidDetector_t, F>, void> {})
398  Seq_->Then (std::function<void ()> { f });
399  else
400  static_assert (std::is_same<F, struct Dummy> {}, "Invalid functor passed to SequenceProxy::Then()");
401  }
402 
403  template<typename F>
404  auto operator>> (F&& f) -> decltype (this->Then (std::forward<F> (f)))
405  {
406  return Then (std::forward<F> (f));
407  }
408 
409  template<typename F>
410  SequenceProxy<Ret, Future, std::result_of_t<F ()>> DestructionValue (F&& f)
411  {
412  static_assert (std::is_same<DestructionTag, EmptyDestructionTag>::value,
413  "Destruction handling function has been already set.");
414 
415  return { ExecuteGuard_, Seq_, std::forward<F> (f) };
416  }
417 
418  template<typename F>
419  void MultipleResults (F&& f)
420  {
421  Seq_->MultipleResults (std::forward<F> (f));
422  }
423 
424  template<typename F, typename Finish>
425  void MultipleResults (F&& f, Finish&& finish)
426  {
427  Seq_->MultipleResults (std::forward<F> (f),
428  std::forward<Finish> (finish));
429  }
430 
431  template<typename F, typename Finish, typename Start>
432  void MultipleResults (F&& f, Finish&& finish, Start&& start)
433  {
434  Seq_->MultipleResults (std::forward<F> (f),
435  std::forward<Finish> (finish),
436  std::forward<Start> (start));
437  }
438 
439  operator QFuture<Ret> ()
440  {
441  constexpr bool isEmptyDestr = std::is_same<DestructionTag, EmptyDestructionTag>::value;
442  static_assert (std::is_same<DestructionTag, Ret>::value || isEmptyDestr,
443  "Destruction handler's return type doesn't match expected future type.");
444 
445  if (ThisFuture_)
446  return *ThisFuture_;
447 
448  QFutureInterface<Ret> iface;
449  iface.reportStarted ();
450 
451  SlotClosure<DeleteLaterPolicy> *deleteGuard = nullptr;
452  if constexpr (!isEmptyDestr)
453  {
454  deleteGuard = new SlotClosure<DeleteLaterPolicy>
455  {
456  [destrHandler = DestrHandler_, iface] () mutable
457  {
458  if (iface.isFinished ())
459  return;
460 
461  const auto res = destrHandler ();
462  iface.reportFinished (&res);
463  },
464  Seq_->parent (),
465  SIGNAL (destroyed ()),
466  Seq_
467  };
468  }
469 
470  Then ([deleteGuard, iface] (const Ret& ret) mutable
471  {
472  iface.reportFinished (&ret);
473 
474  delete deleteGuard;
475  });
476 
477  const auto& future = iface.future ();
478  ThisFuture_ = future;
479  return future;
480  }
481  };
482  }
483 
549  template<typename T>
550  detail::SequenceProxy<
551  detail::SequencerRetType_t<QFuture<T>>,
552  QFuture<T>,
553  detail::EmptyDestructionTag
554  >
555  Sequence (QObject *parent, const QFuture<T>& future)
556  {
557  return { new detail::Sequencer<QFuture<T>> { future, parent } };
558  }
559 
571  template<typename T>
572  QFuture<T> MakeReadyFuture (const T& t)
573  {
574  QFutureInterface<T> iface;
575  iface.reportStarted ();
576  iface.reportFinished (&t);
577  return iface.future ();
578  }
579 }
580 }
auto operator>>(const MV &value, const F &f) -> decltype(Bind(value, f))
Definition: monad.h:92
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition: oral.h:955
Util::ConcurrentException< Util::NewType< std::exception, 0, __LINE__ > > ConcurrentStdException
std::conditional_t< std::is_same_v< detail::RetTypeRaw_t< F >, detail::ReturnsVoid >, void, detail::RetTypeRaw_t< F > > RetType_t
Definition: typegetter.h:77
QException QtException_t
Definition: constants.h:35