Skip to content

Commit

Permalink
added composed.
Browse files Browse the repository at this point in the history
  • Loading branch information
klemens-morgenstern committed Dec 11, 2023
1 parent 820f9c3 commit f6b33eb
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 1 deletion.
13 changes: 13 additions & 0 deletions doc/reference/async_for.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,16 @@ cobalt::main co_main(int argc, char * argv[])
The requirement is that the <<awaitable,awaitable>> used in the for loop has an `operator bool` to check if it
can be awaited again. This is the case for <<generator, generator>> and <<promise,promise>>.

That is, `BOOST_COBALT_FOR(__expression__, __declaration__) __statement__` the above statement is roughly equivalent to:

[source,cpp,subs=+quotes]
----
{
auto && __aw__ = co_await __expression__;
while (__aw__)
{
__expression__ = co_await __aw__;
__statement__
}
}
----
139 changes: 139 additions & 0 deletions include/boost/cobalt/composed.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//
// Copyright (c) 2023 Klemens Morgenstern ([email protected])
//
// 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_COMPOSED_HPP
#define BOOST_COBALT_COMPOSED_HPP

#include <boost/cobalt/detail/handler.hpp>

namespace boost::cobalt::detail
{

template<typename ... Args>
struct composed_promise_return
{
completion_handler<Args...> & handler;

void return_value(std::tuple<Args...> && tup)
{
handler.result.emplace(std::move(tup));
}
};


template<>
struct composed_promise_return<>
{
completion_handler<> & handler;

void return_void()
{
handler.result.emplace();
}
};

template<typename ... Args>
struct composed_promise : composed_promise_return<Args...>
{
#if !defined(BOOST_COBALT_NO_PMR)
template<typename ... Args_>
void * operator new(const std::size_t size, Args_ & ... args)
{
auto res = std::get<sizeof...(Args_) - 1>(std::tie(args...)).get_allocator().resource();
const auto p = res->allocate(size + sizeof(pmr::memory_resource *), alignof(pmr::memory_resource *));
auto pp = static_cast<pmr::memory_resource**>(p);
*pp = res;
return pp + 1;
}

void operator delete(void * raw, const std::size_t size) noexcept
{
const auto p = static_cast<pmr::memory_resource**>(raw) - 1;
pmr::memory_resource * res = *p;
res->deallocate(p, size + sizeof(pmr::memory_resource *), alignof(pmr::memory_resource *));
}
#endif

template<typename ... Args_>
composed_promise(Args_ & ... args)
: composed_promise_return<Args...>(std::get<sizeof...(Args_) - 1u>(std::tie(args...))) {}

using executor_type = executor;
const executor_type & get_executor() const {return this->handler.get_executor();}

#if !defined(BOOST_COBALT_NO_PMR)
using allocator_type = pmr::polymorphic_allocator<void>;
allocator_type get_allocator() const {return this->handler.get_allocator();}
#endif

using cancellation_slot_type = asio::cancellation_slot;
cancellation_slot_type get_cancellation_slot() const noexcept {return this->handler.get_cancellation_slot();}

void get_return_object() {}
std::suspend_never initial_suspend() {return {};}

auto final_suspend() noexcept
{
struct awaitable
{
completion_handler<Args...> & handler;
constexpr bool await_ready() noexcept
{
return handler.completed_immediately != nullptr
&& *handler.completed_immediately == detail::completed_immediately_t::maybe;
}

std::coroutine_handle<void> await_suspend(std::coroutine_handle<composed_promise> h) noexcept
{
auto tmp = handler.self.release();
detail::self_destroy(h);
return tmp;
}

void await_resume() noexcept
{
*handler.completed_immediately = detail::completed_immediately_t::yes;
handler.self.release();
}

};

return awaitable{this->handler};
}

void unhandled_exception() {throw;}

};

template<typename ... Args>
composed_promise<Args...> completion_handler_probe(const completion_handler<Args...> & );

template<typename T>
concept is_completion_handler = requires (const T & h) {{completion_handler_probe(h)};};

}


namespace std
{

template<typename ... Args>
requires boost::cobalt::detail::is_completion_handler<
boost::mp11::mp_back<boost::mp11::mp_list<Args...>>>
struct coroutine_traits<void, Args...>
{
using promise_type = decltype(completion_handler_probe(
std::declval<const boost::mp11::mp_back<boost::mp11::mp_list<Args...>>&>()));

};


}



#endif //BOOST_COBALT_COMPOSED_HPP
12 changes: 12 additions & 0 deletions include/boost/cobalt/detail/handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ namespace boost::cobalt
namespace detail
{

template<typename ... >
struct composed_promise_return;


template<typename ... >
struct composed_promise;

enum class completed_immediately_t
{
no, maybe, yes, initiating
Expand Down Expand Up @@ -285,6 +292,11 @@ struct completion_handler : detail::completion_handler_base
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
boost::source_location loc_;
#endif

template<typename ... >
friend struct detail::composed_promise_return;
template<typename ... >
friend struct detail::composed_promise;
};

};
Expand Down
2 changes: 1 addition & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ add_executable(boost_cobalt_main_compile main_compile.cpp)
add_executable(boost_cobalt_basic_tests
async_for.cpp test_main.cpp promise.cpp with.cpp op.cpp handler.cpp join.cpp race.cpp this_coro.cpp leaf.cpp
channel.cpp generator.cpp run.cpp task.cpp gather.cpp wait_group.cpp wrappers.cpp left_race.cpp
strand.cpp fork.cpp thread.cpp any_completion_handler.cpp detached.cpp monotonic_resource.cpp sbo_resource.cpp)
strand.cpp fork.cpp thread.cpp any_completion_handler.cpp detached.cpp monotonic_resource.cpp sbo_resource.cpp composed.cpp)

target_link_libraries(boost_cobalt_main Boost::cobalt)
target_link_libraries(boost_cobalt_main_compile Boost::cobalt)
Expand Down
61 changes: 61 additions & 0 deletions test/composed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// Copyright (c) 2023 Klemens Morgenstern ([email protected])
//
// 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)
//

#include <boost/cobalt/composed.hpp>
#include <boost/cobalt/op.hpp>

#include <boost/test/unit_test.hpp>
#include "test.hpp"

using namespace boost;


BOOST_AUTO_TEST_SUITE(composed);

struct post_composed_op : cobalt::op<>
{

post_composed_op() {}

void initiate(cobalt::completion_handler<> complete)
{
co_await asio::post(cobalt::use_op);
}
};


struct ec_composed_op : cobalt::op<system::error_code>
{
ec_composed_op() {}

void initiate(cobalt::completion_handler<system::error_code> complete)
{
co_return asio::error::already_open;
}
};

struct ec_n_composed_op : cobalt::op<system::error_code, std::size_t>
{

ec_n_composed_op() {}

void initiate(cobalt::completion_handler<system::error_code, std::size_t> complete)
{
co_await asio::post(cobalt::use_op);
co_return std::make_tuple(system::error_code{}, 2u);
}
};


CO_TEST_CASE(composed_op)
{
co_await post_composed_op();
BOOST_CHECK_THROW(co_await ec_composed_op(), system::system_error);
BOOST_CHECK(co_await ec_n_composed_op() == 2u);
}

BOOST_AUTO_TEST_SUITE_END();

0 comments on commit f6b33eb

Please sign in to comment.