File indexing completed on 2025-01-18 09:54:52
0001
0002
0003
0004
0005 #ifndef CPPCORO_GENERATOR_HPP_INCLUDED
0006 #define CPPCORO_GENERATOR_HPP_INCLUDED
0007
0008 #include <cppcoro/coroutine.hpp>
0009 #include <type_traits>
0010 #include <utility>
0011 #include <exception>
0012 #include <iterator>
0013 #include <functional>
0014
0015 namespace cppcoro
0016 {
0017 template<typename T>
0018 class generator;
0019
0020 namespace detail
0021 {
0022 template<typename T>
0023 class generator_promise
0024 {
0025 public:
0026
0027 using value_type = std::remove_reference_t<T>;
0028 using reference_type = std::conditional_t<std::is_reference_v<T>, T, T&>;
0029 using pointer_type = value_type*;
0030
0031 generator_promise() = default;
0032
0033 generator<T> get_return_object() noexcept;
0034
0035 constexpr cppcoro::suspend_always initial_suspend() const noexcept { return {}; }
0036 constexpr cppcoro::suspend_always final_suspend() const noexcept { return {}; }
0037
0038 template<
0039 typename U = T,
0040 std::enable_if_t<!std::is_rvalue_reference<U>::value, int> = 0>
0041 cppcoro::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
0042 {
0043 m_value = std::addressof(value);
0044 return {};
0045 }
0046
0047 cppcoro::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept
0048 {
0049 m_value = std::addressof(value);
0050 return {};
0051 }
0052
0053 void unhandled_exception()
0054 {
0055 m_exception = std::current_exception();
0056 }
0057
0058 void return_void()
0059 {
0060 }
0061
0062 reference_type value() const noexcept
0063 {
0064 return static_cast<reference_type>(*m_value);
0065 }
0066
0067
0068 template<typename U>
0069 cppcoro::suspend_never await_transform(U&& value) = delete;
0070
0071 void rethrow_if_exception()
0072 {
0073 if (m_exception)
0074 {
0075 std::rethrow_exception(m_exception);
0076 }
0077 }
0078
0079 private:
0080
0081 pointer_type m_value;
0082 std::exception_ptr m_exception;
0083
0084 };
0085
0086 struct generator_sentinel {};
0087
0088 template<typename T>
0089 class generator_iterator
0090 {
0091 using coroutine_handle = cppcoro::coroutine_handle<generator_promise<T>>;
0092
0093 public:
0094
0095 using iterator_category = std::input_iterator_tag;
0096
0097 using difference_type = std::ptrdiff_t;
0098 using value_type = typename generator_promise<T>::value_type;
0099 using reference = typename generator_promise<T>::reference_type;
0100 using pointer = typename generator_promise<T>::pointer_type;
0101
0102
0103 generator_iterator() noexcept
0104 : m_coroutine(nullptr)
0105 {}
0106
0107 explicit generator_iterator(coroutine_handle coroutine) noexcept
0108 : m_coroutine(coroutine)
0109 {}
0110
0111 friend bool operator==(const generator_iterator& it, generator_sentinel) noexcept
0112 {
0113 return !it.m_coroutine || it.m_coroutine.done();
0114 }
0115
0116 friend bool operator!=(const generator_iterator& it, generator_sentinel s) noexcept
0117 {
0118 return !(it == s);
0119 }
0120
0121 friend bool operator==(generator_sentinel s, const generator_iterator& it) noexcept
0122 {
0123 return (it == s);
0124 }
0125
0126 friend bool operator!=(generator_sentinel s, const generator_iterator& it) noexcept
0127 {
0128 return it != s;
0129 }
0130
0131 generator_iterator& operator++()
0132 {
0133 m_coroutine.resume();
0134 if (m_coroutine.done())
0135 {
0136 m_coroutine.promise().rethrow_if_exception();
0137 }
0138
0139 return *this;
0140 }
0141
0142
0143 void operator++(int)
0144 {
0145 (void)operator++();
0146 }
0147
0148 reference operator*() const noexcept
0149 {
0150 return m_coroutine.promise().value();
0151 }
0152
0153 pointer operator->() const noexcept
0154 {
0155 return std::addressof(operator*());
0156 }
0157
0158 private:
0159
0160 coroutine_handle m_coroutine;
0161 };
0162 }
0163
0164 template<typename T>
0165 class [[nodiscard]] generator
0166 {
0167 public:
0168
0169 using promise_type = detail::generator_promise<T>;
0170 using iterator = detail::generator_iterator<T>;
0171
0172 generator() noexcept
0173 : m_coroutine(nullptr)
0174 {}
0175
0176 generator(generator&& other) noexcept
0177 : m_coroutine(other.m_coroutine)
0178 {
0179 other.m_coroutine = nullptr;
0180 }
0181
0182 generator(const generator& other) = delete;
0183
0184 ~generator()
0185 {
0186 if (m_coroutine)
0187 {
0188 m_coroutine.destroy();
0189 }
0190 }
0191
0192 generator& operator=(generator other) noexcept
0193 {
0194 swap(other);
0195 return *this;
0196 }
0197
0198 iterator begin()
0199 {
0200 if (m_coroutine)
0201 {
0202 m_coroutine.resume();
0203 if (m_coroutine.done())
0204 {
0205 m_coroutine.promise().rethrow_if_exception();
0206 }
0207 }
0208
0209 return iterator{ m_coroutine };
0210 }
0211
0212 detail::generator_sentinel end() noexcept
0213 {
0214 return detail::generator_sentinel{};
0215 }
0216
0217 void swap(generator& other) noexcept
0218 {
0219 std::swap(m_coroutine, other.m_coroutine);
0220 }
0221
0222 private:
0223
0224 friend class detail::generator_promise<T>;
0225
0226 explicit generator(cppcoro::coroutine_handle<promise_type> coroutine) noexcept
0227 : m_coroutine(coroutine)
0228 {}
0229
0230 cppcoro::coroutine_handle<promise_type> m_coroutine;
0231
0232 };
0233
0234 template<typename T>
0235 void swap(generator<T>& a, generator<T>& b)
0236 {
0237 a.swap(b);
0238 }
0239
0240 namespace detail
0241 {
0242 template<typename T>
0243 generator<T> generator_promise<T>::get_return_object() noexcept
0244 {
0245 using coroutine_handle = cppcoro::coroutine_handle<generator_promise<T>>;
0246 return generator<T>{ coroutine_handle::from_promise(*this) };
0247 }
0248 }
0249
0250 template<typename FUNC, typename T>
0251 generator<std::invoke_result_t<FUNC&, typename generator<T>::iterator::reference>> fmap(FUNC func, generator<T> source)
0252 {
0253 for (auto&& value : source)
0254 {
0255 co_yield std::invoke(func, static_cast<decltype(value)>(value));
0256 }
0257 }
0258 }
0259
0260 #endif