Add experimental::as_single completion token adapter.

The as_single completion token adapter can be used to specify that the
completion handler arguments should be combined into a single argument.
For completion signatures with a single parameter, the argument is
passed through as-is. For signatures with two or more parameters, the
arguments are combined into a tuple.

The as_single adapter may be used in conjunction with use_awaitable and
structured bindings as follows:

    auto [e, n] = co_await socket.async_read_some(
        asio::buffer(data), as_single(use_awaitable));

Alternatively, it may be used as a default completion token like so:

    using default_token = as_single_t<use_awaitable_t<>>;
    using tcp_socket = default_token::as_default_on_t<tcp::socket>;
    // ...
    awaitable<void> do_read(tcp_socket socket)
    {
      // ...
      auto [e, n] = co_await socket.async_read_some(asio::buffer(data));
      // ...
    }
This commit is contained in:
Christopher Kohlhoff 2020-05-08 22:19:02 +10:00
parent ed5b117546
commit b8e9a44868
9 changed files with 812 additions and 0 deletions

View File

@ -334,6 +334,8 @@ nobase_include_HEADERS = \
asio/execution/submit.hpp \
asio/executor.hpp \
asio/executor_work_guard.hpp \
asio/experimental/as_single.hpp \
asio/experimental/impl/as_single.hpp \
asio/generic/basic_endpoint.hpp \
asio/generic/datagram_protocol.hpp \
asio/generic/detail/endpoint.hpp \

View File

@ -0,0 +1,135 @@
//
// 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 ASIO_EXPERIMENTAL_AS_SINGLE_HPP
#define ASIO_EXPERIMENTAL_AS_SINGLE_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/detail/push_options.hpp"
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 <typename CompletionToken>
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.
*/
ASIO_CONSTEXPR as_single_t(
default_constructor_tag = default_constructor_tag(),
CompletionToken token = CompletionToken())
: token_(ASIO_MOVE_CAST(CompletionToken)(token))
{
}
/// Constructor.
template <typename T>
ASIO_CONSTEXPR explicit as_single_t(
ASIO_MOVE_ARG(T) completion_token)
: token_(ASIO_MOVE_CAST(T)(completion_token))
{
}
/// Adapts an executor to add the @c as_single_t completion token as the
/// default.
template <typename InnerExecutor>
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) ASIO_NOEXCEPT
: InnerExecutor(ex)
{
}
/// Convert the specified executor to the inner executor type, then use
/// that to construct the adapted executor.
template <typename OtherExecutor>
executor_with_default(const OtherExecutor& ex,
typename enable_if<
is_convertible<OtherExecutor, InnerExecutor>::value
>::type* = 0) 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(ASIO_HAS_ALIAS_TEMPLATES) \
|| defined(GENERATING_DOCUMENTATION)
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type> >::other;
#endif // defined(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 <typename T>
static typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other
as_default_on(ASIO_MOVE_ARG(T) object)
{
return typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other(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 <typename CompletionToken>
inline ASIO_CONSTEXPR as_single_t<typename decay<CompletionToken>::type>
as_single(ASIO_MOVE_ARG(CompletionToken) completion_token)
{
return as_single_t<typename decay<CompletionToken>::type>(
ASIO_MOVE_CAST(CompletionToken)(completion_token));
}
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/experimental/impl/as_single.hpp"
#endif // ASIO_EXPERIMENTAL_AS_SINGLE_HPP

View File

@ -0,0 +1,225 @@
//
// 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 ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP
#define ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <tuple>
#include "asio/associated_executor.hpp"
#include "asio/associated_allocator.hpp"
#include "asio/async_result.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_cont_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/variadic_templates.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
// Class to adapt a as_single_t as a completion handler.
template <typename Handler>
class as_single_handler
{
public:
typedef void result_type;
template <typename CompletionToken>
as_single_handler(as_single_t<CompletionToken> e)
: handler_(ASIO_MOVE_CAST(CompletionToken)(e.token_))
{
}
template <typename RedirectedHandler>
as_single_handler(ASIO_MOVE_ARG(RedirectedHandler) h)
: handler_(ASIO_MOVE_CAST(RedirectedHandler)(h))
{
}
void operator()()
{
handler_();
}
template <typename Arg>
void operator()(ASIO_MOVE_ARG(Arg) arg)
{
handler_(ASIO_MOVE_CAST(Arg)(arg));
}
template <typename... Args>
void operator()(ASIO_MOVE_ARG(Args)... args)
{
handler_(std::make_tuple(ASIO_MOVE_CAST(Args)(args)...));
}
//private:
Handler handler_;
};
template <typename Handler>
inline void* asio_handler_allocate(std::size_t size,
as_single_handler<Handler>* this_handler)
{
return asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
}
template <typename Handler>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
as_single_handler<Handler>* this_handler)
{
asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
}
template <typename Handler>
inline bool asio_handler_is_continuation(
as_single_handler<Handler>* this_handler)
{
return asio_handler_cont_helpers::is_continuation(
this_handler->handler_);
}
template <typename Function, typename Handler>
inline void asio_handler_invoke(Function& function,
as_single_handler<Handler>* this_handler)
{
asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
}
template <typename Function, typename Handler>
inline void asio_handler_invoke(const Function& function,
as_single_handler<Handler>* this_handler)
{
asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
}
template <typename Signature>
struct as_single_signature
{
typedef Signature type;
};
template <typename R>
struct as_single_signature<R()>
{
typedef R type();
};
template <typename R, typename Arg>
struct as_single_signature<R(Arg)>
{
typedef R type(Arg);
};
template <typename R, typename... Args>
struct as_single_signature<R(Args...)>
{
typedef R type(std::tuple<typename decay<Args>::type...>);
};
} // namespace detail
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken, typename Signature>
struct async_result<experimental::as_single_t<CompletionToken>, Signature>
{
typedef typename async_result<CompletionToken,
typename experimental::detail::as_single_signature<Signature>::type>
::return_type return_type;
template <typename Initiation>
struct init_wrapper
{
init_wrapper(Initiation init)
: initiation_(ASIO_MOVE_CAST(Initiation)(init))
{
}
template <typename Handler, typename... Args>
void operator()(
ASIO_MOVE_ARG(Handler) handler,
ASIO_MOVE_ARG(Args)... args)
{
ASIO_MOVE_CAST(Initiation)(initiation_)(
experimental::detail::as_single_handler<
typename decay<Handler>::type>(
ASIO_MOVE_CAST(Handler)(handler)),
ASIO_MOVE_CAST(Args)(args)...);
}
Initiation initiation_;
};
template <typename Initiation, typename RawCompletionToken, typename... Args>
static return_type initiate(
ASIO_MOVE_ARG(Initiation) initiation,
ASIO_MOVE_ARG(RawCompletionToken) token,
ASIO_MOVE_ARG(Args)... args)
{
return async_initiate<CompletionToken,
typename experimental::detail::as_single_signature<Signature>::type>(
init_wrapper<typename decay<Initiation>::type>(
ASIO_MOVE_CAST(Initiation)(initiation)),
token.token_, ASIO_MOVE_CAST(Args)(args)...);
}
};
template <typename Handler, typename Executor>
struct associated_executor<
experimental::detail::as_single_handler<Handler>, Executor>
: detail::associated_executor_forwarding_base<Handler, Executor>
{
typedef typename associated_executor<Handler, Executor>::type type;
static type get(
const experimental::detail::as_single_handler<Handler>& h,
const Executor& ex = Executor()) ASIO_NOEXCEPT
{
return associated_executor<Handler, Executor>::get(h.handler_, ex);
}
};
template <typename Handler, typename Allocator>
struct associated_allocator<
experimental::detail::as_single_handler<Handler>, Allocator>
{
typedef typename associated_allocator<Handler, Allocator>::type type;
static type get(
const experimental::detail::as_single_handler<Handler>& h,
const Allocator& a = Allocator()) ASIO_NOEXCEPT
{
return associated_allocator<Handler, Allocator>::get(h.handler_, a);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP

View File

@ -253,6 +253,7 @@
<member><link linkend="asio.reference.basic_yield_context">basic_yield_context</link></member>
<member><link linkend="asio.reference.executor_binder">executor_binder</link></member>
<member><link linkend="asio.reference.executor_work_guard">executor_work_guard</link></member>
<member><link linkend="asio.reference.experimental__as_single_t">experimental::as_single_t</link></member>
<member><link linkend="asio.reference.io_context__basic_executor_type">io_context::basic_executor_type</link></member>
<member><link linkend="asio.reference.redirect_error_t">redirect_error_t</link></member>
<member><link linkend="asio.reference.strand">strand</link></member>
@ -264,6 +265,7 @@
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.detached">detached</link></member>
<member><link linkend="asio.reference.executor_arg">executor_arg</link></member>
<member><link linkend="asio.reference.experimental__as_single">experimental::as_single</link></member>
<member><link linkend="asio.reference.this_coro__executor">this_coro::executor</link></member>
<member><link linkend="asio.reference.use_future">use_future</link></member>
<member><link linkend="asio.reference.use_awaitable">use_awaitable</link></member>

View File

@ -82,6 +82,7 @@ INPUT = ./../../include/asio.hpp \
./../../include/asio/ssl \
./../../include/asio/windows \
./../../include/asio/execution \
./../../include/asio/experimental \
./noncopyable_dox.txt \
./std_exception_dox.txt
FILE_PATTERNS =

View File

@ -74366,6 +74366,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 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: ][^asio/experimental/as_single.hpp]
['Convenience header: ][^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 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 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 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 asio.reference.experimental__as_single_t.as_single_t [*as_single_t]] [constructor]]
[Default constructor.
[hr]
Constructor. ]
]
]
[heading Data Members]
[table
[[Name][Description]]
[
[[link asio.reference.experimental__as_single_t.token_ [*token_]]]
[]
]
]
The [link 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: ][^asio/experimental/as_single.hpp]
['Convenience header: ][^asio.hpp]
[section:as_default_on experimental::as_single_t::as_default_on]
[indexterm2 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 asio.indexterm.experimental__as_single_t.as_single_t..as_single_t..experimental::as_single_t]
Default constructor.
constexpr ``[link asio.reference.experimental__as_single_t.as_single_t.overload1 as_single_t]``(
default_constructor_tag = default_constructor_tag(),
CompletionToken token = CompletionToken());
`` [''''&raquo;''' [link asio.reference.experimental__as_single_t.as_single_t.overload1 more...]]``
Constructor.
template<
typename T>
explicit constexpr ``[link asio.reference.experimental__as_single_t.as_single_t.overload2 as_single_t]``(
T && completion_token);
`` [''''&raquo;''' [link 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 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: ][^asio/experimental/as_single.hpp]
['Convenience header: ][^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 asio.reference.Executor1 InnerExecutor]``>
struct executor_with_default
[heading Types]
[table
[[Name][Description]]
[
[[link 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 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: ][^asio/experimental/as_single.hpp]
['Convenience header: ][^asio.hpp]
[section:default_completion_token_type experimental::as_single_t::executor_with_default::default_completion_token_type]
[indexterm2 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 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 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 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 asio.reference.experimental__as_single_t.as_single_t [*as_single_t]] [constructor]]
[Default constructor.
[hr]
Constructor. ]
]
]
[heading Data Members]
[table
[[Name][Description]]
[
[[link asio.reference.experimental__as_single_t.token_ [*token_]]]
[]
]
]
The [link 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: ][^asio/experimental/as_single.hpp]
['Convenience header: ][^asio.hpp]
[endsect]
[section:executor_with_default experimental::as_single_t::executor_with_default::executor_with_default]
[indexterm2 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 asio.reference.experimental__as_single_t__executor_with_default.executor_with_default.overload1 executor_with_default]``(
const InnerExecutor & ex);
`` [''''&raquo;''' [link 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 asio.reference.Executor1 OtherExecutor]``>
``[link 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);
`` [''''&raquo;''' [link 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 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]

View File

@ -14,6 +14,7 @@ noinst_PROGRAMS = \
coroutines_ts/chat_server \
coroutines_ts/echo_server \
coroutines_ts/echo_server_with_default \
coroutines_ts/echo_server_with_as_single_default \
coroutines_ts/refactored_echo_server
endif
@ -23,6 +24,7 @@ if HAVE_COROUTINES
coroutines_ts_chat_server_SOURCES = coroutines_ts/chat_server.cpp
coroutines_ts_echo_server_SOURCES = coroutines_ts/echo_server.cpp
coroutines_ts_echo_server_with_default_SOURCES = coroutines_ts/echo_server_with_default.cpp
coroutines_ts_echo_server_with_as_single_default_SOURCES = coroutines_ts/echo_server_with_as_single_default.cpp
coroutines_ts_refactored_echo_server_SOURCES = coroutines_ts/refactored_echo_server.cpp
endif

View File

@ -9,3 +9,4 @@
*.tds
*_server
*_server_with_default
*_server_with_as_single_default

View File

@ -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 <asio/experimental/as_single.hpp>
#include <asio/co_spawn.hpp>
#include <asio/detached.hpp>
#include <asio/io_context.hpp>
#include <asio/ip/tcp.hpp>
#include <asio/signal_set.hpp>
#include <asio/write.hpp>
#include <cstdio>
using asio::experimental::as_single_t;
using asio::ip::tcp;
using asio::awaitable;
using asio::co_spawn;
using asio::detached;
using asio::use_awaitable_t;
using default_token = as_single_t<use_awaitable_t<>>;
using tcp_acceptor = default_token::as_default_on_t<tcp::acceptor>;
using tcp_socket = default_token::as_default_on_t<tcp::socket>;
namespace this_coro = asio::this_coro;
awaitable<void> echo(tcp_socket socket)
{
char data[1024];
for (;;)
{
auto [e1, nread] = co_await socket.async_read_some(asio::buffer(data));
if (nread == 0) break;
auto [e2, nwritten] = co_await async_write(socket, asio::buffer(data, nread));
if (nwritten != nread) break;
}
}
awaitable<void> 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
{
asio::io_context io_context(1);
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());
}
}