Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:52

0001 ///////////////////////////////////////////////////////////////////////////////
0002 // Copyright (c) Lewis Baker
0003 // Licenced under MIT license. See LICENSE.txt for details.
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             // Don't allow any use of 'co_await' inside the generator coroutine.
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             // What type should we use for counting elements of a potentially infinite sequence?
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             // Iterator needs to be default-constructible to satisfy the Range concept.
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             // Need to provide post-increment operator to implement the 'Range' concept.
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