asio C++ library

PrevUpHomeNext

Completion Token Adapters

A completion token adapter is a utility that can be generically applied to a completion token, to produce a new completion token with modified behaviour. Common uses of completion token adapters include:

Asio includes a number of completion token adapters as described below.

bind_executor, bind_allocator, bind_cancellation_slot, and bind_immediate_executor

The bind_executor function adapts a completion token to imbue the completion handler with an associated executor.

This example shows the bind_executor adapter applied to a lambda, to specify that the handler should execute in the specified strand. The arguments to the completion handler are passed through as-is.

my_socket.async_read_some(my_buffer,
    asio::bind_executor(my_strand,
      [](asio::error_code error, std::size_t bytes_transferred)
      {
        // ...
      }));

When applied to completion tokens that cause the initiating function to produce a result, such as use_awaitable, the result is returned unmodified.

asio::awaitable<void> my_coroutine()
{
  // ...
  std::size_t bytes_transferred =
    co_await my_socket.async_read_some(my_buffer,
      asio::bind_executor(my_strand, asio::use_awaitable));
  // ...
}

The bind_allocator, bind_cancellation_slot, and bind_immediate_executor adapters work similarly, to imbue the completion handler with an associated allocator, associated cancellation slot, and associated immediate executor respectively.

redirect_error

The redirect_error function adapts a completion token to capture the error_code produced by an operation into a specified variable. In doing so, it modifies the completion signature to remove the initial error_code parameter.

This example shows the redirect_error adapter applied to a lambda, to specify that the error should be captured into my_error. The error_code is no longer passed to the completion handler, but the remaining arguments are passed through as-is.

asio::error_code my_error; // N.B. must be valid until operation completes
// ...
my_socket.async_read_some(my_buffer,
    asio::redirect_error(
      [](std::size_t bytes_transferred)
      {
        // ...
      }, my_error));

When applied to completion tokens that cause the initiating function to produce a result, such as use_awaitable, the result is returned unmodified. However, if the operation fails, the co_await expression will no longer throw an exception on resumption.

asio::awaitable<void> my_coroutine()
{
  // ...
  asio::error_code my_error;
  std::size_t bytes_transferred =
    co_await my_socket.async_read_some(my_buffer,
      asio::redirect_error(asio::use_awaitable, my_error));
  // ...
}
as_tuple

The as_tuple adapter can be used to specify that the completion handler arguments should be combined into a single tuple argument.

For example, the as_tuple adapter may be used in conjunction with use_awaitable and structured bindings as follows:

auto [e, n] =
  co_await my_socket.async_read_some(my_buffer,
    asio::as_tuple(asio::use_awaitable));

This adapter may also be used as a default completion token:

using default_token = asio::as_tuple_t<asio::use_awaitable_t<>>;
using tcp_socket = default_token::as_default_on_t<tcp::socket>;
// ...
asio::awaitable<void> do_read(tcp_socket my_socket)
{
  // ...
  auto [e, n] = co_await my_socket.async_read_some(my_buffer);
  // ...
}
as_single
[Note] Note

This is an experimental feature.

The experimental::as_single 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.

For example, when applied to a timer wait operation, the single error_code argument is passed directly to the completion handler:

my_timer.async_wait(
    asio::experimental::as_single(
      [](asio::error_code error)
      {
        // ...
      }));

When applied to a socket read operation, where the completion signature specifies two parameters, the handler is passed the result as a tuple:

my_socket.async_read_some(my_buffer,
    asio::experimental::as_single,
      [](std::tuple<asio::error_code, std::size_t> result)
      {
        // ...
      }));
append

The append completion token adapter can be used to pass additional completion handler arguments at the end of the completion signature.

For example:

timer.async_wait(
    asio::append(
      [](std::error_code ec, int i)
      {
        // ...
      },
      42
    )
  );

std::future<int> f = timer.async_wait(
    asio::append(
      asio::use_future,
      42
    )
  );
prepend

The prepend completion token adapter can be used to pass additional completion handler arguments before the existing completion handler arguments.

For example:

timer.async_wait(
    asio::prepend(
      [](int i, std::error_code ec)
      {
        // ...
      },
      42
    )
  );

std::future<std::tuple<int, std::error_code>> f = timer.async_wait(
    asio::prepend(
      asio::use_future,
      42
    )
  );
consign

The consign completion token adapter can be used to attach additional values to a completion handler. This is typically used to keep at least one copy of an object, such as a smart pointer, alive until the completion handler is called.

For example:

auto timer1 = std::make_shared<asio::steady_timer>(my_io_context);
timer1->expires_after(std::chrono::seconds(1));
timer1->async_wait(
    asio::consign(
      [](std::error_code ec)
      {
        // ...
      },
      timer1
    )
  );

auto timer2 = std::make_shared<asio::steady_timer>(my_io_context);
timer2->expires_after(std::chrono::seconds(30));
std::future<void> f =
  timer2->async_wait(
    asio::consign(
      asio::use_future,
      timer2
    )
  );
See Also

bind_executor, bind_allocator, bind_cancellation_slot, redirect_error, as_tuple, experimental::as_single, append, prepend.


PrevUpHomeNext