LeechCraft  0.6.70-15082-g543737046d
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  * Distributed under the Boost Software License, Version 1.0.
6  * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7  **********************************************************************/
8 
9 #pragma once
10 
11 #include <type_traits>
12 #include <functional>
13 #include <memory>
14 #include <optional>
15 #include <QFutureInterface>
16 #include <QFutureWatcher>
17 #include <util/sll/slotclosure.h>
18 #include <util/sll/detector.h>
19 #include "threadsconfig.h"
20 #include "concurrentexception.h"
21 
22 namespace LC::Util
23 {
24  template<typename R, typename F, typename... Args>
25  void ReportFutureResult (QFutureInterface<R>& iface, F&& f, Args&&... args)
26  {
27  try
28  {
29  constexpr bool isVoid = std::is_same_v<R, void>;
30  if constexpr (!isVoid && !std::is_invocable_v<std::decay_t<F>, Args...>)
31  {
32  static_assert (std::is_constructible_v<R, F>);
33  static_assert (sizeof... (Args) == 0,
34  "Extra args when a value is passed. Perhaps you wanted to pass in a function?");
35 
36  const R result { std::forward<F> (f) };
37  iface.reportResult (result);
38  }
39  else if constexpr (!isVoid)
40  {
41  const auto result = std::invoke (std::forward<F> (f), std::forward<Args> (args)...);
42  iface.reportResult (result);
43  }
44  else
45  std::invoke (std::forward<F> (f), std::forward<Args> (args)...);
46  }
47  catch (const QtException_t& e)
48  {
49  iface.reportException (e);
50  }
51  catch (const std::exception& e)
52  {
53  iface.reportException (ConcurrentStdException { e });
54  }
55 
56  iface.reportFinished ();
57  }
58 
59  namespace detail
60  {
61  template<typename T>
62  struct UnwrapFutureTypeBase {};
63 
64  template<typename T>
65  struct UnwrapFutureTypeBase<QFuture<T>>
66  {
67  using type = T;
68  };
69 
70  template<typename T>
71  struct UnwrapFutureType : UnwrapFutureTypeBase<std::decay_t<T>>
72  {
73  };
74  }
75 
76  template<typename T>
77  using UnwrapFutureType_t = typename detail::UnwrapFutureType<T>::type;
78 
79  namespace detail
80  {
90  template<typename Future>
91  class Sequencer final : public QObject
92  {
93  public:
97  using RetType_t = UnwrapFutureType_t<Future>;
98  private:
99  Future Future_;
100  QFutureWatcher<RetType_t> BaseWatcher_;
101  QFutureWatcherBase *LastWatcher_ = &BaseWatcher_;
102  public:
108  Sequencer (const Future& future, QObject *parent)
109  : QObject { parent }
110  , Future_ { future }
111  , BaseWatcher_ { this }
112  {
113  }
114 
120  void Start ()
121  {
122  connect (LastWatcher_,
123  &QFutureWatcherBase::finished,
124  this,
125  &QObject::deleteLater);
126  BaseWatcher_.setFuture (Future_);
127  }
128 
149  template<typename RetT, typename ArgT>
150  void Then (const std::function<QFuture<RetT> (ArgT)>& action)
151  {
152  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
153  if (!last)
154  {
155  deleteLater ();
156  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
157  }
158 
159  const auto watcher = new QFutureWatcher<RetT> { this };
160  LastWatcher_ = watcher;
161 
162  new SlotClosure<DeleteLaterPolicy>
163  {
164  [this, last, watcher, action]
165  {
166  if (static_cast<QObject*> (last) != &BaseWatcher_)
167  last->deleteLater ();
168  watcher->setFuture (action (last->result ()));
169  },
170  last,
171  SIGNAL (finished ()),
172  last
173  };
174  }
175 
196  template<typename ArgT>
197  void Then (const std::function<void (ArgT)>& action)
198  {
199  const auto last = dynamic_cast<QFutureWatcher<ArgT>*> (LastWatcher_);
200  if (!last)
201  {
202  deleteLater ();
203  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
204  }
205 
206  new SlotClosure<DeleteLaterPolicy>
207  {
208  [last, action]
209  {
210  action (last->result ());
211  },
212  LastWatcher_,
213  SIGNAL (finished ()),
214  LastWatcher_
215  };
216  }
217 
218  void Then (const std::function<void ()>& action)
219  {
220  const auto last = dynamic_cast<QFutureWatcher<void>*> (LastWatcher_);
221  if (!last)
222  {
223  deleteLater ();
224  throw std::runtime_error { std::string { "invalid type in " } + Q_FUNC_INFO };
225  }
226 
227  new SlotClosure<DeleteLaterPolicy>
228  {
229  action,
230  LastWatcher_,
231  SIGNAL (finished ()),
232  LastWatcher_
233  };
234  }
235 
236  template<typename Handler>
237  void MultipleResults (const Handler& handler,
238  const std::function<void ()>& finishHandler = {},
239  const std::function<void ()>& startHandler = {})
240  {
241  if (LastWatcher_ != &BaseWatcher_)
242  {
243  qWarning () << Q_FUNC_INFO
244  << "multiple results handler should be chained directly to the source";
245  throw std::runtime_error { "invalid multiple results handler chaining" };
246  }
247 
248  connect (&BaseWatcher_,
249  &QFutureWatcherBase::resultReadyAt,
250  &BaseWatcher_,
251  [handler, this] (int index) { handler (BaseWatcher_.resultAt (index)); });
252 
253  if (finishHandler)
254  new Util::SlotClosure<Util::DeleteLaterPolicy>
255  {
256  finishHandler,
257  &BaseWatcher_,
258  SIGNAL (finished ()),
259  &BaseWatcher_
260  };
261 
262  if (startHandler)
263  new Util::SlotClosure<Util::DeleteLaterPolicy>
264  {
265  startHandler,
266  &BaseWatcher_,
267  SIGNAL (started ()),
268  &BaseWatcher_
269  };
270 
271  connect (&BaseWatcher_,
272  SIGNAL (finished ()),
273  this,
274  SLOT (deleteLater ()));
275  }
276  };
277 
278  template<typename T>
279  using SequencerRetType_t = typename Sequencer<T>::RetType_t;
280 
281  struct EmptyDestructionTag;
282 
299  template<typename Ret, typename Future, typename DestructionTag>
300  class SequenceProxy
301  {
302  template<typename, typename, typename>
303  friend class SequenceProxy;
304 
305  std::shared_ptr<void> ExecuteGuard_;
306  Sequencer<Future> * const Seq_;
307 
308  std::optional<QFuture<Ret>> ThisFuture_;
309 
310  std::function<DestructionTag ()> DestrHandler_;
311 
312  SequenceProxy (const std::shared_ptr<void>& guard, Sequencer<Future> *seq,
313  const std::function<DestructionTag ()>& destrHandler)
314  : ExecuteGuard_ { guard }
315  , Seq_ { seq }
316  , DestrHandler_ { destrHandler }
317  {
318  }
319 
320  template<typename F1, typename Ret1>
321  using ReturnsFutureDetector_t = UnwrapFutureType_t<std::result_of_t<F1 (Ret1)>>;
322 
323  template<typename F, typename... Args>
324  using ReturnsVoidDetector_t = std::result_of_t<F (Args...)>;
325  public:
326  using Ret_t = Ret;
327 
333  SequenceProxy (Sequencer<Future> *sequencer)
334  : ExecuteGuard_ { nullptr, [sequencer] (void*) { sequencer->Start (); } }
335  , Seq_ { sequencer }
336  {
337  }
338 
344  SequenceProxy (const SequenceProxy& proxy) = delete;
345 
351  SequenceProxy (SequenceProxy&& proxy) = default;
352 
359  template<typename F>
360  auto Then (F&& f)
361  {
362  if (ThisFuture_)
363  throw std::runtime_error { "SequenceProxy::Then(): cannot chain more after being converted to a QFuture" };
364 
365  if constexpr (IsDetected_v<ReturnsFutureDetector_t, F, Ret>)
366  {
367  using Next_t = UnwrapFutureType_t<decltype (f (std::declval<Ret> ()))>;
368  Seq_->template Then<Next_t, Ret> (f);
369  return SequenceProxy<Next_t, Future, DestructionTag> { ExecuteGuard_, Seq_, DestrHandler_ };
370  }
371  else if constexpr (std::is_same<IsDetected_t<struct Dummy, ReturnsVoidDetector_t, F, Ret>, void> {})
372  Seq_->template Then<Ret> (f);
373  else if constexpr (std::is_same<void, Ret>::value &&
374  std::is_same<IsDetected_t<struct Dummy, ReturnsVoidDetector_t, F>, void> {})
375  Seq_->Then (std::function<void ()> { f });
376  else
377  static_assert (std::is_same<F, struct Dummy> {}, "Invalid functor passed to SequenceProxy::Then()");
378  }
379 
380  template<typename F>
381  auto operator>> (F&& f) -> decltype (this->Then (std::forward<F> (f)))
382  {
383  return Then (std::forward<F> (f));
384  }
385 
386  template<typename F>
387  SequenceProxy<Ret, Future, std::result_of_t<F ()>> DestructionValue (F&& f)
388  {
389  static_assert (std::is_same<DestructionTag, EmptyDestructionTag>::value,
390  "Destruction handling function has been already set.");
391 
392  return { ExecuteGuard_, Seq_, std::forward<F> (f) };
393  }
394 
395  template<typename F>
396  void MultipleResults (F&& f)
397  {
398  Seq_->MultipleResults (std::forward<F> (f));
399  }
400 
401  template<typename F, typename Finish>
402  void MultipleResults (F&& f, Finish&& finish)
403  {
404  Seq_->MultipleResults (std::forward<F> (f),
405  std::forward<Finish> (finish));
406  }
407 
408  template<typename F, typename Finish, typename Start>
409  void MultipleResults (F&& f, Finish&& finish, Start&& start)
410  {
411  Seq_->MultipleResults (std::forward<F> (f),
412  std::forward<Finish> (finish),
413  std::forward<Start> (start));
414  }
415 
416  operator QFuture<Ret> ()
417  {
418  constexpr bool isEmptyDestr = std::is_same<DestructionTag, EmptyDestructionTag>::value;
419  static_assert (std::is_same<DestructionTag, Ret>::value || isEmptyDestr,
420  "Destruction handler's return type doesn't match expected future type.");
421 
422  if (ThisFuture_)
423  return *ThisFuture_;
424 
425  QFutureInterface<Ret> iface;
426  iface.reportStarted ();
427 
428  SlotClosure<DeleteLaterPolicy> *deleteGuard = nullptr;
429  if constexpr (!isEmptyDestr)
430  {
431  deleteGuard = new SlotClosure<DeleteLaterPolicy>
432  {
433  [destrHandler = DestrHandler_, iface] () mutable
434  {
435  if (iface.isFinished ())
436  return;
437 
438  const auto res = destrHandler ();
439  iface.reportFinished (&res);
440  },
441  Seq_->parent (),
442  SIGNAL (destroyed ()),
443  Seq_
444  };
445  }
446 
447  Then ([deleteGuard, iface] (const Ret& ret) mutable
448  {
449  iface.reportFinished (&ret);
450 
451  delete deleteGuard;
452  });
453 
454  const auto& future = iface.future ();
455  ThisFuture_ = future;
456  return future;
457  }
458  };
459  }
460 
526  template<typename T>
527  detail::SequenceProxy<
528  detail::SequencerRetType_t<QFuture<T>>,
529  QFuture<T>,
530  detail::EmptyDestructionTag
531  >
532  Sequence (QObject *parent, const QFuture<T>& future)
533  {
534  return { new detail::Sequencer<QFuture<T>> { future, parent } };
535  }
536 
548  template<typename T>
549  QFuture<T> MakeReadyFuture (const T& t)
550  {
551  QFutureInterface<T> iface;
552  iface.reportStarted ();
553  iface.reportFinished (&t);
554  return iface.future ();
555  }
556 }
LC::Util
Definition: icoreproxy.h:33
slotclosure.h
LC::Util::operator>>
auto operator>>(const MV &value, const F &f) -> decltype(Bind(value, f))
Definition: monad.h:83
LC::Util::ConcurrentStdException
Util::ConcurrentException< Util::NewType< std::exception, struct StdException > > ConcurrentStdException
Definition: concurrentexception.h:90
concurrentexception.h
detector.h
QFuture
Definition: idownload.h:17
LC::Util::oral::sph::f
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition: oral.h:952
QFutureInterface
Definition: consistencychecker.h:20
LC::Util::QtException_t
QException QtException_t
Definition: concurrentexception.h:23
threadsconfig.h
LC::Util::RetType_t
std::tuple_element_t< 0, decltype(detail::TypeGetter(*static_cast< F * >(nullptr)))> RetType_t
Definition: typegetter.h:43