QXmpp  Version: 1.6.0
QXmppTask.h
1 // SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im>
2 // SPDX-FileCopyrightText: 2022 Jonah BrĂ¼chert <jbb@kaidan.im>
3 //
4 // SPDX-License-Identifier: LGPL-2.1-or-later
5 
6 #ifndef QXMPPTASK_H
7 #define QXMPPTASK_H
8 
9 #include "qxmpp_export.h"
10 
11 #include <functional>
12 #include <memory>
13 #include <optional>
14 
15 #include <QFuture>
16 #include <QPointer>
17 
18 template<typename T>
19 class QXmppPromise;
20 
21 namespace QXmpp::Private {
22 
23 struct TaskData;
24 
25 class QXMPP_EXPORT TaskPrivate
26 {
27 public:
28  TaskPrivate(void (*freeResult)(void *));
29  ~TaskPrivate();
30 
31  bool isFinished() const;
32  void setFinished(bool);
33  bool isContextAlive();
34  void setContext(QObject *);
35  void *result() const;
36  void setResult(void *);
37  void resetResult() { setResult(nullptr); }
38  const std::function<void(TaskPrivate &, void *)> continuation() const;
39  void setContinuation(std::function<void(TaskPrivate &, void *)> &&);
40  void invokeContinuation(void *result);
41 
42 private:
43  std::shared_ptr<TaskData> d;
44 };
45 
46 } // namespace QXmpp::Private
47 
60 template<typename T>
61 class QXmppTask
62 {
63 public:
64  ~QXmppTask() = default;
65 
98 #ifndef QXMPP_DOC
99  template<typename Continuation>
100 #endif
101  void then(QObject *context, Continuation continuation)
102  {
103  if constexpr (!std::is_void_v<T>) {
104  static_assert(std::is_invocable_v<Continuation, T &&>, "Function needs to be invocable with T &&.");
105  } else {
106  static_assert(std::is_invocable_v<Continuation>, "Function needs to be invocable without arguments.");
107  }
108  using namespace QXmpp::Private;
109 
110  if (d.isFinished()) {
111  if constexpr (std::is_void_v<T>) {
112  continuation();
113  } else {
114  // when calling then() after finished value could be empty
115  if (hasResult()) {
116  continuation(std::move(*reinterpret_cast<T *>(d.result())));
117  d.resetResult();
118  }
119  }
120  } else {
121  d.setContext(context);
122  d.setContinuation([f = std::forward<Continuation>(continuation)](TaskPrivate &d, void *result) mutable {
123  if (d.isContextAlive()) {
124  if constexpr (std::is_void_v<T>) {
125  f();
126  } else {
127  f(std::move(*reinterpret_cast<T *>(result)));
128  }
129  }
130 
131  // clear continuation to avoid "deadlocks" in case the user captured this QXmppTask
132  d.setContinuation({});
133  });
134  }
135  }
136 
143  [[nodiscard]] bool isFinished() const { return d.isFinished(); }
144 
148 #ifndef QXMPP_DOC
149  template<typename U = T, std::enable_if_t<(!std::is_void_v<U>)> * = nullptr>
150 #endif
151  [[nodiscard]] bool hasResult() const
152  {
153  return d.result() != nullptr;
154  }
155 
161 #ifdef QXMPP_DOC
162  [[nodiscard]] const T &result() const
163 #else
164  template<typename U = T, std::enable_if_t<(!std::is_void_v<U>)> * = nullptr>
165  [[nodiscard]] const U &result() const
166 #endif
167  {
168  Q_ASSERT(isFinished());
169  Q_ASSERT(hasResult());
170  return *reinterpret_cast<U *>(d.result());
171  }
172 
178 #ifdef QXMPP_DOC
179  [[nodiscard]] T takeResult()
180 #else
181  template<typename U = T, std::enable_if_t<(!std::is_void_v<U>)> * = nullptr>
182  [[nodiscard]] U takeResult()
183 #endif
184  {
185  Q_ASSERT(isFinished());
186  Q_ASSERT(hasResult());
187  U result = std::move(*reinterpret_cast<U *>(d.result()));
188  d.resetResult();
189  return result;
190  }
191 
195  QFuture<T> toFuture(QObject *context)
196  {
197  QFutureInterface<T> interface;
198 
199  if constexpr (std::is_same_v<T, void>) {
200  then(context, [interface]() mutable {
201  interface.reportFinished();
202  });
203  } else {
204  then(context, [interface](T &&val) mutable {
205  interface.reportResult(val);
206  interface.reportFinished();
207  });
208  }
209 
210  return interface.future();
211  }
212 
213 private:
214  friend class QXmppPromise<T>;
215 
216  explicit QXmppTask(QXmpp::Private::TaskPrivate data)
217  : d(std::move(data))
218  {
219  }
220 
221  QXmpp::Private::TaskPrivate d;
222 };
223 
224 #endif // QXMPPTASK_H
Create and update QXmppTask objects to communicate results of asynchronous operations.
Definition: QXmppPromise.h:23
Definition: QXmppTask.h:62
void then(QObject *context, Continuation continuation)
Definition: QXmppTask.h:101
bool hasResult() const
Definition: QXmppTask.h:151
QFuture< T > toFuture(QObject *context)
Definition: QXmppTask.h:195
bool isFinished() const
Definition: QXmppTask.h:143
T takeResult()
Definition: QXmppTask.h:179
const T & result() const
Definition: QXmppTask.h:162