in include/unifex/connect_awaitable.hpp [166:218]
static auto connect_impl(Awaitable awaitable, Receiver receiver)
-> _await::sender_task<Receiver> {
#if !UNIFEX_NO_EXCEPTIONS
std::exception_ptr ex;
try {
#endif // !UNIFEX_NO_EXCEPTIONS
using result_type = sender_single_value_result_t<Awaitable>;
// This is a bit mind bending control-flow wise.
// We are first evaluating the co_await expression.
// Then the result of that is passed into std::invoke
// which curries a reference to the result into another
// lambda which is then returned to 'co_yield'.
// The 'co_yield' expression then invokes this lambda
// after the coroutine is suspended so that it is safe
// for the receiver to destroy the coroutine.
co_yield [&](result_type&& result) {
return [&] {
constexpr size_t valueOverloadCount =
sender_value_types_t<Awaitable, count_types, single_value_type>::value;
static_assert(valueOverloadCount <= 1);
if constexpr (valueOverloadCount == 1) {
constexpr size_t valueCount =
sender_value_types_t<Awaitable, type_identity_t, count_types>::value;
if constexpr (valueCount == 0) {
unifex::set_value(std::move(receiver));
} else if constexpr (valueCount == 1) {
unifex::set_value(std::move(receiver), static_cast<result_type&&>(result));
} else {
std::apply(set_value_applicator<Receiver>{receiver}, (result_type&&)result);
}
} else {
// Shouldn't complete with a value if there are no value_types
// specified.
std::terminate();
}
};
// The _comma_hack here makes this well-formed when the co_await
// expression has type void. This could potentially run into trouble
// if the type of the co_await expression itself overloads operator
// comma, but that's pretty unlikely.
}((co_await (Awaitable &&)awaitable, _comma_hack{}));
#if !UNIFEX_NO_EXCEPTIONS
} catch (...) {
ex = std::current_exception();
}
co_yield[&] {
unifex::set_error(std::move(receiver), std::move(ex));
};
#endif // !UNIFEX_NO_EXCEPTIONS
}