summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/asio/src/examples/cpp20/operations/callback_wrapper.cpp
blob: f8f37b489441b005f78c023bc757801c3a294899 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//
// callback_wrapper.cpp
// ~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 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.hpp>
#include <iostream>

//------------------------------------------------------------------------------

// This is a mock implementation of an API that uses a move-only function object
// for exposing a callback. The callback has the signature void(std::string).

template <typename Callback>
void read_input(const std::string& prompt, Callback cb)
{
  std::thread(
      [prompt, cb = std::move(cb)]() mutable
      {
        std::cout << prompt << ": ";
        std::cout.flush();
        std::string line;
        std::getline(std::cin, line);
        std::move(cb)(std::move(line));
      }).detach();
}

//------------------------------------------------------------------------------

// This is an asynchronous operation that wraps the callback-based API.

// The initiating function for the asynchronous operation.
template <asio::completion_token_for<void(std::string)> CompletionToken>
auto async_read_input(const std::string& prompt, CompletionToken&& token)
{
  // Define a function object that contains the code to launch the asynchronous
  // operation. This is passed the concrete completion handler, followed by any
  // additional arguments that were passed through the call to async_initiate.
  auto init = [](
      asio::completion_handler_for<void(std::string)> auto handler,
      const std::string& prompt)
  {
    // According to the rules for asynchronous operations, we need to track
    // outstanding work against the handler's associated executor until the
    // asynchronous operation is complete.
    auto work = asio::make_work_guard(handler);

    // Launch the operation with a callback that will receive the result and
    // pass it through to the asynchronous operation's completion handler.
    read_input(prompt,
        [
          handler = std::move(handler),
          work = std::move(work)
        ](std::string result) mutable
        {
          // Get the handler's associated allocator. If the handler does not
          // specify an allocator, use the recycling allocator as the default.
          auto alloc = asio::get_associated_allocator(
              handler, asio::recycling_allocator<void>());

          // Dispatch the completion handler through the handler's associated
          // executor, using the handler's associated allocator.
          asio::dispatch(work.get_executor(),
              asio::bind_allocator(alloc,
                [
                  handler = std::move(handler),
                  result = std::string(result)
                ]() mutable
                {
                  std::move(handler)(result);
                }));
        });
  };

  // The async_initiate function is used to transform the supplied completion
  // token to the completion handler. When calling this function we explicitly
  // specify the completion signature of the operation. We must also return the
  // result of the call since the completion token may produce a return value,
  // such as a future.
  return asio::async_initiate<CompletionToken, void(std::string)>(
      init, // First, pass the function object that launches the operation,
      token, // then the completion token that will be transformed to a handler,
      prompt); // and, finally, any additional arguments to the function object.
}

//------------------------------------------------------------------------------

void test_callback()
{
  asio::io_context io_context;

  // Test our asynchronous operation using a lambda as a callback. We will use
  // an io_context to specify an associated executor.
  async_read_input("Enter your name",
      asio::bind_executor(io_context,
        [](const std::string& result)
        {
          std::cout << "Hello " << result << "\n";
        }));

  io_context.run();
}

//------------------------------------------------------------------------------

void test_deferred()
{
  asio::io_context io_context;

  // Test our asynchronous operation using the deferred completion token. This
  // token causes the operation's initiating function to package up the
  // operation with its arguments to return a function object, which may then be
  // used to launch the asynchronous operation.
  auto op = async_read_input("Enter your name", asio::deferred);

  // Launch our asynchronous operation using a lambda as a callback. We will use
  // an io_context to obtain an associated executor.
  std::move(op)(
      asio::bind_executor(io_context,
        [](const std::string& result)
        {
          std::cout << "Hello " << result << "\n";
        }));

  io_context.run();
}

//------------------------------------------------------------------------------

void test_future()
{
  // Test our asynchronous operation using the use_future completion token.
  // This token causes the operation's initiating function to return a future,
  // which may be used to synchronously wait for the result of the operation.
  std::future<std::string> f =
    async_read_input("Enter your name", asio::use_future);

  std::string result = f.get();
  std::cout << "Hello " << result << "\n";
}

//------------------------------------------------------------------------------

int main()
{
  test_callback();
  test_deferred();
  test_future();
}