QXmpp  Version: 1.10.4
QXmppFutureUtils_p.h
1 // SPDX-FileCopyrightText: 2021 Linus Jahn <lnj@kaidan.im>
2 //
3 // SPDX-License-Identifier: LGPL-2.1-or-later
4 
5 #ifndef QXMPPFUTUREUTILS_P_H
6 #define QXMPPFUTUREUTILS_P_H
7 
8 //
9 // W A R N I N G
10 // -------------
11 //
12 // This file is not part of the QXmpp API. This header file may change from
13 // version to version without notice, or even be removed.
14 //
15 // We mean it.
16 //
17 
18 #include "QXmppIq.h"
19 #include "QXmppPromise.h"
20 #include "QXmppSendResult.h"
21 
22 #include <memory>
23 #include <variant>
24 
25 #include <QFutureWatcher>
26 #include <QObject>
27 
28 namespace QXmpp::Private {
29 
30 // helper for std::visit
31 template<class... Ts>
32 struct overloaded : Ts... {
33  using Ts::operator()...;
34 };
35 // explicit deduction guide (not needed as of C++20)
36 template<class... Ts>
37 overloaded(Ts...) -> overloaded<Ts...>;
38 
39 // Variation of std::visit allowing to forward unhandled types
40 template<typename ReturnType, typename T, typename Visitor>
41 auto visitForward(T variant, Visitor visitor)
42 {
43  return std::visit([&](auto &&value) -> ReturnType {
44  using ValueType = std::decay_t<decltype(value)>;
45  if constexpr (std::is_invocable_v<Visitor, ValueType>) {
46  return visitor(std::move(value));
47  } else {
48  return value;
49  }
50  },
51  std::forward<T>(variant));
52 }
53 
54 template<typename F, typename Ret, typename A, typename... Rest>
55 A lambda_helper(Ret (F::*)(A, Rest...));
56 
57 template<typename F, typename Ret, typename A, typename... Rest>
58 A lambda_helper(Ret (F::*)(A, Rest...) const);
59 
60 template<typename F>
61 struct first_argument {
62  using type = decltype(lambda_helper(&F::operator()));
63 };
64 
65 template<typename F>
66 using first_argument_t = typename first_argument<F>::type;
67 
68 #if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
69 using QtFuture::makeReadyFuture;
70 #else
71 template<typename T>
72 QFuture<T> makeReadyFuture(T &&value)
73 {
74  QFutureInterface<T> interface(QFutureInterfaceBase::Started);
75  interface.reportResult(std::move(value));
76  interface.reportFinished();
77  return interface.future();
78 }
79 
80 inline QFuture<void> makeReadyFuture()
81 {
82  using State = QFutureInterfaceBase::State;
83  return QFutureInterface<void>(State(State::Started | State::Finished)).future();
84 }
85 #endif
86 
87 template<typename T>
88 QXmppTask<T> makeReadyTask(T &&value)
89 {
90  QXmppPromise<T> promise;
91  promise.finish(std::move(value));
92  return promise.task();
93 }
94 
95 inline QXmppTask<void> makeReadyTask()
96 {
97  QXmppPromise<void> promise;
98  promise.finish();
99  return promise.task();
100 }
101 
102 template<typename T, typename Handler>
103 void await(const QFuture<T> &future, QObject *context, Handler handler)
104 {
105  auto *watcher = new QFutureWatcher<T>(context);
106  QObject::connect(watcher, &QFutureWatcherBase::finished,
107  context, [watcher, handler = std::move(handler)]() mutable {
108  handler(watcher->result());
109  watcher->deleteLater();
110  });
111  watcher->setFuture(future);
112 }
113 
114 template<typename Handler>
115 void await(const QFuture<void> &future, QObject *context, Handler handler)
116 {
117  auto *watcher = new QFutureWatcher<void>(context);
118  QObject::connect(watcher, &QFutureWatcherBase::finished,
119  context, [watcher, handler = std::move(handler)]() mutable {
120  handler();
121  watcher->deleteLater();
122  });
123  watcher->setFuture(future);
124 }
125 
126 template<typename Result, typename Input, typename Converter>
127 auto chain(QXmppTask<Input> &&source, QObject *context, Converter task) -> QXmppTask<Result>
128 {
129  QXmppPromise<Result> promise;
130 
131  source.then(context, [=](Input &&input) mutable {
132  promise.finish(task(std::move(input)));
133  });
134  return promise.task();
135 }
136 
137 template<typename IqType, typename Input, typename Converter>
138 auto parseIq(Input &&sendResult, Converter convert) -> decltype(convert({}))
139 {
140  using Result = decltype(convert({}));
141  return std::visit(overloaded {
142  [convert = std::move(convert)](const QDomElement &element) -> Result {
143  IqType iq;
144  iq.parse(element);
145  return convert(std::move(iq));
146  },
147  [](QXmppError &&error) -> Result {
148  return error;
149  },
150  },
151  std::move(sendResult));
152 }
153 
154 template<typename IqType, typename Result, typename Input>
155 auto parseIq(Input &&sendResult) -> Result
156 {
157  return parseIq<IqType>(std::move(sendResult), [](IqType &&iq) -> Result {
158  // no conversion
159  return iq;
160  });
161 }
162 
163 template<typename Input, typename Converter>
164 auto chainIq(QXmppTask<Input> &&input, QObject *context, Converter convert) -> QXmppTask<decltype(convert({}))>
165 {
166  using Result = decltype(convert({}));
167  using IqType = std::decay_t<first_argument_t<Converter>>;
168  return chain<Result>(std::move(input), context, [convert = std::move(convert)](Input &&input) -> Result {
169  return parseIq<IqType>(std::move(input), convert);
170  });
171 }
172 
173 template<typename Result, typename Input>
174 auto chainIq(QXmppTask<Input> &&input, QObject *context) -> QXmppTask<Result>
175 {
176  // IQ type is first std::variant parameter
177  using IqType = std::decay_t<decltype(std::get<0>(Result {}))>;
178  return chain<Result>(std::move(input), context, [](Input &&sendResult) mutable {
179  return parseIq<IqType, Result>(sendResult);
180  });
181 }
182 
183 template<typename T, typename Err, typename Function>
184 auto mapSuccess(std::variant<T, Err> var, Function lambda)
185 {
186  using MapResult = std::decay_t<decltype(lambda({}))>;
187  using MappedVariant = std::variant<MapResult, Err>;
188  return std::visit(overloaded {
189  [lambda = std::move(lambda)](T val) -> MappedVariant {
190  return lambda(std::move(val));
191  },
192  [](Err err) -> MappedVariant {
193  return err;
194  } },
195  std::move(var));
196 }
197 
198 template<typename T, typename Err>
199 auto mapToSuccess(std::variant<T, Err> var)
200 {
201  return mapSuccess(std::move(var), [](T) {
202  return Success();
203  });
204 }
205 
206 template<typename T, typename Err>
207 auto chainSuccess(QXmppTask<std::variant<T, Err>> &&source, QObject *context) -> QXmppTask<std::variant<QXmpp::Success, QXmppError>>
208 {
209  return chain<std::variant<QXmpp::Success, QXmppError>>(std::move(source), context, mapToSuccess<T, Err>);
210 }
211 
212 template<typename Input, typename Converter>
213 auto chainMapSuccess(QXmppTask<Input> &&source, QObject *context, Converter convert)
214 {
215  return chain<std::variant<decltype(convert({})), QXmppError>>(std::move(source), context, [convert](Input &&input) {
216  return mapSuccess(std::move(input), convert);
217  });
218 }
219 
220 } // namespace QXmpp::Private
221 
222 #endif // QXMPPFUTUREUTILS_P_H
Definition: QXmppError.h:17
QXmppTask< T > task()
Definition: QXmppPromise.h:96
Definition: QXmppTask.h:61
Create and update QXmppTask objects to communicate results of asynchronous operations.
Definition: QXmppPromise.h:22
Definition: Algorithms.h:12