LeechCraft 0.6.70-17609-g3dde4097dd
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
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>
19#include <util/sll/detector.h>
20#include "threadsconfig.h"
21#include "concurrentexception.h"
22
23namespace 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
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>>,
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}
const QDBusArgument & operator>>(const QDBusArgument &in, IconFrame &frame)
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition oral.h:945
constexpr bool IsDetected_v
Definition detector.h:35
std::tuple_element_t< 0, detail::CallTypeGetter_t< F > > RetType_t
Definition typegetter.h:43
QException QtException_t
Util::ConcurrentException< Util::NewType< std::exception, struct StdException > > ConcurrentStdException