diff --git a/asio/include/Makefile.am b/asio/include/Makefile.am index 13c89d47..9ce64f75 100644 --- a/asio/include/Makefile.am +++ b/asio/include/Makefile.am @@ -308,6 +308,7 @@ nobase_include_HEADERS = \ asio/execution/detail/as_invocable.hpp \ asio/execution/detail/as_operation.hpp \ asio/execution/detail/as_receiver.hpp \ + asio/execution/detail/bulk_sender.hpp \ asio/execution/detail/void_receiver.hpp \ asio/execution/execute.hpp \ asio/execution/executor.hpp \ diff --git a/asio/include/asio/detail/config.hpp b/asio/include/asio/detail/config.hpp index 3d8d00e3..ff797f45 100644 --- a/asio/include/asio/detail/config.hpp +++ b/asio/include/asio/detail/config.hpp @@ -148,6 +148,7 @@ # define ASIO_MOVE_CAST(type) static_cast # define ASIO_MOVE_CAST2(type1, type2) static_cast # define ASIO_MOVE_OR_LVALUE(type) static_cast +# define ASIO_MOVE_OR_LVALUE_TYPE(type) type #endif // defined(ASIO_HAS_MOVE) && !defined(ASIO_MOVE_CAST) // If ASIO_MOVE_CAST still isn't defined, default to a C++03-compatible @@ -175,6 +176,7 @@ # define ASIO_MOVE_CAST(type) static_cast # define ASIO_MOVE_CAST2(type1, type2) static_cast # define ASIO_MOVE_OR_LVALUE(type) +# define ASIO_MOVE_OR_LVALUE_TYPE(type) type& #endif // !defined(ASIO_MOVE_CAST) // Support variadic templates on compilers known to allow it. diff --git a/asio/include/asio/execution/bulk_execute.hpp b/asio/include/asio/execution/bulk_execute.hpp index a48e91f6..58fa97d0 100644 --- a/asio/include/asio/execution/bulk_execute.hpp +++ b/asio/include/asio/execution/bulk_execute.hpp @@ -17,6 +17,9 @@ #include "asio/detail/config.hpp" #include "asio/detail/type_traits.hpp" +#include "asio/execution/bulk_guarantee.hpp" +#include "asio/execution/detail/bulk_sender.hpp" +#include "asio/execution/executor.hpp" #include "asio/execution/sender.hpp" #include "asio/traits/bulk_execute_member.hpp" #include "asio/traits/bulk_execute_free.hpp" @@ -98,10 +101,17 @@ namespace asio_execution_bulk_execute_fn { using asio::declval; using asio::enable_if; +using asio::execution::bulk_guarantee_t; +using asio::execution::detail::bulk_sender; +using asio::execution::executor_index; using asio::execution::is_sender; using asio::is_convertible; +using asio::is_same; +using asio::remove_cvref; +using asio::result_of; using asio::traits::bulk_execute_free; using asio::traits::bulk_execute_member; +using asio::traits::static_require; void bulk_execute(); @@ -109,6 +119,7 @@ enum overload_type { call_member, call_free, + adapter, ill_formed }; @@ -158,6 +169,36 @@ struct call_traits +struct call_traits::value + && + !bulk_execute_member::is_valid + && + !bulk_execute_free::is_valid + && + is_sender::value + && + is_same< + typename result_of< + F(typename executor_index::type>::type) + >::type, + typename result_of< + F(typename executor_index::type>::type) + >::type + >::value + && + static_require::is_valid + ) + >::type> +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = adapter); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef bulk_sender result_type; +}; + struct impl { #if defined(ASIO_HAS_MOVE) @@ -186,6 +227,20 @@ struct impl return bulk_execute(ASIO_MOVE_CAST(S)(s), ASIO_MOVE_CAST(F)(f), ASIO_MOVE_CAST(N)(n)); } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(S&& s, F&& f, N&& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type( + ASIO_MOVE_CAST(S)(s), ASIO_MOVE_CAST(F)(f), + ASIO_MOVE_CAST(N)(n)); + } #else // defined(ASIO_HAS_MOVE) template ASIO_CONSTEXPR typename enable_if< @@ -238,6 +293,32 @@ struct impl return bulk_execute(s, ASIO_MOVE_CAST(F)(f), ASIO_MOVE_CAST(N)(n)); } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(S& s, const F& f, const N& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type( + s, ASIO_MOVE_CAST(F)(f), ASIO_MOVE_CAST(N)(n)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(const S& s, const F& f, const N& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type( + s, ASIO_MOVE_CAST(F)(f), ASIO_MOVE_CAST(N)(n)); + } #endif // defined(ASIO_HAS_MOVE) }; diff --git a/asio/include/asio/execution/detail/as_receiver.hpp b/asio/include/asio/execution/detail/as_receiver.hpp index c759e730..2fcaf2f8 100644 --- a/asio/include/asio/execution/detail/as_receiver.hpp +++ b/asio/include/asio/execution/detail/as_receiver.hpp @@ -33,7 +33,7 @@ struct as_receiver Function f_; template - explicit as_receiver(ASIO_MOVE_ARG(F) f) + explicit as_receiver(ASIO_MOVE_ARG(F) f, int) : f_(ASIO_MOVE_CAST(F)(f)) { } diff --git a/asio/include/asio/execution/detail/bulk_sender.hpp b/asio/include/asio/execution/detail/bulk_sender.hpp new file mode 100644 index 00000000..5e2a1e52 --- /dev/null +++ b/asio/include/asio/execution/detail/bulk_sender.hpp @@ -0,0 +1,261 @@ +// +// execution/detail/bulk_sender.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// 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 ASIO_EXECUTION_DETAIL_BULK_SENDER_HPP +#define ASIO_EXECUTION_DETAIL_BULK_SENDER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/connect.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/set_done.hpp" +#include "asio/execution/set_error.hpp" +#include "asio/traits/connect_member.hpp" +#include "asio/traits/set_done_member.hpp" +#include "asio/traits/set_error_member.hpp" +#include "asio/traits/set_value_member.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +template +struct bulk_receiver +{ + typename remove_cvref::type receiver_; + typename decay::type f_; + typename decay::type n_; + + template + explicit bulk_receiver(ASIO_MOVE_ARG(R) r, + ASIO_MOVE_ARG(F) f, ASIO_MOVE_ARG(N) n) + : receiver_(ASIO_MOVE_CAST(R)(r)), + f_(ASIO_MOVE_CAST(F)(f)), + n_(ASIO_MOVE_CAST(N)(n)) + { + } + + void set_value() + { + for (Index i = 0; i < n_; ++i) + f_(i); + + execution::set_value( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(receiver_)); + } + + template + void set_error(ASIO_MOVE_ARG(Error) e) ASIO_NOEXCEPT + { + execution::set_error( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(receiver_), + ASIO_MOVE_CAST(Error)(e)); + } + + void set_done() ASIO_NOEXCEPT + { + execution::set_done( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(receiver_)); + } +}; + +template +struct bulk_receiver_traits +{ + typedef bulk_receiver< + Receiver, Function, Number, + typename execution::executor_index< + typename remove_cvref::type + >::type + > type; + +#if defined(ASIO_HAS_MOVE) + typedef type arg_type; +#else // defined(ASIO_HAS_MOVE) + typedef const type& arg_type; +#endif // defined(ASIO_HAS_MOVE) +}; + +template +struct bulk_sender : sender_base +{ + typename remove_cvref::type sender_; + typename decay::type f_; + typename decay::type n_; + + template + explicit bulk_sender(ASIO_MOVE_ARG(S) s, + ASIO_MOVE_ARG(F) f, ASIO_MOVE_ARG(N) n) + : sender_(ASIO_MOVE_CAST(S)(s)), + f_(ASIO_MOVE_CAST(F)(f)), + n_(ASIO_MOVE_CAST(N)(n)) + { + } + + template + typename connect_result_type< + ASIO_MOVE_OR_LVALUE_TYPE(typename remove_cvref::type), + typename bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::type connect(ASIO_MOVE_ARG(Receiver) r, + typename enable_if< + can_connect< + typename remove_cvref::type, + typename bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::value + >::type* = 0) ASIO_RVALUE_REF_QUAL ASIO_NOEXCEPT + { + return execution::connect( + ASIO_MOVE_OR_LVALUE(typename remove_cvref::type)(sender_), + typename bulk_receiver_traits::type( + ASIO_MOVE_CAST(Receiver)(r), + ASIO_MOVE_CAST(typename decay::type)(f_), + ASIO_MOVE_CAST(typename decay::type)(n_))); + } + + template + typename connect_result_type< + const typename remove_cvref::type&, + typename bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::type connect(ASIO_MOVE_ARG(Receiver) r, + typename enable_if< + can_connect< + const typename remove_cvref::type&, + typename bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::value + >::type* = 0) const ASIO_LVALUE_REF_QUAL ASIO_NOEXCEPT + { + return execution::connect(sender_, + typename bulk_receiver_traits::type( + ASIO_MOVE_CAST(Receiver)(r), f_, n_)); + } +}; + +} // namespace detail +} // namespace execution +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_SET_VALUE_TRAIT) + +template +struct set_value_member< + execution::detail::bulk_receiver, + void()> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_VALUE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_VALUE_TRAIT) + +template +struct set_error_member< + execution::detail::bulk_receiver, + Error> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_VALUE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_DONE_TRAIT) + +template +struct set_done_member< + execution::detail::bulk_receiver > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_DONE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_CONNECT_MEMBER_TRAIT) + +template +struct connect_member< + execution::detail::bulk_sender, + Receiver, + typename enable_if< + execution::can_connect< + ASIO_MOVE_OR_LVALUE_TYPE(typename remove_cvref::type), + typename execution::detail::bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef typename execution::connect_result_type< + ASIO_MOVE_OR_LVALUE_TYPE(typename remove_cvref::type), + typename execution::detail::bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::type result_type; +}; + +template +struct connect_member< + const execution::detail::bulk_sender, + Receiver, + typename enable_if< + execution::can_connect< + const typename remove_cvref::type&, + typename execution::detail::bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef typename execution::connect_result_type< + const typename remove_cvref::type&, + typename execution::detail::bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::type result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_CONNECT_MEMBER_TRAIT) + +} // namespace traits +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_DETAIL_BULK_SENDER_HPP diff --git a/asio/include/asio/execution/execute.hpp b/asio/include/asio/execution/execute.hpp index ada8ae13..277cd18a 100644 --- a/asio/include/asio/execution/execute.hpp +++ b/asio/include/asio/execution/execute.hpp @@ -216,7 +216,7 @@ struct impl return asio::execution::detail::submit_helper( ASIO_MOVE_CAST(T)(t), as_receiver::type, T>( - ASIO_MOVE_CAST(F)(f))); + ASIO_MOVE_CAST(F)(f), 0)); } }; diff --git a/asio/include/asio/execution/invocable_archetype.hpp b/asio/include/asio/execution/invocable_archetype.hpp index 1f35e1ab..9ccbb8ce 100644 --- a/asio/include/asio/execution/invocable_archetype.hpp +++ b/asio/include/asio/execution/invocable_archetype.hpp @@ -28,6 +28,11 @@ namespace execution { /// execution::executor concept. struct invocable_archetype { +#if !defined(GENERATING_DOCUMENTATION) + // Necessary for compatibility with a C++03 implementation of result_of. + typedef void result_type; +#endif // !defined(GENERATING_DOCUMENTATION) + #if defined(ASIO_HAS_VARIADIC_TEMPLATES) \ || defined(GENERATING_DOCUMENTATION) diff --git a/asio/src/tests/unit/execution/bulk_execute.cpp b/asio/src/tests/unit/execution/bulk_execute.cpp index 2e799099..5d362cab 100644 --- a/asio/src/tests/unit/execution/bulk_execute.cpp +++ b/asio/src/tests/unit/execution/bulk_execute.cpp @@ -16,6 +16,7 @@ // Test that header file is self-contained. #include "asio/execution/bulk_execute.hpp" +#include "asio/execution.hpp" #include "../unit_test.hpp" namespace exec = asio::execution; @@ -148,6 +149,68 @@ struct bulk_execute_free } // namespace traits } // namespace asio +struct executor +{ + executor() + { + } + + executor(const executor&) ASIO_NOEXCEPT + { + } + +#if defined(ASIO_HAS_MOVE) + executor(executor&&) ASIO_NOEXCEPT + { + } +#endif // defined(ASIO_HAS_MOVE) + + template + void execute(ASIO_MOVE_ARG(F) f) const ASIO_NOEXCEPT + { + typename asio::decay::type tmp(ASIO_MOVE_CAST(F)(f)); + tmp(); + } + + bool operator==(const executor&) const ASIO_NOEXCEPT + { + return true; + } + + bool operator!=(const executor&) const ASIO_NOEXCEPT + { + return false; + } +}; + +namespace asio { +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + +template +struct execute_member +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) +#if !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +template <> +struct equality_comparable +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); +}; + +#endif // !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +} // namespace traits +} // namespace asio + void test_can_bulk_execute() { ASIO_CONSTEXPR bool b1 = exec::can_bulk_execute< @@ -174,12 +237,30 @@ void test_can_bulk_execute() ASIO_CONSTEXPR bool b6 = exec::can_bulk_execute< const free_bulk_execute&, exec::invocable_archetype, std::size_t>::value; ASIO_CHECK(b6 == true); + + ASIO_CONSTEXPR bool b7 = exec::can_bulk_execute< + executor&, exec::invocable_archetype, std::size_t>::value; + ASIO_CHECK(b7 == true); + + ASIO_CONSTEXPR bool b8 = exec::can_bulk_execute< + const executor&, exec::invocable_archetype, std::size_t>::value; + ASIO_CHECK(b8 == true); } void handler(std::size_t) { } +void counting_handler(std::size_t) +{ + ++call_count; +} + +void completion_handler() +{ + ++call_count; +} + void test_bulk_execute() { call_count = 0; @@ -209,6 +290,26 @@ void test_bulk_execute() call_count = 0; exec::bulk_execute(free_bulk_execute(), handler, 2); ASIO_CHECK(call_count == 1); + + call_count = 0; + executor ex5; + exec::execute( + exec::bulk_execute(ex5, counting_handler, 10u), + completion_handler); + ASIO_CHECK(call_count == 11); + + call_count = 0; + const executor ex6; + exec::execute( + exec::bulk_execute(ex6, counting_handler, 10u), + completion_handler); + ASIO_CHECK(call_count == 11); + + call_count = 0; + exec::execute( + exec::bulk_execute(executor(), counting_handler, 10u), + completion_handler); + ASIO_CHECK(call_count == 11); } ASIO_TEST_SUITE