forked from jbandela/stackless_coroutine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
example.cpp
151 lines (124 loc) · 4.97 KB
/
example.cpp
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
// Copyright 2016 John R. Bandela
// 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 "stackless_coroutine.hpp"
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <iostream>
template <class Finished>
auto print_url(boost::asio::io_service &io, std::string host, std::string url,
Finished f) {
// Holds values that must be across calls to the lambdas
struct coroutine_variables_t {
boost::asio::ip::tcp::resolver resolver;
boost::asio::ip::tcp::socket socket;
boost::asio::streambuf request;
std::string current;
explicit coroutine_variables_t(boost::asio::io_service &io)
: resolver{io}, socket{io} {}
};
// Create the block for the coroutine
static auto block = stackless_coroutine::make_block(
// For the first lamda of the block, in addition to context, and
// variables,
// can take other parameters
[](auto &context, auto &variables, const std::string &host,
const std::string &path) {
std::ostream request_stream(&variables.request);
request_stream << "GET " << path << " HTTP/1.0\r\n";
request_stream << "Host: " << host << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
boost::asio::ip::tcp::resolver::query query{host, "http"};
// Pass context in to any function that requires a callback
variables.resolver.async_resolve(query, context);
// Return do_async to signal that we have made an async call, and that
// we should not go on the next
// lamda upon return
return context.do_async();
},
// In the next lambda after a do_async, in addition to context, and
// variables,
// also take the parameters
// of the callback, in this case the error_code ec, and iterator
[](auto &context, auto &variables, auto ec, auto iterator) {
// We can throw exceptions, and the exception will exit the entire block
if (ec) {
throw boost::system::system_error{ec};
} else {
// Pass context as the callback
boost::asio::async_connect(variables.socket, iterator, context);
return context.do_async();
}
},
// Take the parameters for the async_connect callback
[](auto &context, auto &variables, auto &ec, auto iterator) {
if (ec) {
throw boost::system::system_error{ec};
}
// Send the request.
boost::asio::async_write(variables.socket, variables.request, context);
return context.do_async();
},
[](auto &context, auto &variables, auto &ec, auto n) {
if (ec) {
throw boost::system::system_error{ec};
}
},
// make_while_true creates a loop, that always repeats, unless
// context.do_break is called
stackless_coroutine::make_while_true(
[](auto &context, auto &variables) {
variables.current.resize(20);
variables.socket.async_read_some(
boost::asio::buffer(&variables.current[0],
variables.current.size()),
context);
return context.do_async();
},
[](auto &context, auto &variables, auto &ec, auto n) {
if (ec) {
if (ec != boost::asio::error::eof)
std::cerr << ec.message();
// context.do_break breaks out of the loop
return context.do_break();
} else {
variables.current.resize(n);
std::cout << variables.current;
// context.do_continue continues the loop out of the loop
// We could also return do_next() which will continue to the next
// lamda
// which in this case would result in re-entering the loop at the
// top
return context.do_continue();
}
}
));
// pass the block, and the callback, along with any arguments for our
// cororoutine_variables_t
// constructor,
// which in this case is io to make_coroutine
auto co = stackless_coroutine::make_coroutine<coroutine_variables_t>(
block, std::move(f), io);
// call co with arguments corresponding to the parameters of the first lambda
// after context and variables
// which in this case is host, and url
return co(host, url);
}
int main() {
boost::asio::io_service io;
print_url(
io, "www.httpbin.org", "/",
// The callback - which takes a reference to the coroutine variables
// struct, an exception pointer, and op which tells us how we exited the
// coroutine
[&](auto &variables, std::exception_ptr e, auto op) {
if (e) {
std::cerr << "\nHad an exception\n";
} else {
std::cout << "\nFinished successfully\n";
}
});
io.run();
};