/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Lewis Baker // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// #ifndef CPPCORO_FMAP_HPP_INCLUDED #define CPPCORO_FMAP_HPP_INCLUDED #include #include #include #include #include namespace cppcoro { namespace detail { template class fmap_awaiter { using awaiter_t = typename awaitable_traits::awaiter_t; FUNC&& m_func; awaiter_t m_awaiter; public: fmap_awaiter(FUNC&& func, AWAITABLE&& awaitable) noexcept( std::is_nothrow_move_constructible_v && noexcept(detail::get_awaiter(static_cast(awaitable)))) : m_func(static_cast(func)) , m_awaiter(detail::get_awaiter(static_cast(awaitable))) {} decltype(auto) await_ready() noexcept(noexcept(static_cast(m_awaiter).await_ready())) { return static_cast(m_awaiter).await_ready(); } template decltype(auto) await_suspend(cppcoro::coroutine_handle coro) noexcept(noexcept(static_cast(m_awaiter).await_suspend(std::move(coro)))) { return static_cast(m_awaiter).await_suspend(std::move(coro)); } template< typename AWAIT_RESULT = decltype(std::declval().await_resume()), std::enable_if_t, int> = 0> decltype(auto) await_resume() noexcept(noexcept(std::invoke(static_cast(m_func)))) { static_cast(m_awaiter).await_resume(); return std::invoke(static_cast(m_func)); } template< typename AWAIT_RESULT = decltype(std::declval().await_resume()), std::enable_if_t, int> = 0> decltype(auto) await_resume() noexcept(noexcept(std::invoke(static_cast(m_func), static_cast(m_awaiter).await_resume()))) { return std::invoke( static_cast(m_func), static_cast(m_awaiter).await_resume()); } }; template class fmap_awaitable { static_assert(!std::is_lvalue_reference_v); static_assert(!std::is_lvalue_reference_v); public: template< typename FUNC_ARG, typename AWAITABLE_ARG, std::enable_if_t< std::is_constructible_v && std::is_constructible_v, int> = 0> explicit fmap_awaitable(FUNC_ARG&& func, AWAITABLE_ARG&& awaitable) noexcept( std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) : m_func(static_cast(func)) , m_awaitable(static_cast(awaitable)) {} auto operator co_await() const & { return fmap_awaiter(m_func, m_awaitable); } auto operator co_await() & { return fmap_awaiter(m_func, m_awaitable); } auto operator co_await() && { return fmap_awaiter( static_cast(m_func), static_cast(m_awaitable)); } private: FUNC m_func; AWAITABLE m_awaitable; }; } template struct fmap_transform { explicit fmap_transform(FUNC&& f) noexcept(std::is_nothrow_move_constructible_v) : func(std::forward(f)) {} FUNC func; }; template< typename FUNC, typename AWAITABLE, std::enable_if_t, int> = 0> auto fmap(FUNC&& func, AWAITABLE&& awaitable) { return detail::fmap_awaitable< std::remove_cv_t>, std::remove_cv_t>>( std::forward(func), std::forward(awaitable)); } template auto fmap(FUNC&& func) { return fmap_transform{ std::forward(func) }; } template decltype(auto) operator|(T&& value, fmap_transform&& transform) { // Use ADL for finding fmap() overload. return fmap(std::forward(transform.func), std::forward(value)); } template decltype(auto) operator|(T&& value, const fmap_transform& transform) { // Use ADL for finding fmap() overload. return fmap(transform.func, std::forward(value)); } template decltype(auto) operator|(T&& value, fmap_transform& transform) { // Use ADL for finding fmap() overload. return fmap(transform.func, std::forward(value)); } } #endif