// Copyright (c) 2023 Klemens D. Morgenstern // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_COBALT_RESULT_HPP #define BOOST_COBALT_RESULT_HPP #include #include #include namespace boost::cobalt { namespace detail { template concept result_error = requires (const T & t, const source_location & loc) { system::throw_exception_from_error(t, loc); } || // ADL requires (const T & t, const source_location & loc) { throw_exception_from_error(t, loc); } ; } inline constexpr auto interpret_as_result(std::tuple<> &&) { return system::result(); } template auto interpret_as_result(std::tuple && args) { if constexpr (detail::result_error) { if (std::get<0>(args)) return system::result(system::in_place_error, std::get<0>(args)); else return system::result(system::in_place_value); } else return system::result(std::move(std::get<0>(args))); } template requires (!detail::result_error && sizeof...(Args) > 0u) auto interpret_as_result(std::tuple && args) -> system::result> { return std::move(args); } template requires (sizeof...(Args) > 1u) auto interpret_as_result(std::tuple && args) -> system::result, Error> { if (std::get<0>(args)) return {system::in_place_error, std::move(std::get<0>(args))}; return { system::in_place_value, std::apply([](auto, auto && ... rest) {return std::make_tuple(std::move(rest)...);}) }; } template auto interpret_as_result(std::tuple && args) -> system::result { if (std::get<0>(args)) return {system::in_place_error, std::get<0>(args)}; return {system::in_place_value, std::get<1>(std::move(args))}; } struct as_result_tag {}; struct as_tuple_tag {}; template struct as_result_t { as_result_t(Aw && aw) : aw_(std::forward(aw)) {} bool await_ready() { return aw_.await_ready();} template auto await_suspend(std::coroutine_handle h) { return aw_.await_suspend(h);} auto await_resume() { if constexpr (requires {aw_.await_resume(as_result_tag{});}) return aw_.await_resume(as_result_tag{}); else { using type = decltype(aw_.await_resume()); if constexpr (std::is_void_v) { using res_t = system::result; BOOST_TRY { aw_.await_resume(); return res_t{system::in_place_value}; } BOOST_CATCH (...) { return res_t{system::in_place_error, std::current_exception()}; } BOOST_CATCH_END } else { using res_t = system::result; BOOST_TRY { return res_t{system::in_place_value, aw_.await_resume()}; } BOOST_CATCH (...) { return res_t{system::in_place_error, std::current_exception()}; } BOOST_CATCH_END } } } private: Aw aw_; }; template as_result_t(Aw &&) -> as_result_t; template auto as_result(Aw && aw) -> as_result_t { return as_result_t(std::forward(aw)); } template requires requires (Aw && aw) { {std::forward(aw).operator co_await()} -> awaitable_type; } auto as_result(Aw && aw) { struct lazy_tuple { Aw aw; auto operator co_await () { return as_result(std::forward(aw).operator co_await()); } }; return lazy_tuple{std::forward(aw)}; } template requires requires (Aw && aw) { {operator co_await(std::forward(aw))} -> awaitable_type; } auto as_result(Aw && aw) { struct lazy_tuple { Aw aw; auto operator co_await () { return as_result(operator co_await(std::forward(aw))); } }; return lazy_tuple{std::forward(aw)}; } template struct as_tuple_t { as_tuple_t(Aw && aw) : aw_(std::forward(aw)) {} bool await_ready() { return aw_.await_ready();} template auto await_suspend(std::coroutine_handle h) { return aw_.await_suspend(h);} auto await_resume() { using type = decltype(aw_.await_resume()); if constexpr (requires {aw_.await_resume(as_tuple_tag{});}) return aw_.await_resume(as_tuple_tag{}); else if (noexcept(aw_.await_resume())) { if constexpr (std::is_void_v) { aw_.await_resume(); return std::make_tuple(); } else return std::make_tuple(aw_.await_resume()); } else { if constexpr (std::is_void_v) { BOOST_TRY { aw_.await_resume(); return std::make_tuple(std::exception_ptr()); } BOOST_CATCH (...) { return make_tuple_(std::current_exception()); } BOOST_CATCH_END } else { BOOST_TRY { return make_tuple_(std::exception_ptr(), aw_.await_resume()); } BOOST_CATCH (...) { return make_tuple_(std::current_exception(), type()); } BOOST_CATCH_END } } } private: template std::tuple make_tuple_(std::exception_ptr ep, std::tuple && tup) { return std::apply( [&](auto ... args) { return std::make_tuple(std::move(ep), std::move(args)...); }, std::move(tup)); } template std::tuple make_tuple_(std::exception_ptr ep, Arg && arg) { return std::make_tuple(std::move(ep), std::move(arg)); } private: Aw aw_; }; template as_tuple_t(Aw &&) -> as_tuple_t; template auto as_tuple(Aw && aw) -> as_tuple_t { return as_tuple_t(std::forward(aw)); } template requires requires (Aw && aw) { {std::forward(aw).operator co_await()} -> awaitable_type; } auto as_tuple(Aw && aw) { struct lazy_tuple { Aw aw; auto operator co_await () { return as_tuple(std::forward(aw).operator co_await()); } }; return lazy_tuple{std::forward(aw)}; } template requires requires (Aw && aw) { {operator co_await(std::forward(aw))} -> awaitable_type; } auto as_tuple(Aw && aw) { struct lazy_tuple { Aw aw; auto operator co_await () { return as_tuple(operator co_await(std::forward(aw))); } }; return lazy_tuple{std::forward(aw)}; } } #endif //BOOST_COBALT_RESULT_HPP