5 #include <QtCore/QFuture>
9 template <
typename FnT,
typename JobT>
10 concept BoundResultHandler = std::invocable<FnT, JobT*> || std::invocable<FnT>
11 || requires(FnT f, JobT j) { f(collectResponse(&j)); };
13 template <
typename FnT,
typename JobT>
14 concept ResultHandler = BoundResultHandler<FnT, JobT> || std::is_member_function_pointer_v<FnT>;
73 class QUOTIENT_API JobHandle :
public QPointer<JobT>,
public QFuture<JobT*> {
75 using pointer_type = QPointer<JobT>;
76 using future_value_type = JobT*;
77 using future_type = QFuture<future_value_type>;
81 struct Skip :
public decltype([](future_value_type) {}) {};
83 JobHandle(JobT* job, future_type&& futureToWrap)
84 : pointer_type(job), future_type(std::move(futureToWrap))
87 static future_type setupFuture(JobT* job)
89 return job ? job->future().then([job] {
return future_value_type{ job }; }) : future_type{};
93 Q_IMPLICIT JobHandle(JobT* job =
nullptr) : JobHandle(job, setupFuture(job)) {}
121 template <
typename ConfigT, ResultHandler<JobT> FnT>
122 auto onResult(ConfigT config, FnT&& fn)
124 return rewrap(future_type::then(config, continuation(std::forward<FnT>(fn), config)));
128 template <BoundResultHandler<JobT> FnT>
129 auto onResult(FnT&& fn)
131 return rewrap(future_type::then(continuation(std::forward<FnT>(fn))));
141 template <
typename ConfigT, ResultHandler<JobT> SuccessFnT, ResultHandler<JobT> FailureFnT = Skip>
142 auto then(ConfigT config, SuccessFnT&& onSuccess, FailureFnT&& onFailure = {})
143 requires requires(future_type f) { f.then(config, Skip{}); }
145 return rewrap(future_type::then(
146 config, combineContinuations(std::forward<SuccessFnT>(onSuccess),
147 std::forward<FailureFnT>(onFailure), config)));
151 template <BoundResultHandler<JobT> SuccessFnT, BoundResultHandler<JobT> FailureFnT = Skip>
152 auto then(SuccessFnT&& onSuccess, FailureFnT&& onFailure = {})
154 return rewrap(future_type::then(combineContinuations(std::forward<SuccessFnT>(onSuccess),
155 std::forward<FailureFnT>(onFailure))));
159 template <
typename FnT>
160 auto onFailure(
auto config, FnT&& fn)
162 return then(config, Skip{}, std::forward<FnT>(fn));
166 template <
typename FnT>
167 auto onFailure(FnT&& fn)
169 return then(Skip{}, std::forward<FnT>(fn));
174 template <
typename FnT>
175 auto onCanceled(QObject* context, FnT&& fn)
178 future_type::onCanceled(context, bindToContext(std::forward<FnT>(fn), context)));
183 template <
typename FnT>
184 auto onCanceled(FnT&& fn)
186 return rewrap(future_type::onCanceled(BoundFn{ std::forward<FnT>(fn) }));
190 auto responseFuture()
192 return future_type::then([](
auto* j) {
return collectResponse(j); });
203 if (
auto pJob = pointer_type::get(); isJobPending(pJob)) {
204 Q_ASSERT(QThread::currentThread() == pJob->thread());
211 template <
typename FnT>
213 auto operator()() {
return callFn<
false>(
nullptr); }
214 auto operator()(future_value_type job) {
return callFn(job); }
216 template <
bool AllowJobArg =
true>
217 auto callFn(future_value_type job)
219 if constexpr (std::invocable<FnT>) {
220 return std::forward<FnT>(fn)();
222 static_assert(AllowJobArg,
"onCanceled continuations should not accept arguments");
223 if constexpr (requires { fn(job); })
225 else if constexpr (requires { collectResponse(job); }) {
227 requires { fn(collectResponse(job)); },
228 "The continuation function must accept either of: 1) no arguments; "
229 "2) the job pointer itself; 3) the value returned by collectResponse(job)");
230 return fn(collectResponse(job));
239 [[no_unique_address]]
244 template <
typename FnT>
245 BoundFn(FnT&&) -> BoundFn<FnT>;
247 template <
typename FnT,
typename ConfigT = Skip>
248 static auto bindToContext(FnT&& fn, ConfigT config = {})
253 if constexpr (std::derived_from<std::remove_pointer_t<ConfigT>, QObject>
254 && std::is_member_function_pointer_v<FnT>) {
255 return BoundFn{ std::bind_front(std::forward<FnT>(fn), config) };
257 return BoundFn{ std::forward<FnT>(fn) };
260 template <ResultHandler<JobT> FnT,
typename ConfigT = Skip>
261 static auto continuation(FnT&& fn, ConfigT config = {})
263 return [f = bindToContext(std::forward<FnT>(fn), config)](future_value_type arg)
mutable {
264 if constexpr (std::is_void_v<
decltype(f(arg))>) {
272 template <ResultHandler<JobT> SuccessFnT, ResultHandler<JobT> FailureFnT,
typename ConfigT = Skip>
273 static auto combineContinuations(SuccessFnT&& onSuccess, FailureFnT&& onFailure,
276 return [sFn = bindToContext(std::forward<SuccessFnT>(onSuccess), config),
277 fFn = bindToContext(std::forward<FailureFnT>(onFailure), config)](
278 future_value_type job)
mutable {
279 using sType =
decltype(sFn(job));
280 using fType =
decltype(fFn(job));
281 if constexpr (std::is_void_v<sType> && std::is_void_v<fType>)
282 return (job->status().good() ? sFn(job) : fFn(job), job);
283 else if constexpr (std::is_same_v<FailureFnT, Skip>) {
285 return job->status().good() ? sFn(job) : (fFn(job), sType{});
287 return job->status().good() ? sFn(job) : fFn(job);
291 auto rewrap(future_type&& ft)
const
293 return JobHandle(pointer_type::get(), std::move(ft));
296 template <
typename NewJobT>
297 auto rewrap(QFuture<JobHandle<NewJobT>> ft)
298 -> QFuture<
typename JobHandle<NewJobT>::future_value_type>
314 QFutureInterface<
typename JobHandle<NewJobT>::future_value_type> newPromise(
315 QFutureInterfaceBase::State::Pending);
316 ft.then([newPromise](JobHandle<NewJobT> nestedHandle)
mutable {
317 Q_ASSERT(nestedHandle.isStarted());
318 newPromise.reportStarted();
319 nestedHandle.then([newPromise]()
mutable { newPromise.reportFinished(); })
320 .onCanceled([newPromise]()
mutable { newPromise.cancelAndFinish(); });
321 }).onCanceled([newPromise]()
mutable {
322 newPromise.reportStarted();
323 newPromise.cancelAndFinish();
325 return newPromise.future();
328 static auto rewrap(
auto someOtherFuture) {
return someOtherFuture; }
331 template <std::derived_from<BaseJob> JobT>
332 JobHandle(JobT*) -> JobHandle<JobT>;
336 Q_DECLARE_SMART_POINTER_METATYPE(Quotient::JobHandle)