| // { dg-additional-options "-std=c++17 -w" } |
| |
| #include <utility> |
| #include <type_traits> |
| #include <tuple> |
| #include <functional> |
| #include <coroutine> |
| |
| struct any { |
| template <typename T> any(T &&) noexcept; |
| }; |
| |
| template <typename T> |
| auto get_awaiter_impl(T &&value, int) noexcept |
| -> decltype(static_cast<T &&>(value).operator co_await()) { |
| return static_cast<T &&>(value).operator co_await(); |
| } |
| template <typename T, int = 0> |
| T &&get_awaiter_impl(T &&value, any) noexcept; |
| template <typename T> |
| auto get_awaiter(T &&value) noexcept |
| -> decltype(get_awaiter_impl(static_cast<T &&>(value), 123)) { |
| return get_awaiter_impl(static_cast<T &&>(value), 123); |
| } |
| |
| template <typename T, typename = void> struct awaitable_traits { |
| using awaiter_t = decltype(get_awaiter(std::declval<T>())); |
| using await_result_t = decltype(std::declval<awaiter_t>().await_resume()); |
| }; |
| |
| template <typename TASK_CONTAINER> class when_all_ready_awaitable; |
| template <typename... TASKS> |
| class when_all_ready_awaitable<std::tuple<TASKS...>> { |
| public: |
| explicit when_all_ready_awaitable(std::tuple<TASKS...> &&tasks) noexcept |
| : m_tasks(std::move(tasks)) {} |
| auto operator co_await() &&noexcept { |
| struct awaiter { |
| awaiter(when_all_ready_awaitable &awaitable) noexcept |
| : m_awaitable(awaitable) {} |
| bool await_ready() const noexcept { return false; } |
| bool await_suspend() noexcept { return false; } |
| std::tuple<TASKS...> &&await_resume() noexcept { |
| return std::move(m_awaitable.m_tasks); |
| } |
| when_all_ready_awaitable& m_awaitable; |
| }; |
| return awaiter{*this}; |
| } |
| std::tuple<TASKS...> m_tasks; |
| }; |
| |
| inline void *operator new(std::size_t, void *__p) noexcept; |
| |
| template <typename RESULT> |
| class when_all_task_promise final{ |
| public: |
| using coroutine_handle_t = std::coroutine_handle<when_all_task_promise>; |
| RESULT &&result() &&; |
| }; |
| template <typename RESULT> class when_all_task final { |
| public: |
| using promise_type = when_all_task_promise<RESULT>; |
| using coroutine_handle_t = typename promise_type::coroutine_handle_t; |
| decltype(auto) result() &; |
| decltype(auto) result() && { |
| return std::move(m_coroutine.promise()).result(); |
| } |
| decltype(auto) non_void_result() && { |
| if constexpr (std::is_void_v<decltype(0)>) |
| ; |
| else |
| return std::move(*this).result(); |
| } |
| coroutine_handle_t m_coroutine; |
| }; |
| class task; |
| template <typename AWAITABLE, |
| typename RESULT = |
| typename awaitable_traits<AWAITABLE &&>::await_result_t, |
| std::enable_if_t<!std::is_void_v<RESULT>, int> = 0> |
| when_all_task<RESULT> make_when_all_task(AWAITABLE awaitable); |
| |
| template <typename... AWAITABLES> |
| inline auto when_all_ready(AWAITABLES &&... awaitables) { |
| return when_all_ready_awaitable< |
| std::tuple<when_all_task<typename awaitable_traits< |
| std::remove_reference_t<AWAITABLES>>::await_result_t>...>>( |
| std::make_tuple( |
| make_when_all_task(std::forward<AWAITABLES>(awaitables))...)); |
| } |
| |
| template <typename FUNC, typename AWAITABLE> class fmap_awaiter { |
| using awaiter_t = typename awaitable_traits<AWAITABLE &&>::awaiter_t; |
| |
| public: |
| fmap_awaiter(FUNC &&func, AWAITABLE &&awaitable) noexcept |
| : m_func(static_cast<FUNC &&>(func)), |
| m_awaiter(get_awaiter(static_cast<AWAITABLE &&>(awaitable))) {} |
| decltype(auto) await_ready() noexcept { |
| return static_cast<awaiter_t &&>(m_awaiter).await_ready(); |
| } |
| template <typename PROMISE> |
| decltype(auto) await_suspend(std::coroutine_handle<PROMISE> coro) noexcept {} |
| template <typename AWAIT_RESULT = |
| decltype(std::declval<awaiter_t>().await_resume()), |
| std::enable_if_t<!std::is_void_v<AWAIT_RESULT>, int> = 0> |
| decltype(auto) await_resume() noexcept { |
| return std::invoke(static_cast<FUNC &&>(m_func), |
| static_cast<awaiter_t &&>(m_awaiter).await_resume()); |
| } |
| |
| private: |
| FUNC &&m_func; |
| awaiter_t m_awaiter; |
| }; |
| template <typename FUNC, typename AWAITABLE> class fmap_awaitable { |
| public: |
| template < |
| typename FUNC_ARG, typename AWAITABLE_ARG, |
| std::enable_if_t<std::is_constructible_v<FUNC, FUNC_ARG &&> && |
| std::is_constructible_v<AWAITABLE, AWAITABLE_ARG &&>, |
| int> = 0> |
| explicit fmap_awaitable(FUNC_ARG &&func, AWAITABLE_ARG &&awaitable) noexcept |
| : m_func(static_cast<FUNC_ARG &&>(func)), |
| m_awaitable(static_cast<AWAITABLE_ARG &&>(awaitable)) {} |
| auto operator co_await() && { |
| return fmap_awaiter(static_cast<FUNC &&>(m_func), |
| static_cast<AWAITABLE &&>(m_awaitable)); |
| } |
| |
| private: |
| FUNC m_func; |
| AWAITABLE m_awaitable; |
| }; |
| |
| template <typename FUNC, typename AWAITABLE> |
| auto fmap(FUNC &&func, AWAITABLE &&awaitable) { |
| return fmap_awaitable<std::remove_cv_t<std::remove_reference_t<FUNC>>, |
| std::remove_cv_t<std::remove_reference_t<AWAITABLE>>>( |
| std::forward<FUNC>(func), std::forward<AWAITABLE>(awaitable)); |
| } |
| template <typename... AWAITABLES> |
| auto when_all(AWAITABLES &&... awaitables) { |
| return fmap( |
| [](auto &&taskTuple) { |
| decltype(auto) __trans_tmp_1 = std::apply( |
| [](auto &&... tasks) { |
| return std::make_tuple( |
| static_cast<decltype(tasks)>(tasks).non_void_result()...); |
| }, |
| static_cast<decltype(taskTuple)>(taskTuple)); |
| return __trans_tmp_1; |
| }, |
| when_all_ready(std::forward<AWAITABLES>(awaitables)...)); |
| } |
| class async_mutex_scoped_lock_operation; |
| class async_mutex { |
| public: |
| async_mutex() noexcept; |
| async_mutex_scoped_lock_operation scoped_lock_async() noexcept; |
| }; |
| class async_mutex_lock { |
| public: |
| explicit async_mutex_lock(); |
| ~async_mutex_lock(); |
| |
| private: |
| async_mutex *m_mutex; |
| }; |
| class async_mutex_scoped_lock_operation { |
| public: |
| async_mutex_lock await_resume() const noexcept; |
| }; |
| class task { |
| public: |
| class promise_type { |
| public: |
| auto initial_suspend() noexcept { return std::suspend_always{}; } |
| auto final_suspend() noexcept { return std::suspend_always{}; } |
| task get_return_object() noexcept { return task{}; } |
| void unhandled_exception() noexcept {} |
| void return_value(int value) noexcept { v = value; } |
| int result(){ return v; } |
| int v = 0; |
| }; |
| public: |
| task() noexcept {} |
| auto operator co_await() const &noexcept { |
| struct awaitable { |
| std::coroutine_handle<promise_type> m_coroutine; |
| decltype(auto) await_resume() { |
| return this->m_coroutine.promise().result(); |
| } |
| }; |
| return awaitable{}; |
| } |
| }; |
| void foo() { |
| (void) []() -> task { |
| auto makeTask = [](int x) -> task { co_return x; }; |
| async_mutex_scoped_lock_operation op; |
| co_await when_all(std::move(op), makeTask(123)); |
| }(); |
| } |