Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added composed. #90

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
Loading