LeechCraft 0.6.70-14794-g33744ae6ce
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>
18#include <util/sll/detector.h>
19#include "threadsconfig.h"
20#include "concurrentexception.h"
21
22namespace 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
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>>,
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 {
552 iface.reportStarted ();
553 iface.reportFinished (&t);
554 return iface.future ();
555 }
556}
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition: oral.h:934
Util::ConcurrentException< Util::NewType< std::exception, struct StdException > > ConcurrentStdException
auto operator>>(const MV &value, const F &f) -> decltype(Bind(value, f))
Definition: monad.h:71
std::tuple_element_t< 0, decltype(detail::TypeGetter(*static_cast< F * >(nullptr)))> RetType_t
Definition: typegetter.h:37
QException QtException_t