22template<
typename T =
void>
33using awaiter_type_t =
typename awaiter_type<T>::type;
36class TaskFinalSuspend {
43 explicit TaskFinalSuspend(
const std::vector<std::coroutine_handle<>> &awaitingCoroutines)
44 : mAwaitingCoroutines(awaitingCoroutines) {}
53 bool await_ready() const noexcept {
67 template<
typename Promise>
68 void await_suspend(std::coroutine_handle<Promise> finishedCoroutine)
noexcept {
69 auto &promise = finishedCoroutine.promise();
71 for (
auto &awaiter : mAwaitingCoroutines) {
74 mAwaitingCoroutines.clear();
77 if (promise.setDestroyHandle()) {
78 finishedCoroutine.destroy();
87 constexpr void await_resume() const noexcept {}
90 std::vector<std::coroutine_handle<>> mAwaitingCoroutines;
132class TaskPromiseBase {
141 std::suspend_never initial_suspend() const noexcept {
149 auto final_suspend() const noexcept {
150 return TaskFinalSuspend{mAwaitingCoroutines};
168 template<
typename T,
typename Awaiter = QCoro::detail::awaiter_type_t<std::remove_cvref_t<T>>>
169 auto await_transform(T &&value) {
170 return Awaiter{value};
196 return std::forward<QCoro::Task<T>>(task);
206 template<Awaitable T>
207 auto &&await_transform(T &&awaitable) {
208 return std::forward<T>(awaitable);
212 template<Awaitable T>
213 auto &await_transform(T &awaitable) {
227 void addAwaitingCoroutine(std::coroutine_handle<> awaitingCoroutine) {
228 mAwaitingCoroutines.push_back(awaitingCoroutine);
231 bool hasAwaitingCoroutine()
const {
232 return !mAwaitingCoroutines.empty();
235 bool setDestroyHandle() noexcept {
236 return mDestroyHandle.exchange(
true, std::memory_order_acq_rel);
240 friend class TaskFinalSuspend;
243 std::vector<std::coroutine_handle<>> mAwaitingCoroutines;
246 std::atomic<bool> mDestroyHandle{
false};
254class TaskPromise final :
public TaskPromiseBase {
256 explicit TaskPromise() =
default;
257 ~TaskPromise() =
default;
260 Task<T> get_return_object() noexcept;
269 void unhandled_exception() {
270 mValue = std::current_exception();
278 void return_value(T &&value)
noexcept {
279 mValue.template emplace<T>(std::forward<T>(value));
283 void return_value(
const T &value)
noexcept {
288 requires QCoro::concepts::constructible_from<T, U>
289 void return_value(U &&value)
noexcept {
290 mValue = std::move(value);
294 requires QCoro::concepts::constructible_from<T, U>
295 void return_value(
const U &value)
noexcept {
305 if (std::holds_alternative<std::exception_ptr>(mValue)) {
306 assert(std::get<std::exception_ptr>(mValue) !=
nullptr);
307 std::rethrow_exception(std::get<std::exception_ptr>(mValue));
310 return std::get<T>(mValue);
315 if (std::holds_alternative<std::exception_ptr>(mValue)) {
316 assert(std::get<std::exception_ptr>(mValue) !=
nullptr);
317 std::rethrow_exception(std::get<std::exception_ptr>(mValue));
320 return std::move(std::get<T>(mValue));
325 std::variant<std::monostate, T, std::exception_ptr> mValue;
330class TaskPromise<void> final :
public TaskPromiseBase {
333 explicit TaskPromise() =
default;
336 ~TaskPromise() =
default;
339 Task<void> get_return_object() noexcept;
342 void unhandled_exception() {
343 mException = std::current_exception();
347 void return_void() noexcept {};
357 std::rethrow_exception(mException);
363 std::exception_ptr mException;
367template<
typename Promise>
368class TaskAwaiterBase {
371 bool await_ready() const noexcept {
372 return !mAwaitedCoroutine || mAwaitedCoroutine.done();
407 void await_suspend(std::coroutine_handle<> awaitingCoroutine)
noexcept {
408 mAwaitedCoroutine.promise().addAwaitingCoroutine(awaitingCoroutine);
416 TaskAwaiterBase(std::coroutine_handle<Promise> awaitedCoroutine)
417 : mAwaitedCoroutine(awaitedCoroutine) {}
420 std::coroutine_handle<Promise> mAwaitedCoroutine = {};
424struct isTask : std::false_type {
425 using return_type = T;
429struct isTask<
QCoro::Task<T>> : std::true_type {
434constexpr bool isTask_v = isTask<T>::value;
466 explicit Task() noexcept = default;
480 Task(
Task &&other) noexcept : mCoroutine(other.mCoroutine) {
481 other.mCoroutine =
nullptr;
486 if (std::addressof(other) !=
this) {
489 if (mCoroutine.promise().setDestroyHandle()) {
490 mCoroutine.destroy();
494 mCoroutine = other.mCoroutine;
495 other.mCoroutine =
nullptr;
506 if (mCoroutine.promise().setDestroyHandle()) {
507 mCoroutine.destroy();
517 return !mCoroutine || mCoroutine.done();
527 auto operator co_await()
const noexcept {
529 class TaskAwaiter :
public detail::TaskAwaiterBase<promise_type> {
531 TaskAwaiter(std::coroutine_handle<promise_type> awaitedCoroutine)
532 : detail::TaskAwaiterBase<promise_type>{awaitedCoroutine} {}
538 auto await_resume() {
539 assert(this->mAwaitedCoroutine !=
nullptr);
540 if constexpr (!std::is_void_v<T>) {
541 return std::move(this->mAwaitedCoroutine.promise().result());
544 this->mAwaitedCoroutine.promise().result();
549 return TaskAwaiter{mCoroutine};
568 template<
typename ThenCallback>
569 requires(std::is_invocable_v<ThenCallback> ||
570 (!std::is_void_v<T> && std::is_invocable_v<ThenCallback, T>))
571 auto then(ThenCallback &&callback) {
573 return thenImpl(std::forward<ThenCallback>(callback), [](
const auto &) {
throw; });
576 template<
typename ThenCallback,
typename ErrorCallback>
577 requires((std::is_invocable_v<ThenCallback> ||
578 (!std::is_void_v<T> && std::is_invocable_v<ThenCallback, T>)) &&
579 std::is_invocable_v<ErrorCallback, const std::exception &>)
580 auto then(ThenCallback &&callback, ErrorCallback &&errorCallback) {
581 return thenImpl(std::forward<ThenCallback>(callback),
582 std::forward<ErrorCallback>(errorCallback));
586 template<
typename ThenCallback,
typename... Args>
587 auto invokeCb(ThenCallback &&callback, [[maybe_unused]] Args &&...args) {
588 if constexpr (std::is_invocable_v<ThenCallback, Args...>) {
589 return callback(std::forward<Args>(args)...);
595 template<
typename ThenCallback,
typename Arg>
596 struct cb_invoke_result
597 : std::conditional_t<std::is_invocable_v<ThenCallback>, std::invoke_result<ThenCallback>,
598 std::invoke_result<ThenCallback, Arg>> {};
600 template<
typename ThenCallback>
601 struct cb_invoke_result<ThenCallback, void> : std::invoke_result<ThenCallback> {};
603 template<
typename ThenCallback,
typename Arg>
604 using cb_invoke_result_t =
typename cb_invoke_result<ThenCallback, Arg>::type;
606 template<
typename R,
typename ErrorCallback,
607 typename U =
typename detail::isTask<R>::return_type>
608 auto handleException(ErrorCallback &errCb,
const std::exception &exception) -> U {
610 if constexpr (!std::is_void_v<U>) {
615 template<
typename ThenCallback,
typename ErrorCallback,
616 typename R = cb_invoke_result_t<ThenCallback, T>>
617 auto thenImpl(ThenCallback &&thenCallback, ErrorCallback &&errorCallback)
618 -> std::conditional_t<detail::isTask_v<R>, R, Task<R>> {
619 auto thenCb = std::forward<ThenCallback>(thenCallback);
620 auto errCb = std::forward<ErrorCallback>(errorCallback);
621 if constexpr (std::is_void_v<value_type>) {
624 }
catch (
const std::exception &e) {
625 co_return handleException<R>(errCb, e);
627 if constexpr (detail::isTask_v<R>) {
628 co_return co_await invokeCb(thenCb);
630 co_return invokeCb(thenCb);
633 std::optional<T> value;
635 value =
co_await *
this;
636 }
catch (
const std::exception &e) {
637 co_return handleException<R>(errCb, e);
639 if constexpr (detail::isTask_v<R>) {
640 co_return co_await invokeCb(thenCb, std::move(*value));
642 co_return invokeCb(thenCb, std::move(*value));
653 std::coroutine_handle<promise_type> mCoroutine = {};
659inline Task<T> TaskPromise<T>::get_return_object() noexcept {
660 return Task<T>{std::coroutine_handle<TaskPromise>::from_promise(*
this)};
663Task<void>
inline TaskPromise<void>::get_return_object() noexcept {
664 return Task<void>{std::coroutine_handle<TaskPromise>::from_promise(*
this)};
669 { t.await_transform(v) };
673struct awaitable_return_type {
674 using type = std::decay_t<decltype(std::declval<T>().await_resume())>;
677template<QCoro::detail::has_member_operator_coawait T>
678struct awaitable_return_type<T> {
679 using type = std::decay_t<
680 typename awaitable_return_type<decltype(std::declval<T>().operator
co_await())>::type>;
683template<QCoro::detail::has_nonmember_operator_coawait T>
684struct awaitable_return_type<T> {
685 using type = std::decay_t<
686 typename awaitable_return_type<decltype(operator co_await(std::declval<T>()))>::type>;
689template<Awaitable Awaitable>
690using awaitable_return_type_t =
typename detail::awaitable_return_type<Awaitable>::type;
692template<
typename Awaitable>
693 requires TaskConvertible<Awaitable>
694using convertible_awaitable_return_type_t =
695 typename detail::awaitable_return_type<decltype(std::declval<TaskPromiseBase>().await_transform(
696 Awaitable()))>::type;
698template<
typename Awaitable>
699 requires TaskConvertible<Awaitable>
700auto toTask(Awaitable &&future)
702 co_return co_await future;
707 bool coroutineFinished =
false;
708 std::exception_ptr exception;
713template<
typename T,
typename QObjectSub
class,
typename Callback>
714 requires detail::TaskConvertible<T> &&
715 (std::is_invocable_v<Callback> ||
716 std::is_invocable_v<Callback, detail::convertible_awaitable_return_type_t<T>> ||
717 std::is_invocable_v<Callback, QObjectSubclass *> ||
718 std::is_invocable_v<Callback, QObjectSubclass *,
719 detail::convertible_awaitable_return_type_t<T>>) &&
720 (!detail::isTask_v<T>)
721void
connect(T &&future, QObjectSubclass *context, Callback func) {
722 auto task = detail::toTask(std::move(future));
723 connect(std::move(task), context, func);
An asynchronously executed task.
T value_type
The type of the coroutine return value.
auto then(ThenCallback &&callback, ErrorCallback &&errorCallback)
Task(const Task &)=delete
Task cannot be copy-constructed.
Task & operator=(Task &&other) noexcept
The task can be move-assigned.
Task() noexcept=default
Constructs a new empty task.
auto then(ThenCallback &&callback)
A callback to be invoked when the asynchronous task finishes.
detail::TaskPromise< T > promise_type
Promise type of the coroutine. This is required by the C++ standard.
bool isReady() const
Returns whether the task has finished.
Task(Task &&other) noexcept
The task can be move-constructed.
Task & operator=(const Task &)=delete
Task cannot be copy-assigned.
void connect(T &&future, QObjectSubclass *context, Callback func)