123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 |
- //
- // experimental/awaitable_operators.hpp
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- //
- // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
- //
- // 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_ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP
- #define BOOST_ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP
- #if defined(_MSC_VER) && (_MSC_VER >= 1200)
- # pragma once
- #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
- #include <boost/asio/detail/config.hpp>
- #include <optional>
- #include <stdexcept>
- #include <tuple>
- #include <variant>
- #include <boost/asio/awaitable.hpp>
- #include <boost/asio/co_spawn.hpp>
- #include <boost/asio/detail/type_traits.hpp>
- #include <boost/asio/experimental/deferred.hpp>
- #include <boost/asio/experimental/parallel_group.hpp>
- #include <boost/asio/multiple_exceptions.hpp>
- #include <boost/asio/this_coro.hpp>
- #include <boost/asio/detail/push_options.hpp>
- namespace boost {
- namespace asio {
- namespace experimental {
- namespace awaitable_operators {
- namespace detail {
- template <typename T, typename Executor>
- awaitable<T, Executor> awaitable_wrap(awaitable<T, Executor> a,
- constraint_t<is_constructible<T>::value>* = 0)
- {
- return a;
- }
- template <typename T, typename Executor>
- awaitable<std::optional<T>, Executor> awaitable_wrap(awaitable<T, Executor> a,
- constraint_t<!is_constructible<T>::value>* = 0)
- {
- co_return std::optional<T>(co_await std::move(a));
- }
- template <typename T>
- T& awaitable_unwrap(conditional_t<true, T, void>& r,
- constraint_t<is_constructible<T>::value>* = 0)
- {
- return r;
- }
- template <typename T>
- T& awaitable_unwrap(std::optional<conditional_t<true, T, void>>& r,
- constraint_t<!is_constructible<T>::value>* = 0)
- {
- return *r;
- }
- } // namespace detail
- /// Wait for both operations to succeed.
- /**
- * If one operations fails, the other is cancelled as the AND-condition can no
- * longer be satisfied.
- */
- template <typename Executor>
- awaitable<void, Executor> operator&&(
- awaitable<void, Executor> t, awaitable<void, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, ex1] =
- co_await make_parallel_group(
- co_spawn(ex, std::move(t), deferred),
- co_spawn(ex, std::move(u), deferred)
- ).async_wait(
- wait_for_one_error(),
- deferred
- );
- if (ex0 && ex1)
- throw multiple_exceptions(ex0);
- if (ex0)
- std::rethrow_exception(ex0);
- if (ex1)
- std::rethrow_exception(ex1);
- co_return;
- }
- /// Wait for both operations to succeed.
- /**
- * If one operations fails, the other is cancelled as the AND-condition can no
- * longer be satisfied.
- */
- template <typename U, typename Executor>
- awaitable<U, Executor> operator&&(
- awaitable<void, Executor> t, awaitable<U, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, ex1, r1] =
- co_await make_parallel_group(
- co_spawn(ex, std::move(t), deferred),
- co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
- ).async_wait(
- wait_for_one_error(),
- deferred
- );
- if (ex0 && ex1)
- throw multiple_exceptions(ex0);
- if (ex0)
- std::rethrow_exception(ex0);
- if (ex1)
- std::rethrow_exception(ex1);
- co_return std::move(detail::awaitable_unwrap<U>(r1));
- }
- /// Wait for both operations to succeed.
- /**
- * If one operations fails, the other is cancelled as the AND-condition can no
- * longer be satisfied.
- */
- template <typename T, typename Executor>
- awaitable<T, Executor> operator&&(
- awaitable<T, Executor> t, awaitable<void, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, r0, ex1] =
- co_await make_parallel_group(
- co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
- co_spawn(ex, std::move(u), deferred)
- ).async_wait(
- wait_for_one_error(),
- deferred
- );
- if (ex0 && ex1)
- throw multiple_exceptions(ex0);
- if (ex0)
- std::rethrow_exception(ex0);
- if (ex1)
- std::rethrow_exception(ex1);
- co_return std::move(detail::awaitable_unwrap<T>(r0));
- }
- /// Wait for both operations to succeed.
- /**
- * If one operations fails, the other is cancelled as the AND-condition can no
- * longer be satisfied.
- */
- template <typename T, typename U, typename Executor>
- awaitable<std::tuple<T, U>, Executor> operator&&(
- awaitable<T, Executor> t, awaitable<U, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, r0, ex1, r1] =
- co_await make_parallel_group(
- co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
- co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
- ).async_wait(
- wait_for_one_error(),
- deferred
- );
- if (ex0 && ex1)
- throw multiple_exceptions(ex0);
- if (ex0)
- std::rethrow_exception(ex0);
- if (ex1)
- std::rethrow_exception(ex1);
- co_return std::make_tuple(
- std::move(detail::awaitable_unwrap<T>(r0)),
- std::move(detail::awaitable_unwrap<U>(r1)));
- }
- /// Wait for both operations to succeed.
- /**
- * If one operations fails, the other is cancelled as the AND-condition can no
- * longer be satisfied.
- */
- template <typename... T, typename Executor>
- awaitable<std::tuple<T..., std::monostate>, Executor> operator&&(
- awaitable<std::tuple<T...>, Executor> t, awaitable<void, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, r0, ex1, r1] =
- co_await make_parallel_group(
- co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
- co_spawn(ex, std::move(u), deferred)
- ).async_wait(
- wait_for_one_error(),
- deferred
- );
- if (ex0 && ex1)
- throw multiple_exceptions(ex0);
- if (ex0)
- std::rethrow_exception(ex0);
- if (ex1)
- std::rethrow_exception(ex1);
- co_return std::move(detail::awaitable_unwrap<std::tuple<T...>>(r0));
- }
- /// Wait for both operations to succeed.
- /**
- * If one operations fails, the other is cancelled as the AND-condition can no
- * longer be satisfied.
- */
- template <typename... T, typename U, typename Executor>
- awaitable<std::tuple<T..., U>, Executor> operator&&(
- awaitable<std::tuple<T...>, Executor> t, awaitable<U, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, r0, ex1, r1] =
- co_await make_parallel_group(
- co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
- co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
- ).async_wait(
- wait_for_one_error(),
- deferred
- );
- if (ex0 && ex1)
- throw multiple_exceptions(ex0);
- if (ex0)
- std::rethrow_exception(ex0);
- if (ex1)
- std::rethrow_exception(ex1);
- co_return std::tuple_cat(
- std::move(detail::awaitable_unwrap<std::tuple<T...>>(r0)),
- std::make_tuple(std::move(detail::awaitable_unwrap<U>(r1))));
- }
- /// Wait for one operation to succeed.
- /**
- * If one operations succeeds, the other is cancelled as the OR-condition is
- * already satisfied.
- */
- template <typename Executor>
- awaitable<std::variant<std::monostate, std::monostate>, Executor> operator||(
- awaitable<void, Executor> t, awaitable<void, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, ex1] =
- co_await make_parallel_group(
- co_spawn(ex, std::move(t), deferred),
- co_spawn(ex, std::move(u), deferred)
- ).async_wait(
- wait_for_one_success(),
- deferred
- );
- if (order[0] == 0)
- {
- if (!ex0)
- co_return std::variant<std::monostate, std::monostate>{
- std::in_place_index<0>};
- if (!ex1)
- co_return std::variant<std::monostate, std::monostate>{
- std::in_place_index<1>};
- throw multiple_exceptions(ex0);
- }
- else
- {
- if (!ex1)
- co_return std::variant<std::monostate, std::monostate>{
- std::in_place_index<1>};
- if (!ex0)
- co_return std::variant<std::monostate, std::monostate>{
- std::in_place_index<0>};
- throw multiple_exceptions(ex1);
- }
- }
- /// Wait for one operation to succeed.
- /**
- * If one operations succeeds, the other is cancelled as the OR-condition is
- * already satisfied.
- */
- template <typename U, typename Executor>
- awaitable<std::variant<std::monostate, U>, Executor> operator||(
- awaitable<void, Executor> t, awaitable<U, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, ex1, r1] =
- co_await make_parallel_group(
- co_spawn(ex, std::move(t), deferred),
- co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
- ).async_wait(
- wait_for_one_success(),
- deferred
- );
- if (order[0] == 0)
- {
- if (!ex0)
- co_return std::variant<std::monostate, U>{
- std::in_place_index<0>};
- if (!ex1)
- co_return std::variant<std::monostate, U>{
- std::in_place_index<1>,
- std::move(detail::awaitable_unwrap<U>(r1))};
- throw multiple_exceptions(ex0);
- }
- else
- {
- if (!ex1)
- co_return std::variant<std::monostate, U>{
- std::in_place_index<1>,
- std::move(detail::awaitable_unwrap<U>(r1))};
- if (!ex0)
- co_return std::variant<std::monostate, U>{
- std::in_place_index<0>};
- throw multiple_exceptions(ex1);
- }
- }
- /// Wait for one operation to succeed.
- /**
- * If one operations succeeds, the other is cancelled as the OR-condition is
- * already satisfied.
- */
- template <typename T, typename Executor>
- awaitable<std::variant<T, std::monostate>, Executor> operator||(
- awaitable<T, Executor> t, awaitable<void, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, r0, ex1] =
- co_await make_parallel_group(
- co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
- co_spawn(ex, std::move(u), deferred)
- ).async_wait(
- wait_for_one_success(),
- deferred
- );
- if (order[0] == 0)
- {
- if (!ex0)
- co_return std::variant<T, std::monostate>{
- std::in_place_index<0>,
- std::move(detail::awaitable_unwrap<T>(r0))};
- if (!ex1)
- co_return std::variant<T, std::monostate>{
- std::in_place_index<1>};
- throw multiple_exceptions(ex0);
- }
- else
- {
- if (!ex1)
- co_return std::variant<T, std::monostate>{
- std::in_place_index<1>};
- if (!ex0)
- co_return std::variant<T, std::monostate>{
- std::in_place_index<0>,
- std::move(detail::awaitable_unwrap<T>(r0))};
- throw multiple_exceptions(ex1);
- }
- }
- /// Wait for one operation to succeed.
- /**
- * If one operations succeeds, the other is cancelled as the OR-condition is
- * already satisfied.
- */
- template <typename T, typename U, typename Executor>
- awaitable<std::variant<T, U>, Executor> operator||(
- awaitable<T, Executor> t, awaitable<U, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, r0, ex1, r1] =
- co_await make_parallel_group(
- co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
- co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
- ).async_wait(
- wait_for_one_success(),
- deferred
- );
- if (order[0] == 0)
- {
- if (!ex0)
- co_return std::variant<T, U>{
- std::in_place_index<0>,
- std::move(detail::awaitable_unwrap<T>(r0))};
- if (!ex1)
- co_return std::variant<T, U>{
- std::in_place_index<1>,
- std::move(detail::awaitable_unwrap<U>(r1))};
- throw multiple_exceptions(ex0);
- }
- else
- {
- if (!ex1)
- co_return std::variant<T, U>{
- std::in_place_index<1>,
- std::move(detail::awaitable_unwrap<U>(r1))};
- if (!ex0)
- co_return std::variant<T, U>{
- std::in_place_index<0>,
- std::move(detail::awaitable_unwrap<T>(r0))};
- throw multiple_exceptions(ex1);
- }
- }
- namespace detail {
- template <typename... T>
- struct widen_variant
- {
- template <std::size_t I, typename SourceVariant>
- static std::variant<T...> call(SourceVariant& source)
- {
- if (source.index() == I)
- return std::variant<T...>{
- std::in_place_index<I>, std::move(std::get<I>(source))};
- else if constexpr (I + 1 < std::variant_size_v<SourceVariant>)
- return call<I + 1>(source);
- else
- throw std::logic_error("empty variant");
- }
- };
- } // namespace detail
- /// Wait for one operation to succeed.
- /**
- * If one operations succeeds, the other is cancelled as the OR-condition is
- * already satisfied.
- */
- template <typename... T, typename Executor>
- awaitable<std::variant<T..., std::monostate>, Executor> operator||(
- awaitable<std::variant<T...>, Executor> t, awaitable<void, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, r0, ex1] =
- co_await make_parallel_group(
- co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
- co_spawn(ex, std::move(u), deferred)
- ).async_wait(
- wait_for_one_success(),
- deferred
- );
- using widen = detail::widen_variant<T..., std::monostate>;
- if (order[0] == 0)
- {
- if (!ex0)
- co_return widen::template call<0>(
- detail::awaitable_unwrap<std::variant<T...>>(r0));
- if (!ex1)
- co_return std::variant<T..., std::monostate>{
- std::in_place_index<sizeof...(T)>};
- throw multiple_exceptions(ex0);
- }
- else
- {
- if (!ex1)
- co_return std::variant<T..., std::monostate>{
- std::in_place_index<sizeof...(T)>};
- if (!ex0)
- co_return widen::template call<0>(
- detail::awaitable_unwrap<std::variant<T...>>(r0));
- throw multiple_exceptions(ex1);
- }
- }
- /// Wait for one operation to succeed.
- /**
- * If one operations succeeds, the other is cancelled as the OR-condition is
- * already satisfied.
- */
- template <typename... T, typename U, typename Executor>
- awaitable<std::variant<T..., U>, Executor> operator||(
- awaitable<std::variant<T...>, Executor> t, awaitable<U, Executor> u)
- {
- auto ex = co_await this_coro::executor;
- auto [order, ex0, r0, ex1, r1] =
- co_await make_parallel_group(
- co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
- co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
- ).async_wait(
- wait_for_one_success(),
- deferred
- );
- using widen = detail::widen_variant<T..., U>;
- if (order[0] == 0)
- {
- if (!ex0)
- co_return widen::template call<0>(
- detail::awaitable_unwrap<std::variant<T...>>(r0));
- if (!ex1)
- co_return std::variant<T..., U>{
- std::in_place_index<sizeof...(T)>,
- std::move(detail::awaitable_unwrap<U>(r1))};
- throw multiple_exceptions(ex0);
- }
- else
- {
- if (!ex1)
- co_return std::variant<T..., U>{
- std::in_place_index<sizeof...(T)>,
- std::move(detail::awaitable_unwrap<U>(r1))};
- if (!ex0)
- co_return widen::template call<0>(
- detail::awaitable_unwrap<std::variant<T...>>(r0));
- throw multiple_exceptions(ex1);
- }
- }
- } // namespace awaitable_operators
- } // namespace experimental
- } // namespace asio
- } // namespace boost
- #include <boost/asio/detail/pop_options.hpp>
- #endif // BOOST_ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP
|