diff --git a/doc/quickref.xml b/doc/quickref.xml index 0727be6e..323adf77 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -250,6 +250,7 @@ basic_yield_context executor_binder executor_work_guard + experimental::as_single_t io_context::basic_executor_type redirect_error_t strand @@ -261,6 +262,7 @@ detached executor_arg + experimental::as_single this_coro::executor use_future use_awaitable diff --git a/doc/reference.dox b/doc/reference.dox index 23d69279..d88513c2 100644 --- a/doc/reference.dox +++ b/doc/reference.dox @@ -82,6 +82,7 @@ INPUT = ./../../../boost/asio.hpp \ ./../../../boost/asio/ssl \ ./../../../boost/asio/windows \ ./../../../boost/asio/execution \ + ./../../../boost/asio/experimental \ ./noncopyable_dox.txt \ ./std_exception_dox.txt FILE_PATTERNS = diff --git a/doc/reference.qbk b/doc/reference.qbk index 08fb4f35..e033bd0f 100644 --- a/doc/reference.qbk +++ b/doc/reference.qbk @@ -73865,6 +73865,379 @@ Unless the object has already been reset, or is in a moved-from state, calls `on +[endsect] + + +[section:experimental__as_single experimental::as_single] + +[indexterm1 boost_asio.indexterm.experimental__as_single..experimental::as_single] +Create a completion token to specify that the completion handler arguments should be combined into a single argument. + + + template< + typename CompletionToken> + constexpr as_single_t< typename decay< CompletionToken >::type > as_single( + CompletionToken && completion_token); + + +[heading Requirements] + +['Header: ][^boost/asio/experimental/as_single.hpp] + +['Convenience header: ][^boost/asio.hpp] + + +[endsect] + + +[section:experimental__as_single_t experimental::as_single_t] + + +Completion token type used to specify that the completion handler arguments should be combined into a single argument. + + + template< + typename CompletionToken> + class as_single_t + + +[heading Types] +[table + [[Name][Description]] + + [ + + [[link boost_asio.reference.experimental__as_single_t__default_constructor_tag [*default_constructor_tag]]] + [Tag type used to prevent the "default" constructor from being used for conversions. ] + + ] + + [ + + [[link boost_asio.reference.experimental__as_single_t__executor_with_default [*executor_with_default]]] + [Adapts an executor to add the as_single_t completion token as the default. ] + + ] + +] + +[heading Member Functions] +[table + [[Name][Description]] + + [ + [[link boost_asio.reference.experimental__as_single_t.as_default_on [*as_default_on]] [static]] + [Function helper to adapt an I/O object to use as_single_t as its default completion token type. ] + ] + + [ + [[link boost_asio.reference.experimental__as_single_t.as_single_t [*as_single_t]] [constructor]] + [Default constructor. + [hr] + Constructor. ] + ] + +] + +[heading Data Members] +[table + [[Name][Description]] + + [ + [[link boost_asio.reference.experimental__as_single_t.token_ [*token_]]] + [] + ] + +] + +The [link boost_asio.reference.experimental__as_single_t `experimental::as_single_t`] class is used to indicate that any arguments to the completion handler should be combined and passed as a single argument. If there is already one argument, that argument is passed as-is. If there is more than argument, the arguments are first moved into a `std::tuple` and that tuple is then passed to the completion handler. + +[heading Requirements] + +['Header: ][^boost/asio/experimental/as_single.hpp] + +['Convenience header: ][^boost/asio.hpp] + + +[section:as_default_on experimental::as_single_t::as_default_on] + +[indexterm2 boost_asio.indexterm.experimental__as_single_t.as_default_on..as_default_on..experimental::as_single_t] +Function helper to adapt an I/O object to use `as_single_t` as its default completion token type. + + + template< + typename T> + static decay< T >::type::template rebind_executor< executor_with_default< typename decay< T >::type::executor_type > >::other as_default_on( + T && object); + + + +[endsect] + + +[section:as_single_t experimental::as_single_t::as_single_t] + +[indexterm2 boost_asio.indexterm.experimental__as_single_t.as_single_t..as_single_t..experimental::as_single_t] +Default constructor. + + + constexpr ``[link boost_asio.reference.experimental__as_single_t.as_single_t.overload1 as_single_t]``( + default_constructor_tag = default_constructor_tag(), + CompletionToken token = CompletionToken()); + `` [''''»''' [link boost_asio.reference.experimental__as_single_t.as_single_t.overload1 more...]]`` + + +Constructor. + + + template< + typename T> + explicit constexpr ``[link boost_asio.reference.experimental__as_single_t.as_single_t.overload2 as_single_t]``( + T && completion_token); + `` [''''»''' [link boost_asio.reference.experimental__as_single_t.as_single_t.overload2 more...]]`` + + +[section:overload1 experimental::as_single_t::as_single_t (1 of 2 overloads)] + + +Default constructor. + + + constexpr as_single_t( + default_constructor_tag = default_constructor_tag(), + CompletionToken token = CompletionToken()); + + +This constructor is only valid if the underlying completion token is default constructible and move constructible. The underlying completion token is itself defaulted as an argument to allow it to capture a source location. + + +[endsect] + + + +[section:overload2 experimental::as_single_t::as_single_t (2 of 2 overloads)] + + +Constructor. + + + template< + typename T> + constexpr as_single_t( + T && completion_token); + + + +[endsect] + + +[endsect] + + +[section:token_ experimental::as_single_t::token_] + +[indexterm2 boost_asio.indexterm.experimental__as_single_t.token_..token_..experimental::as_single_t] + + CompletionToken token_; + + + +[endsect] + + + +[endsect] + +[section:experimental__as_single_t__default_constructor_tag experimental::as_single_t::default_constructor_tag] + + +Tag type used to prevent the "default" constructor from being used for conversions. + + + struct default_constructor_tag + + +[heading Requirements] + +['Header: ][^boost/asio/experimental/as_single.hpp] + +['Convenience header: ][^boost/asio.hpp] + + +[endsect] + +[section:experimental__as_single_t__executor_with_default experimental::as_single_t::executor_with_default] + + +Adapts an executor to add the `as_single_t` completion token as the default. + + + template< + typename ``[link boost_asio.reference.Executor1 InnerExecutor]``> + struct executor_with_default + + +[heading Types] +[table + [[Name][Description]] + + [ + + [[link boost_asio.reference.experimental__as_single_t__executor_with_default.default_completion_token_type [*default_completion_token_type]]] + [Specify as_single_t as the default completion token type. ] + + ] + +] + +[heading Member Functions] +[table + [[Name][Description]] + + [ + [[link boost_asio.reference.experimental__as_single_t__executor_with_default.executor_with_default [*executor_with_default]] [constructor]] + [Construct the adapted executor from the inner executor type. + [hr] + Convert the specified executor to the inner executor type, then use that to construct the adapted executor. ] + ] + +] + +[heading Requirements] + +['Header: ][^boost/asio/experimental/as_single.hpp] + +['Convenience header: ][^boost/asio.hpp] + + +[section:default_completion_token_type experimental::as_single_t::executor_with_default::default_completion_token_type] + +[indexterm2 boost_asio.indexterm.experimental__as_single_t__executor_with_default.default_completion_token_type..default_completion_token_type..experimental::as_single_t::executor_with_default] +Specify `as_single_t` as the default completion token type. + + + typedef as_single_t default_completion_token_type; + + +[heading Types] +[table + [[Name][Description]] + + [ + + [[link boost_asio.reference.experimental__as_single_t__default_constructor_tag [*default_constructor_tag]]] + [Tag type used to prevent the "default" constructor from being used for conversions. ] + + ] + + [ + + [[link boost_asio.reference.experimental__as_single_t__executor_with_default [*executor_with_default]]] + [Adapts an executor to add the as_single_t completion token as the default. ] + + ] + +] + +[heading Member Functions] +[table + [[Name][Description]] + + [ + [[link boost_asio.reference.experimental__as_single_t.as_default_on [*as_default_on]] [static]] + [Function helper to adapt an I/O object to use as_single_t as its default completion token type. ] + ] + + [ + [[link boost_asio.reference.experimental__as_single_t.as_single_t [*as_single_t]] [constructor]] + [Default constructor. + [hr] + Constructor. ] + ] + +] + +[heading Data Members] +[table + [[Name][Description]] + + [ + [[link boost_asio.reference.experimental__as_single_t.token_ [*token_]]] + [] + ] + +] + +The [link boost_asio.reference.experimental__as_single_t `experimental::as_single_t`] class is used to indicate that any arguments to the completion handler should be combined and passed as a single argument. If there is already one argument, that argument is passed as-is. If there is more than argument, the arguments are first moved into a `std::tuple` and that tuple is then passed to the completion handler. + + +[heading Requirements] + +['Header: ][^boost/asio/experimental/as_single.hpp] + +['Convenience header: ][^boost/asio.hpp] + + +[endsect] + + +[section:executor_with_default experimental::as_single_t::executor_with_default::executor_with_default] + +[indexterm2 boost_asio.indexterm.experimental__as_single_t__executor_with_default.executor_with_default..executor_with_default..experimental::as_single_t::executor_with_default] +Construct the adapted executor from the inner executor type. + + + ``[link boost_asio.reference.experimental__as_single_t__executor_with_default.executor_with_default.overload1 executor_with_default]``( + const InnerExecutor & ex); + `` [''''»''' [link boost_asio.reference.experimental__as_single_t__executor_with_default.executor_with_default.overload1 more...]]`` + + +Convert the specified executor to the inner executor type, then use that to construct the adapted executor. + + + template< + typename ``[link boost_asio.reference.Executor1 OtherExecutor]``> + ``[link boost_asio.reference.experimental__as_single_t__executor_with_default.executor_with_default.overload2 executor_with_default]``( + const OtherExecutor & ex, + typename enable_if< is_convertible< OtherExecutor, InnerExecutor >::value >::type * = 0); + `` [''''»''' [link boost_asio.reference.experimental__as_single_t__executor_with_default.executor_with_default.overload2 more...]]`` + + +[section:overload1 experimental::as_single_t::executor_with_default::executor_with_default (1 of 2 overloads)] + + +Construct the adapted executor from the inner executor type. + + + executor_with_default( + const InnerExecutor & ex); + + + +[endsect] + + + +[section:overload2 experimental::as_single_t::executor_with_default::executor_with_default (2 of 2 overloads)] + + +Convert the specified executor to the inner executor type, then use that to construct the adapted executor. + + + template< + typename ``[link boost_asio.reference.Executor1 OtherExecutor]``> + executor_with_default( + const OtherExecutor & ex, + typename enable_if< is_convertible< OtherExecutor, InnerExecutor >::value >::type * = 0); + + + +[endsect] + + +[endsect] + + [endsect] [section:generic__basic_endpoint generic::basic_endpoint] diff --git a/example/cpp17/coroutines_ts/echo_server_with_as_single_default.cpp b/example/cpp17/coroutines_ts/echo_server_with_as_single_default.cpp new file mode 100644 index 00000000..8515644e --- /dev/null +++ b/example/cpp17/coroutines_ts/echo_server_with_as_single_default.cpp @@ -0,0 +1,71 @@ +// +// echo_server_with_as_single_default.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// 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) +// + +#include +#include +#include +#include +#include +#include +#include +#include + +using boost::asio::experimental::as_single_t; +using boost::asio::ip::tcp; +using boost::asio::awaitable; +using boost::asio::co_spawn; +using boost::asio::detached; +using boost::asio::use_awaitable_t; +using default_token = as_single_t>; +using tcp_acceptor = default_token::as_default_on_t; +using tcp_socket = default_token::as_default_on_t; +namespace this_coro = boost::asio::this_coro; + +awaitable echo(tcp_socket socket) +{ + char data[1024]; + for (;;) + { + auto [e1, nread] = co_await socket.async_read_some(boost::asio::buffer(data)); + if (nread == 0) break; + auto [e2, nwritten] = co_await async_write(socket, boost::asio::buffer(data, nread)); + if (nwritten != nread) break; + } +} + +awaitable listener() +{ + auto executor = co_await this_coro::executor; + tcp_acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + if (auto [e, socket] = co_await acceptor.async_accept(); socket.is_open()) + co_spawn(executor, echo(std::move(socket)), detached); + } +} + +int main() +{ + try + { + boost::asio::io_context io_context(1); + + boost::asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener(), detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/include/boost/asio/experimental/as_single.hpp b/include/boost/asio/experimental/as_single.hpp new file mode 100644 index 00000000..8d6f779f --- /dev/null +++ b/include/boost/asio/experimental/as_single.hpp @@ -0,0 +1,137 @@ +// +// experimental/as_single.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 BOOST_ASIO_EXPERIMENTAL_AS_SINGLE_HPP +#define BOOST_ASIO_EXPERIMENTAL_AS_SINGLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include + +#include + +namespace boost { +namespace asio { +namespace experimental { + +/// Completion token type used to specify that the completion handler +/// arguments should be combined into a single argument. +/** + * The as_single_t class is used to indicate that any arguments to the + * completion handler should be combined and passed as a single argument. + * If there is already one argument, that argument is passed as-is. If + * there is more than argument, the arguments are first moved into a + * @c std::tuple and that tuple is then passed to the completion handler. + */ +template +class as_single_t +{ +public: + /// Tag type used to prevent the "default" constructor from being used for + /// conversions. + struct default_constructor_tag {}; + + /// Default constructor. + /** + * This constructor is only valid if the underlying completion token is + * default constructible and move constructible. The underlying completion + * token is itself defaulted as an argument to allow it to capture a source + * location. + */ + BOOST_ASIO_CONSTEXPR as_single_t( + default_constructor_tag = default_constructor_tag(), + CompletionToken token = CompletionToken()) + : token_(BOOST_ASIO_MOVE_CAST(CompletionToken)(token)) + { + } + + /// Constructor. + template + BOOST_ASIO_CONSTEXPR explicit as_single_t( + BOOST_ASIO_MOVE_ARG(T) completion_token) + : token_(BOOST_ASIO_MOVE_CAST(T)(completion_token)) + { + } + + /// Adapts an executor to add the @c as_single_t completion token as the + /// default. + template + struct executor_with_default : InnerExecutor + { + /// Specify @c as_single_t as the default completion token type. + typedef as_single_t default_completion_token_type; + + /// Construct the adapted executor from the inner executor type. + executor_with_default(const InnerExecutor& ex) BOOST_ASIO_NOEXCEPT + : InnerExecutor(ex) + { + } + + /// Convert the specified executor to the inner executor type, then use + /// that to construct the adapted executor. + template + executor_with_default(const OtherExecutor& ex, + typename enable_if< + is_convertible::value + >::type* = 0) BOOST_ASIO_NOEXCEPT + : InnerExecutor(ex) + { + } + }; + + /// Type alias to adapt an I/O object to use @c as_single_t as its + /// default completion token type. +#if defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES) \ + || defined(GENERATING_DOCUMENTATION) + template + using as_default_on_t = typename T::template rebind_executor< + executor_with_default >::other; +#endif // defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + + /// Function helper to adapt an I/O object to use @c as_single_t as its + /// default completion token type. + template + static typename decay::type::template rebind_executor< + executor_with_default::type::executor_type> + >::other + as_default_on(BOOST_ASIO_MOVE_ARG(T) object) + { + return typename decay::type::template rebind_executor< + executor_with_default::type::executor_type> + >::other(BOOST_ASIO_MOVE_CAST(T)(object)); + } + +//private: + CompletionToken token_; +}; + +/// Create a completion token to specify that the completion handler arguments +/// should be combined into a single argument. +template +inline BOOST_ASIO_CONSTEXPR as_single_t::type> +as_single(BOOST_ASIO_MOVE_ARG(CompletionToken) completion_token) +{ + return as_single_t::type>( + BOOST_ASIO_MOVE_CAST(CompletionToken)(completion_token)); +} + +} // namespace experimental +} // namespace asio +} // namespace boost + +#include + +#include + +#endif // BOOST_ASIO_EXPERIMENTAL_AS_SINGLE_HPP diff --git a/include/boost/asio/experimental/impl/as_single.hpp b/include/boost/asio/experimental/impl/as_single.hpp new file mode 100644 index 00000000..7d61095d --- /dev/null +++ b/include/boost/asio/experimental/impl/as_single.hpp @@ -0,0 +1,227 @@ +// +// experimental/impl/as_single.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 BOOST_ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP +#define BOOST_ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace experimental { +namespace detail { + +// Class to adapt a as_single_t as a completion handler. +template +class as_single_handler +{ +public: + typedef void result_type; + + template + as_single_handler(as_single_t e) + : handler_(BOOST_ASIO_MOVE_CAST(CompletionToken)(e.token_)) + { + } + + template + as_single_handler(BOOST_ASIO_MOVE_ARG(RedirectedHandler) h) + : handler_(BOOST_ASIO_MOVE_CAST(RedirectedHandler)(h)) + { + } + + void operator()() + { + handler_(); + } + + template + void operator()(BOOST_ASIO_MOVE_ARG(Arg) arg) + { + handler_(BOOST_ASIO_MOVE_CAST(Arg)(arg)); + } + + template + void operator()(BOOST_ASIO_MOVE_ARG(Args)... args) + { + handler_(std::make_tuple(BOOST_ASIO_MOVE_CAST(Args)(args)...)); + } + +//private: + Handler handler_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + as_single_handler* this_handler) +{ + return boost_asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + as_single_handler* this_handler) +{ + boost_asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template +inline bool asio_handler_is_continuation( + as_single_handler* this_handler) +{ + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline void asio_handler_invoke(Function& function, + as_single_handler* this_handler) +{ + boost_asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + as_single_handler* this_handler) +{ + boost_asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template +struct as_single_signature +{ + typedef Signature type; +}; + +template +struct as_single_signature +{ + typedef R type(); +}; + +template +struct as_single_signature +{ + typedef R type(Arg); +}; + +template +struct as_single_signature +{ + typedef R type(std::tuple::type...>); +}; + +} // namespace detail +} // namespace experimental + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct async_result, Signature> +{ + typedef typename async_result::type> + ::return_type return_type; + + template + struct init_wrapper + { + init_wrapper(Initiation init) + : initiation_(BOOST_ASIO_MOVE_CAST(Initiation)(init)) + { + } + + template + void operator()( + BOOST_ASIO_MOVE_ARG(Handler) handler, + BOOST_ASIO_MOVE_ARG(Args)... args) + { + BOOST_ASIO_MOVE_CAST(Initiation)(initiation_)( + experimental::detail::as_single_handler< + typename decay::type>( + BOOST_ASIO_MOVE_CAST(Handler)(handler)), + BOOST_ASIO_MOVE_CAST(Args)(args)...); + } + + Initiation initiation_; + }; + + template + static return_type initiate( + BOOST_ASIO_MOVE_ARG(Initiation) initiation, + BOOST_ASIO_MOVE_ARG(RawCompletionToken) token, + BOOST_ASIO_MOVE_ARG(Args)... args) + { + return async_initiate::type>( + init_wrapper::type>( + BOOST_ASIO_MOVE_CAST(Initiation)(initiation)), + token.token_, BOOST_ASIO_MOVE_CAST(Args)(args)...); + } +}; + +template +struct associated_executor< + experimental::detail::as_single_handler, Executor> + : detail::associated_executor_forwarding_base +{ + typedef typename associated_executor::type type; + + static type get( + const experimental::detail::as_single_handler& h, + const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +template +struct associated_allocator< + experimental::detail::as_single_handler, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get( + const experimental::detail::as_single_handler& h, + const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP