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