coro-generator2.cpp (5882B)
1 #include <coroutine> 2 #include <exception> 3 #include <iostream> 4 #include <optional> 5 #include <utility> 6 #include <cstdio> 7 #include <unistd.h> 8 9 auto exchange_default(auto& x) 10 { 11 return std::exchange(x, {}); 12 } 13 14 struct resume_null_coroutine : std::exception 15 { 16 ~resume_null_coroutine() override = default; 17 char const* what() const noexcept override 18 { 19 return "resume_null_coroutine"; 20 } 21 }; 22 23 struct resume_finished_coroutine : std::exception 24 { 25 ~resume_finished_coroutine() override = default; 26 char const* what() const noexcept override 27 { 28 return "resume_finished_coroutine"; 29 } 30 }; 31 32 template <typename T = void> 33 struct [[nodiscard]] coroutine_owner 34 { 35 coroutine_owner() = default; 36 37 template <typename U> 38 explicit coroutine_owner(std::coroutine_handle<U> h) noexcept 39 : h{h} 40 {} 41 42 coroutine_owner(coroutine_owner const&) = delete; 43 coroutine_owner& operator =(coroutine_owner const&) = delete; 44 45 template <typename U> 46 coroutine_owner(coroutine_owner<U>&& old) noexcept 47 : h{old.release()} 48 {} 49 50 coroutine_owner& operator =(coroutine_owner&& old) 51 { 52 cancel(); 53 h = old.release(); 54 return *this; 55 } 56 57 ~coroutine_owner() noexcept 58 try { 59 cancel(); 60 } catch (...) 61 {} 62 63 void cancel() 64 { 65 if (auto x = release()) 66 x.destroy(); 67 } 68 69 [[nodiscard]] 70 std::coroutine_handle<T> get() noexcept 71 { 72 return h; 73 } 74 75 [[nodiscard]] 76 std::coroutine_handle<T> release() noexcept 77 { 78 return exchange_default(h); 79 } 80 81 [[nodiscard]] 82 std::coroutine_handle<T>const* operator->() const noexcept 83 { 84 return &h; 85 } 86 87 [[nodiscard]] 88 std::coroutine_handle<T>* operator->() noexcept 89 { 90 return &h; 91 } 92 93 void resume() 94 { 95 if (not h) 96 throw resume_null_coroutine{}; 97 if (h.done()) 98 throw resume_finished_coroutine{}; 99 h.resume(); 100 } 101 102 void operator() () 103 { 104 resume(); 105 } 106 107 private: 108 std::coroutine_handle<T> h; 109 }; 110 111 template <typename T, typename U = T> 112 coroutine_owner<U> make_coroutine_owner(T& promise) noexcept 113 { 114 return coroutine_owner<U>{std::coroutine_handle<T>::from_promise(promise)}; 115 } 116 117 struct yield_blocked : std::exception 118 { 119 ~yield_blocked() override = default; 120 char const* what() const noexcept override 121 { 122 return "yield_blocked"; 123 } 124 }; 125 126 struct symmetric_transfer : std::suspend_always 127 { 128 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) noexcept 129 { 130 return std::exchange(waiting, std::noop_coroutine()); 131 } 132 std::coroutine_handle<>& waiting; 133 }; 134 135 template <typename T> 136 struct [[nodiscard]] generator 137 { 138 struct promise_type 139 { 140 promise_type() = default; 141 ~promise_type() noexcept 142 try { 143 if (ep) 144 std::rethrow_exception(ep); 145 } catch (std::exception const& e) 146 { 147 std::cerr << "Dropped exception '" << e.what() << "'\n"; 148 } catch (...) 149 { 150 std::cerr << "Dropped unknown exception type\n"; 151 } 152 153 auto get_return_object() { return generator{*this}; } 154 auto initial_suspend() const noexcept { return std::suspend_always{}; } 155 auto yield_value(T v) 156 { 157 return_value(std::move(v)); 158 return symmetric_transfer{{}, waiting}; 159 } 160 void return_value(T v) 161 { 162 if (yielded_value) 163 throw yield_blocked{}; 164 yielded_value = std::move(v); 165 } 166 void unhandled_exception() noexcept { ep = std::current_exception(); } 167 auto final_suspend() noexcept { return symmetric_transfer{{}, waiting}; } 168 169 friend struct generator; 170 private: 171 std::optional<T> yielded_value; 172 std::exception_ptr ep; 173 std::coroutine_handle<> waiting = std::noop_coroutine(); 174 }; 175 176 struct [[nodiscard]] awaitable 177 { 178 bool await_ready() 179 { 180 return !!coro->promise().yielded_value; 181 } 182 std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) 183 { 184 coro->promise().waiting = h; 185 return coro.get(); 186 } 187 T await_resume() 188 { 189 if (auto ep = exchange_default(coro->promise().ep)) 190 std::rethrow_exception(ep); 191 return exchange_default(coro->promise().yielded_value).value(); 192 } 193 coroutine_owner<promise_type>& coro; 194 }; 195 196 awaitable get_awaitable() { return {coro}; } 197 198 T generate() 199 { 200 auto& promise = coro->promise(); 201 if (not promise.yielded_value) 202 { 203 coro.resume(); 204 if (auto ep = exchange_default(promise.ep)) 205 std::rethrow_exception(ep); 206 } 207 return exchange_default(promise.yielded_value).value(); 208 } 209 210 T operator() () { return generate(); } 211 212 private: 213 explicit generator(promise_type& p) 214 : coro{make_coroutine_owner<promise_type>(p)} 215 {} 216 217 coroutine_owner<promise_type> coro; 218 }; 219 220 generator<unsigned char> fd_charize(int fd) 221 { 222 for (;;) 223 { 224 unsigned char buf[1024]; 225 ssize_t n = ::read(fd, buf, sizeof buf); 226 227 if (n < 0) 228 throw std::runtime_error{"read() failed"}; 229 else if (not n) 230 co_return '\0'; 231 232 for (ssize_t i = 0; i < n; ++i) 233 if (buf[i]) 234 co_yield buf[i]; 235 else 236 throw std::runtime_error{"null in char stream"}; 237 } 238 } 239 240 generator<std::string> tokenize(generator<unsigned char>& chars, int(*is_delimiter)(int)) 241 { 242 std::string rval; 243 244 while (auto c = chars.generate()) 245 { 246 while (is_delimiter(c)) 247 if (not (c = chars.generate())) 248 goto empty; 249 250 while (not is_delimiter(c)) 251 { 252 rval.push_back(c); 253 if (not (c = chars.generate())) 254 goto empty; 255 } 256 257 co_yield std::move(rval); 258 rval.clear(); 259 } 260 empty: 261 if (not rval.empty()) 262 co_yield std::move(rval); 263 co_return {}; 264 } 265 266 generator<std::string> tokenize2(generator<unsigned char>& chars, int(*is_delimiter)(int)) 267 { 268 std::string rval; 269 270 auto a = chars.get_awaitable(); 271 272 while (auto c = co_await a) 273 { 274 while (is_delimiter(c)) 275 if (not (c = co_await a)) 276 goto empty; 277 278 while (not is_delimiter(c)) 279 { 280 rval.push_back(c); 281 if (not (c = co_await a)) 282 goto empty; 283 } 284 285 co_yield std::move(rval); 286 rval.clear(); 287 } 288 empty: 289 if (not rval.empty()) 290 co_yield std::move(rval); 291 co_return {}; 292 } 293 294 int main() 295 { 296 std::cerr << std::boolalpha; 297 auto chars = fd_charize(0); 298 auto strings = tokenize2(chars, std::isspace); 299 for (auto str = strings.generate(); not str.empty(); str = strings.generate()) 300 std::cout << '[' << str << "]\n"; 301 }