diff --git a/include/beman/execution/detail/basic_sender.hpp b/include/beman/execution/detail/basic_sender.hpp index 2cf03d79..5f46cf82 100644 --- a/include/beman/execution/detail/basic_sender.hpp +++ b/include/beman/execution/detail/basic_sender.hpp @@ -17,6 +17,7 @@ import beman.execution.detail.completion_signatures_for; import beman.execution.detail.connect; import beman.execution.detail.connect_all; import beman.execution.detail.decays_to; +import beman.execution.detail.dependent_sender; import beman.execution.detail.get_completion_signatures; import beman.execution.detail.impls_for; import beman.execution.detail.product_type; @@ -28,6 +29,7 @@ import beman.execution.detail.sender_decompose; #include #include #include +#include #include #include #include @@ -110,6 +112,7 @@ struct basic_sender : ::beman::execution::detail::product_type Self, typename... Env> + requires(sizeof...(Env) == 1) || (... && !::beman::execution::dependent_sender) static consteval auto get_completion_signatures() noexcept { if constexpr (requires { Tag::template get_completion_signatures(); }) return Tag::template get_completion_signatures(); diff --git a/include/beman/execution/detail/common.hpp b/include/beman/execution/detail/common.hpp index 77235773..2f86f712 100644 --- a/include/beman/execution/detail/common.hpp +++ b/include/beman/execution/detail/common.hpp @@ -17,6 +17,17 @@ #define BEMAN_SPECIALIZE_EXPORT template <> #endif +#define BEMAN_EXECUTION_TRY_EVAL(rcvr, expr) \ + do { \ + try { \ + (expr); \ + } catch (...) { \ + if constexpr (!noexcept((expr))) { \ + ::beman::execution::set_error(::std::move((rcvr)), ::std::current_exception()); \ + } \ + } \ + } while (false) + // ---------------------------------------------------------------------------- /*! * \mainpage Asynchronous Operation Support diff --git a/include/beman/execution/detail/dependent_sender.hpp b/include/beman/execution/detail/dependent_sender.hpp new file mode 100644 index 00000000..e643309c --- /dev/null +++ b/include/beman/execution/detail/dependent_sender.hpp @@ -0,0 +1,41 @@ +// include/beman/execution/detail/dependent_sender.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_DEPENDENT_SENDER +#define INCLUDED_BEMAN_EXECUTION_DETAIL_DEPENDENT_SENDER + +#include +#ifdef BEMAN_HAS_IMPORT_STD +import std; +#else +#include +#endif +#ifdef BEMAN_HAS_MODULES +import beman.execution.detail.get_completion_signatures; +import beman.execution.detail.sender; +#else +#include +#include +#endif + +// ---------------------------------------------------------------------------- + +namespace beman::execution::detail { +template +auto non_dependent_sender_helper() noexcept -> void {} + +template +concept non_dependent_sender = ::beman::execution::sender && requires { + ::beman::execution::detail::non_dependent_sender_helper<::beman::execution::get_completion_signatures()>(); +}; +} // namespace beman::execution::detail + +namespace beman::execution { +template +concept dependent_sender = + ::beman::execution::sender && !::beman::execution::detail::non_dependent_sender; +} // namespace beman::execution + +// ---------------------------------------------------------------------------- + +#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_DEPENDENT_SENDER diff --git a/include/beman/execution/detail/into_variant.hpp b/include/beman/execution/detail/into_variant.hpp index 54a89484..167e3892 100644 --- a/include/beman/execution/detail/into_variant.hpp +++ b/include/beman/execution/detail/into_variant.hpp @@ -20,6 +20,9 @@ import beman.execution.detail.completion_signatures; import beman.execution.detail.completion_signatures_for; import beman.execution.detail.decayed_tuple; import beman.execution.detail.default_impls; +import beman.execution.detail.dependent_sender; +import beman.execution.detail.dependent_sender_error; +import beman.execution.detail.env; import beman.execution.detail.env_of_t; import beman.execution.detail.error_types_of_t; import beman.execution.detail.get_domain_early; @@ -27,6 +30,7 @@ import beman.execution.detail.impls_for; import beman.execution.detail.make_sender; import beman.execution.detail.meta.combine; import beman.execution.detail.sender; +import beman.execution.detail.sender_adaptor_closure; import beman.execution.detail.sends_stopped; import beman.execution.detail.set_error; import beman.execution.detail.set_stopped; @@ -39,6 +43,9 @@ import beman.execution.detail.variant_or_empty; #include #include #include +#include +#include +#include #include #include #include @@ -46,6 +53,7 @@ import beman.execution.detail.variant_or_empty; #include #include #include +#include #include #include #include @@ -56,25 +64,25 @@ import beman.execution.detail.variant_or_empty; // ---------------------------------------------------------------------------- namespace beman::execution::detail { -struct into_variant_t { +struct into_variant_t : ::beman::execution::sender_adaptor_closure { template <::beman::execution::sender Sender> auto operator()(Sender&& sender) const { - // auto domain{::beman::execution::detail::get_domain_early(sender)}; - //(void)domain; - return ::beman::execution::detail::make_sender(*this, {}, ::std::forward(sender)); - // return ::beman::execution::transform_sender( - // ::std::move(domain), - // ::beman::execution::detail::make_sender(*this, {}, ::std::forward(sender)) - //); + return ::beman::execution::transform_sender( + ::beman::execution::detail::get_domain_early(sender), + ::beman::execution::detail::make_sender(*this, {}, ::std::forward(sender))); } + auto operator()() const noexcept { return ::beman::execution::detail::make_sender_adaptor(*this); } + private: template using make_error_types = ::beman::execution::completion_signatures<::beman::execution::set_error_t(E)...>; private: - template + template struct get_signatures; + template + struct get_signatures : get_signatures> {}; template <::beman::execution::sender Child, typename State, typename Env> struct get_signatures< ::beman::execution::detail::basic_sender<::beman::execution::detail::into_variant_t, State, Child>, @@ -99,9 +107,9 @@ struct into_variant_t { }; public: - template <::beman::execution::sender Sender, typename Env> + template <::beman::execution::sender Sender, typename... Env> static consteval auto get_completion_signatures() { - return get_signatures<::std::remove_cvref_t, Env>::get(); + return get_signatures<::std::remove_cvref_t, Env...>::get(); } struct impls_for : ::beman::execution::detail::default_impls { struct get_state_impl { @@ -119,14 +127,14 @@ struct into_variant_t { if constexpr (::std::same_as) { using variant_type = typename State::type; using tuple_type = ::beman::execution::detail::decayed_tuple; - try { - if constexpr (sizeof...(Args) == 0u) - ::beman::execution::set_value(::std::move(receiver)); - else + if constexpr (std::same_as) { + static_assert(sizeof...(Args) == 0); + BEMAN_EXECUTION_TRY_EVAL(receiver, ::beman::execution::set_value(std::move(receiver))); + } else { + BEMAN_EXECUTION_TRY_EVAL( + receiver, ::beman::execution::set_value(::std::move(receiver), - variant_type(tuple_type{::std::forward(args)...})); - } catch (...) { - ::beman::execution::set_error(::std::move(receiver), ::std::current_exception()); + variant_type(tuple_type{::std::forward(args)...}))); } } else { Tag()(::std::move(receiver), ::std::forward(args)...); diff --git a/include/beman/execution/detail/read_env.hpp b/include/beman/execution/detail/read_env.hpp index 0082bd2d..5ab59ed8 100644 --- a/include/beman/execution/detail/read_env.hpp +++ b/include/beman/execution/detail/read_env.hpp @@ -17,6 +17,7 @@ import beman.execution.detail.basic_sender; import beman.execution.detail.completion_signatures; import beman.execution.detail.completion_signatures_for; import beman.execution.detail.default_impls; +import beman.execution.detail.env_of_t; import beman.execution.detail.get_env; import beman.execution.detail.impls_for; import beman.execution.detail.make_sender; @@ -27,6 +28,7 @@ import beman.execution.detail.set_value; #include #include #include +#include #include #include #include @@ -59,18 +61,21 @@ struct read_env_t { }; public: - template + template static consteval auto get_completion_signatures() { - return typename get_signatures<::std::remove_cvref_t, Env...>::type{}; + return typename get_signatures<::std::remove_cvref_t, Env>::type{}; } struct impls_for : ::beman::execution::detail::default_impls { struct start_impl { - auto operator()(auto query, auto& receiver) const noexcept -> void { + template + auto operator()(Query query, Receiver& receiver) const noexcept -> void { try { auto env{::beman::execution::get_env(receiver)}; ::beman::execution::set_value(::std::move(receiver), query(env)); } catch (...) { - ::beman::execution::set_error(::std::move(receiver), ::std::current_exception()); + if constexpr (!std::is_nothrow_invocable_v>) { + ::beman::execution::set_error(::std::move(receiver), ::std::current_exception()); + } } } }; diff --git a/include/beman/execution/detail/sender.hpp b/include/beman/execution/detail/sender.hpp index 3b43d36d..80de7c50 100644 --- a/include/beman/execution/detail/sender.hpp +++ b/include/beman/execution/detail/sender.hpp @@ -42,7 +42,10 @@ concept enable_sender = } // namespace beman::execution::detail namespace beman::execution { template -concept sender = ::beman::execution::detail::enable_sender<::std::remove_cvref_t> && +inline constexpr bool enable_sender = ::beman::execution::detail::enable_sender; + +template +concept sender = ::beman::execution::enable_sender<::std::remove_cvref_t> && requires(const ::std::remove_cvref_t& sndr) { { ::beman::execution::get_env(sndr) } -> ::beman::execution::detail::queryable; } && ::std::move_constructible<::std::remove_cvref_t> && diff --git a/include/beman/execution/detail/then.hpp b/include/beman/execution/detail/then.hpp index 85d59dd1..2f4b239d 100644 --- a/include/beman/execution/detail/then.hpp +++ b/include/beman/execution/detail/then.hpp @@ -21,6 +21,9 @@ import beman.execution.detail.completion_signatures; import beman.execution.detail.completion_signatures_for; import beman.execution.detail.completion_signatures_of_t; import beman.execution.detail.default_impls; +import beman.execution.detail.dependent_sender; +import beman.execution.detail.dependent_sender_error; +import beman.execution.detail.env; import beman.execution.detail.get_domain_early; import beman.execution.detail.impls_for; import beman.execution.detail.make_sender; @@ -41,6 +44,9 @@ import beman.execution.detail.transform_sender; #include #include #include +#include +#include +#include #include #include #include @@ -125,17 +131,19 @@ struct then_t : ::beman::execution::sender_adaptor_closure> { private: template struct get_signatures; - template + template + struct get_signatures : get_signatures> {}; + template struct get_signatures< ::beman::execution::detail::basic_sender<::beman::execution::detail::then_t, Fun, Child>, - Env...> { + Env> { using type = ::beman::execution::detail::meta::unique<::beman::execution::detail::meta::combine< ::beman::execution::detail::meta::transform< ::beman::execution::detail::then_transform_t::template transform, - ::beman::execution::completion_signatures_of_t>, + ::beman::execution::completion_signatures_of_t>, ::std::conditional_t< ::beman::execution::detail:: - then_exception>::value, + then_exception>::value, ::beman::execution::completion_signatures<::beman::execution::set_error_t(::std::exception_ptr)>, ::beman::execution::completion_signatures<>>>>; }; diff --git a/include/beman/execution/detail/when_all.hpp b/include/beman/execution/detail/when_all.hpp index 0868c2c2..47ce6fde 100644 --- a/include/beman/execution/detail/when_all.hpp +++ b/include/beman/execution/detail/when_all.hpp @@ -26,6 +26,8 @@ import beman.execution.detail.decayed_tuple; import beman.execution.detail.decayed_type_list; import beman.execution.detail.default_domain; import beman.execution.detail.default_impls; +import beman.execution.detail.dependent_sender; +import beman.execution.detail.dependent_sender_error; import beman.execution.detail.env; import beman.execution.detail.env_of_t; import beman.execution.detail.error_types_of_t; @@ -63,6 +65,8 @@ import beman.execution.detail.value_types_of_t; #include #include #include +#include +#include #include #include #include @@ -102,15 +106,27 @@ struct when_all_value_types<::beman::execution::detail::type_list> { using type = ::beman::execution::completion_signatures<::beman::execution::set_value_t(T...)>; }; +template +concept valid_when_all_sender = ::beman::execution::dependent_sender || + ::beman::execution::detail::meta::size_v< + ::beman::execution::value_types_of_t, + ::std::tuple, + ::beman::execution::detail::type_list>> == 1u; + +inline constexpr auto make_when_all_env = [](const ::beman::execution::inplace_stop_source& stop_src, + const auto& env) noexcept { + return ::beman::execution::detail::join_env( + ::beman::execution::detail::make_env(::beman::execution::get_stop_token, stop_src.get_token()), env); +}; + +template +using when_all_env = + decltype(make_when_all_env(::std::declval<::beman::execution::inplace_stop_source>(), ::std::declval())); + struct when_all_t { template <::beman::execution::sender... Sender> - requires(0u != sizeof...(Sender)) && - ((::beman::execution::detail::meta::size_v< - ::beman::execution::value_types_of_t, - ::std::tuple, - ::beman::execution::detail::type_list>> == 1u) && - ...) && + requires(0u != sizeof...(Sender)) && (... && beman::execution::detail::valid_when_all_sender) && requires(Sender&&... s) { typename ::std::common_type_t; } @@ -122,8 +138,10 @@ struct when_all_t { } private: - template + template struct get_signatures; + template + struct get_signatures : get_signatures> {}; template struct get_signatures< ::beman::execution::detail::basic_sender<::beman::execution::detail::when_all_t, Data, Sender...>, @@ -170,20 +188,30 @@ struct when_all_t { struct get_env_impl { template auto operator()(auto&&, State& state, const Receiver& receiver) const noexcept { - return ::beman::execution::detail::join_env( - ::beman::execution::detail::make_env(::beman::execution::get_stop_token, - state.stop_src.get_token()), - ::beman::execution::get_env(receiver)); + return make_when_all_env(state.stop_src, ::beman::execution::get_env(receiver)); } }; static constexpr auto get_env{get_env_impl{}}; enum class disposition : unsigned char { started, error, stopped }; + template + struct is_nothrow_decay_copy_constructible + : ::std::bool_constant<(... && ::std::is_nothrow_constructible_v<::std::decay_t, Values>)> {}; + template struct state_type { struct nonesuch {}; - using env_t = ::beman::execution::env_of_t; + using env_t = ::beman::execution::env_of_t; + using copy_fail = ::std::conditional_t< + (... && ::beman::execution::value_types_of_t::value) && + (... && + ::beman::execution::error_types_of_t::value), + nonesuch, + std::exception_ptr>; using values_tuple = ::std::tuple< ::beman::execution:: value_types_of_t...>; @@ -192,7 +220,7 @@ struct when_all_t { ::beman::execution::detail::meta::unique<::beman::execution::detail::meta::prepend< nonesuch, ::beman::execution::detail::meta::prepend< - ::std::exception_ptr, + copy_fail, ::beman::execution::detail::meta::combine<::beman::execution::detail::meta::to< ::beman::execution::detail::type_list, ::beman::execution::detail::meta::combine<::beman::execution::error_types_of_t< @@ -224,21 +252,21 @@ struct when_all_t { } break; case disposition::error: this->on_stop.reset(); - try { - ::std::visit( - [&](Error& error) noexcept { - if constexpr (!::std::same_as) { - ::beman::execution::set_error(::std::move(recvr), ::std::move(error)); - } - }, - this->errors); - } catch (...) { - ::beman::execution::set_error(::std::move(recvr), ::std::current_exception()); - } + ::std::visit( + [&](Error& error) noexcept { + if constexpr (!::std::same_as) { + ::beman::execution::set_error(::std::move(recvr), ::std::move(error)); + } + }, + this->errors); break; case disposition::stopped: - this->on_stop.reset(); - ::beman::execution::set_stopped(::std::move(recvr)); + if constexpr ((... || + ::beman::execution:: + sends_stopped>>)) { + this->on_stop.reset(); + ::beman::execution::set_stopped(::std::move(recvr)); + } break; } } @@ -282,12 +310,7 @@ struct when_all_t { state.receiver = &receiver; state.on_stop.emplace(::beman::execution::get_stop_token(::beman::execution::get_env(receiver)), ::beman::execution::detail::on_stop_request{state}); - if (state.stop_src.stop_requested()) { - state.on_stop.reset(); - ::beman::execution::set_stopped(std::move(receiver)); - } else { - (::beman::execution::start(ops), ...); - } + (::beman::execution::start(ops), ...); } }; static constexpr auto start{start_impl{}}; @@ -297,11 +320,16 @@ struct when_all_t { if constexpr (::std::same_as) { if (disposition::error != state.disp.exchange(disposition::error)) { state.stop_src.request_stop(); + using error_t = typename std::type_identity::type; + constexpr bool nothrow = ::std::is_nothrow_constructible_v<::std::decay_t, error_t>; try { - state.errors.template emplace::type>( - ::std::forward(args)...); + [&]() noexcept(nothrow) { + state.errors.template emplace<::std::decay_t>(::std::forward(args)...); + }(); } catch (...) { - state.errors.template emplace<::std::exception_ptr>(::std::current_exception()); + if constexpr (!nothrow) { + state.errors.template emplace<::std::exception_ptr>(::std::current_exception()); + } } } } else if constexpr (::std::same_as) { @@ -311,13 +339,17 @@ struct when_all_t { } } else if constexpr (!::std::same_as>) { if (state.disp == disposition::started) { - auto& opt = ::std::get(state.values); + auto& opt = ::std::get(state.values); + using decayed_tuple_t = typename ::std::decay_t::value_type; + constexpr bool nothrow = std::is_nothrow_constructible_v; try { - opt.emplace(::std::forward(args)...); + [&]() noexcept(nothrow) { opt.emplace(::std::forward(args)...); }(); } catch (...) { - if (disposition::error != state.disp.exchange(disposition::error)) { - state.stop_src.request_stop(); - state.errors.template emplace<::std::exception_ptr>(::std::current_exception()); + if constexpr (!nothrow) { + if (disposition::error != state.disp.exchange(disposition::error)) { + state.stop_src.request_stop(); + state.errors.template emplace<::std::exception_ptr>(::std::current_exception()); + } } } } diff --git a/include/beman/execution/execution.hpp b/include/beman/execution/execution.hpp index 6d632826..5f526386 100644 --- a/include/beman/execution/execution.hpp +++ b/include/beman/execution/execution.hpp @@ -18,6 +18,7 @@ import beman.execution.detail.completion_signatures; import beman.execution.detail.connect; import beman.execution.detail.continues_on; import beman.execution.detail.counting_scope; +import beman.execution.detail.dependent_sender; import beman.execution.detail.env; import beman.execution.detail.execution_policy; import beman.execution.detail.forwarding_query; @@ -79,6 +80,7 @@ import beman.execution.detail.write_env; #include #include #include +#include #include #include #include diff --git a/src/beman/execution/CMakeLists.txt b/src/beman/execution/CMakeLists.txt index 9344e402..674d52c5 100644 --- a/src/beman/execution/CMakeLists.txt +++ b/src/beman/execution/CMakeLists.txt @@ -68,6 +68,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/decays_to.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/default_domain.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/default_impls.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/dependent_sender.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/dependent_sender_error.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/execution_policy.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/emplace_from.hpp @@ -259,6 +260,7 @@ if(BEMAN_USE_MODULES) decays_to.cppm default_domain.cppm default_impls.cppm + dependent_sender.cppm dependent_sender_error.cppm execution_policy.cppm emplace_from.cppm diff --git a/src/beman/execution/dependent_sender.cppm b/src/beman/execution/dependent_sender.cppm new file mode 100644 index 00000000..93129e75 --- /dev/null +++ b/src/beman/execution/dependent_sender.cppm @@ -0,0 +1,11 @@ +module; +// src/beman/execution/dependent_sender.cppm -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +export module beman.execution.detail.dependent_sender; + +namespace beman::execution { +export using beman::execution::dependent_sender; +} // namespace beman::execution diff --git a/src/beman/execution/execution.cppm b/src/beman/execution/execution.cppm index 3bf9e4c2..3de60d3c 100644 --- a/src/beman/execution/execution.cppm +++ b/src/beman/execution/execution.cppm @@ -94,9 +94,8 @@ export import beman.execution.detail.receiver_of; // [exec.snd], senders export import beman.execution.detail.sender; export import beman.execution.detail.sender_in; -//-dk:TODO export using ::beman::execution::sender_to; export import beman.execution.detail.sender_to; - +export import beman.execution.detail.dependent_sender; export import beman.execution.detail.sends_stopped; namespace beman::execution { diff --git a/src/beman/execution/sender.cppm b/src/beman/execution/sender.cppm index 6443867f..3049e8a3 100644 --- a/src/beman/execution/sender.cppm +++ b/src/beman/execution/sender.cppm @@ -9,4 +9,5 @@ export module beman.execution.detail.sender; namespace beman::execution { export using beman::execution::sender_t; export using beman::execution::sender; +export using beman::execution::enable_sender; } // namespace beman::execution diff --git a/tests/beman/execution/exec-snd-expos.test.cpp b/tests/beman/execution/exec-snd-expos.test.cpp index b831b915..aca8b075 100644 --- a/tests/beman/execution/exec-snd-expos.test.cpp +++ b/tests/beman/execution/exec-snd-expos.test.cpp @@ -1146,7 +1146,10 @@ auto test_basic_sender() -> void { static_assert( std::same_as); - static_assert(test_std::sender_in); + static_assert(test_std::dependent_sender); + // According to https://eel.is/c++draft/exec#adapt.general-3.5, basic_sender shall be a dependent-sender + static_assert(test_std::dependent_sender); + static_assert(test_std::sender_in); #if 0 //-dk:TODO restore completion_sigatures_for test static_assert(std::same_as<