Async++ unknown
Async (co_await/co_return) code for C++
Loading...
Searching...
No Matches
generator.h
1#pragma once
2#include <asyncpp/detail/promise_allocator_base.h>
4#include <exception>
5#include <functional>
6
7namespace asyncpp {
8 template<class T, ByteAllocator Allocator = default_allocator_type>
9 class generator;
10
11 namespace detail {
12 template<class T, ByteAllocator Allocator>
13 class generator_promise : public promise_allocator_base<Allocator> {
14 public:
15 using value_type = std::remove_reference_t<T>;
16 using reference_type = std::conditional_t<std::is_reference_v<T>, T, T&>;
17 using pointer_type = std::add_pointer_t<value_type>;
18
19 generator_promise() noexcept = default;
20 ~generator_promise() = default;
21 generator_promise(const generator_promise&) = delete;
22 generator_promise(generator_promise&&) = delete;
23
24 coroutine_handle<generator_promise> get_return_object() noexcept {
25 return coroutine_handle<generator_promise>::from_promise(*this);
26 }
27 suspend_always initial_suspend() noexcept { return {}; }
28 suspend_always final_suspend() noexcept { return {}; }
29
30 void return_void() noexcept {}
31
32 suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept {
33 m_value = std::addressof(value);
34 return {};
35 }
36
37 template<class U = T, typename = std::enable_if_t<!std::is_rvalue_reference_v<U>>>
38 suspend_always yield_value(std::remove_reference_t<T>& value) noexcept {
39 m_value = std::addressof(value);
40 return {};
41 }
42
43 void unhandled_exception() noexcept { m_exception = std::current_exception(); }
44
45 pointer_type value() const noexcept { return m_value; }
46
47 std::exception_ptr exception() const noexcept { return m_exception; }
48
49 // co_await is not supported in a synchronous generator
50 template<typename U>
51 suspend_never await_transform(U&& value) = delete;
52
53 private:
54 T* m_value{nullptr};
55 std::exception_ptr m_exception{nullptr};
56 };
57
58 struct generator_end {};
59
60 template<class T, ByteAllocator Allocator>
61 class generator_iterator {
62 public:
63 using promise_type = generator_promise<T, Allocator>;
64 using handle_t = coroutine_handle<promise_type>;
65 using iterator_category = std::input_iterator_tag;
66 using difference_type = std::ptrdiff_t;
67 using value_type = typename promise_type::value_type;
68 using reference = typename promise_type::reference_type;
69 using pointer = typename promise_type::pointer_type;
70
71 constexpr generator_iterator() noexcept : m_coro{nullptr} {}
72 explicit constexpr generator_iterator(handle_t hdl) noexcept : m_coro{hdl} {}
73
74 bool operator==(generator_end) const noexcept { return !m_coro || m_coro.done(); }
75
76 generator_iterator& operator++() {
77 m_coro.resume();
78 if (m_coro.done()) {
79 auto except = m_coro.promise().exception();
80 if (except) std::rethrow_exception(except);
81 }
82 return *this;
83 }
84
85 // Not really supported, but many people prefer postincrement even if the result is unused
86 void operator++(int) { ++(*this); }
87
88 reference operator*() const noexcept { return static_cast<reference>(*m_coro.promise().value()); }
89
90 pointer operator->() const noexcept { return m_coro.promise().value(); }
91
92 private:
93 handle_t m_coro;
94 };
95
96 template<typename T, ByteAllocator Allocator>
97 inline bool operator!=(const generator_iterator<T, Allocator>& iter, generator_end end) noexcept {
98 return !(iter == end);
99 }
100
101 template<typename T, ByteAllocator Allocator>
102 inline bool operator==(generator_end end, const generator_iterator<T, Allocator>& iter) noexcept {
103 return iter == end;
104 }
105
106 template<typename T, ByteAllocator Allocator>
107 inline bool operator!=(generator_end end, const generator_iterator<T, Allocator>& iter) noexcept {
108 return iter != end;
109 }
110 } // namespace detail
111
115 template<typename T, ByteAllocator Allocator>
116 class [[nodiscard]] generator {
117 public:
119 using promise_type = detail::generator_promise<T, Allocator>;
121 using iterator = detail::generator_iterator<T, Allocator>;
122
123 generator() noexcept : m_coro{nullptr} {}
124 //NOLINTNEXTLINE(google-explicit-constructor)
125 generator(coroutine_handle<promise_type> coro) noexcept : m_coro{coro} {}
126 generator(generator&& other) noexcept : m_coro{std::exchange(other.m_coro, {})} {}
127 generator(const generator&) = delete;
128 generator& operator=(generator&& other) noexcept {
129 m_coro = std::exchange(other.m_coro, m_coro);
130 return *this;
131 }
132 generator& operator=(const generator&) = delete;
133 ~generator() {
134 if (m_coro) m_coro.destroy();
135 }
136 [[nodiscard]] iterator begin() {
137 if (m_coro) {
138 m_coro.resume();
139 if (m_coro.done()) {
140 auto except = m_coro.promise().exception();
141 if (except) std::rethrow_exception(except);
142 }
143 }
144 return iterator{m_coro};
145 }
146 [[nodiscard]] constexpr detail::generator_end end() const noexcept { return {}; }
147
148 private:
149 coroutine_handle<promise_type> m_coro;
150 };
151
158 template<typename FUNC, typename T>
159 generator<std::invoke_result_t<FUNC&, typename generator<T>::iterator::reference>> fmap(FUNC func,
160 generator<T> source) {
161 for (auto&& value : source) {
162 co_yield std::invoke(func, static_cast<decltype(value)>(value));
163 }
164 }
165} // namespace asyncpp
Generator coroutine class.
Definition generator.h:116
detail::generator_promise< T, Allocator > promise_type
The promise type.
Definition generator.h:119
detail::generator_iterator< T, Allocator > iterator
The iterator type.
Definition generator.h:121
Provides a consistent import interface for coroutine, experimental/coroutine or a best effort fallbac...