// Copyright (C) 2025 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "result.h" #include #include // return_object_holder adapted from https://github.com/toby-allsopp/coroutine_monad/blob/master/return_object_holder.h template using deferred = std::optional; template struct return_object_holder { // The staging object that is returned (by copy/move) to the caller of the coroutine. deferred stage; return_object_holder *&p; // When constructed, we assign a pointer to ourselves to the supplied reference to // pointer. return_object_holder(return_object_holder *&p) : stage{} , p(p) { p = this; } // Copying doesn't make any sense (which copy should the pointer refer to?). return_object_holder(return_object_holder const &) = delete; // To move, we just update the pointer to point at the new object. return_object_holder(return_object_holder &&other) : stage(std::move(other.stage)) , p(other.p) { p = this; } // Assignment doesn't make sense. void operator=(return_object_holder const &) = delete; void operator=(return_object_holder &&) = delete; // Construct the staging value; arguments are perfect forwarded to T's constructor. template void emplace(Args &&...args) { // std::cout << this << ": emplace" << std::endl; stage.emplace(std::forward(args)...); } // We assume that we will be converted only once, so we can move from the staging // object. We also assume that `emplace` has been called at least once. operator T() { // std::cout << this << ": operator T" << std::endl; return std::move(*stage); } }; template auto make_return_object_holder(return_object_holder *&p) { return return_object_holder{p}; } using std::coroutine_traits; template using p_t = coroutine_traits>::promise_type; template struct expectation_failed { E value; }; template struct coroutine_traits, Args...> { struct promise_type { return_object_holder> *value; auto get_return_object() { return make_return_object_holder(value); } std::suspend_never initial_suspend() { return {}; } std::suspend_never final_suspend() noexcept { return {}; } template requires(!std::is_void_v) void return_value(const R_ &ret) { value->emplace(std::move(ret)); } void return_value(const Utils::ResultError &err) { value->emplace(std::move(err)); } void unhandled_exception() { std::rethrow_exception(std::current_exception()); } }; }; template struct result_awaiter { Utils::Result value; bool await_ready() { return value.has_value(); } void await_suspend(auto &&h) { h.promise().value->emplace(Utils::ResultError{value.error()}); } template requires(!std::is_void_v) R await_resume() { return value.value(); } template requires(std::is_void_v) void await_resume() {} }; template result_awaiter operator co_await(const Utils::Result &expected) { return result_awaiter{expected}; }