#### 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:

- Automatically wrapping the completion handler to add associated characteristics.
- Transforming the completion signature and arguments passed to the completion handler.

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

###### bind_executor, bind_allocator, and bind_cancellation_slot

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` and `bind_cancellation_slot` adapters work similarly, to imbue the completion handler with an associated allocator or associated cancellation slot 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
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.