| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #ifdef CXXITER_HAS_COROUTINE | ||
| 4 | |||
| 5 | #include <coroutine> | ||
| 6 | |||
| 7 | #include "Common.h" | ||
| 8 | |||
| 9 | namespace CXXIter { | ||
| 10 | |||
| 11 | // ################################################################################################ | ||
| 12 | // GENERATOR | ||
| 13 | // ################################################################################################ | ||
| 14 | |||
| 15 | /** | ||
| 16 | * @brief Generator that C++20 coroutines passed to CXXIter::IterApi::generateFrom() have to return. | ||
| 17 | * This generator supports exceptions, co_yield for producing an arbitrary amount of elements, and can | ||
| 18 | * take references as results from coroutines - as long as they live long enough until used. | ||
| 19 | */ | ||
| 20 | template<typename T> | ||
| 21 | class Generator { | ||
| 22 | public: | ||
| 23 | using value_type = T; | ||
| 24 | struct promise_type; | ||
| 25 | using Handle = std::coroutine_handle<promise_type>; | ||
| 26 | |||
| 27 | struct promise_type { | ||
| 28 | IterValue<T> currentItem; | ||
| 29 | std::exception_ptr exceptionPtr; | ||
| 30 | |||
| 31 | 34 | Generator<T> get_return_object() { | |
| 32 | 34 | return Generator{Handle::from_promise(*this)}; | |
| 33 | } | ||
| 34 | 34 | static std::suspend_always initial_suspend() noexcept { return {}; } | |
| 35 | 34 | static std::suspend_always final_suspend() noexcept { return {}; } | |
| 36 | 2080 | std::suspend_always yield_value(T value) noexcept { | |
| 37 | 2080 | currentItem = value; | |
| 38 | 2080 | return {}; | |
| 39 | } | ||
| 40 | |||
| 41 | 2 | void unhandled_exception() { | |
| 42 | 2 | exceptionPtr = std::current_exception(); | |
| 43 | } | ||
| 44 | }; | ||
| 45 | |||
| 46 | 34 | explicit Generator(const Handle coroutine) : m_coroutine{coroutine} {} | |
| 47 | Generator() = default; | ||
| 48 | 68 | ~Generator() { | |
| 49 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 33 times.
|
68 | if (m_coroutine) { |
| 50 | 2 | m_coroutine.destroy(); | |
| 51 | } | ||
| 52 | 68 | } | |
| 53 | |||
| 54 | Generator(const Generator&) = delete; | ||
| 55 | Generator& operator=(const Generator&) = delete; | ||
| 56 | |||
| 57 | 34 | Generator(Generator&& other) noexcept : m_coroutine{other.m_coroutine} { | |
| 58 | 34 | other.m_coroutine = {}; | |
| 59 | 34 | } | |
| 60 | Generator& operator=(Generator&& other) noexcept { | ||
| 61 | m_coroutine = other; | ||
| 62 | other.m_coroutine = {}; | ||
| 63 | return *this; | ||
| 64 | } | ||
| 65 | |||
| 66 | 1057 | IterValue<T> next() { | |
| 67 |
0/6✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
1057 | if(m_coroutine && !m_coroutine.promise().currentItem.has_value()) { |
| 68 | 1057 | m_coroutine.resume(); | |
| 69 |
0/2✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1057 | if(m_coroutine.promise().exceptionPtr) { |
| 70 | 1 | std::rethrow_exception(m_coroutine.promise().exceptionPtr); | |
| 71 | } | ||
| 72 | } | ||
| 73 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
1056 | if(m_coroutine.done()) { |
| 74 | 16 | m_coroutine.destroy(); | |
| 75 | 16 | m_coroutine = {}; | |
| 76 | 16 | return {}; | |
| 77 | } | ||
| 78 | // Depends on IterValue<T>'s guarantee, to clear the moved-out-of IterValue | ||
| 79 | // irrespecting on the type of T. (std::optional<T> doesn't!) | ||
| 80 | //TODO: unit-test for IterValue? | ||
| 81 | 1040 | return std::move(m_coroutine.promise().currentItem); | |
| 82 | } | ||
| 83 | |||
| 84 | private: | ||
| 85 | Handle m_coroutine; | ||
| 86 | }; | ||
| 87 | |||
| 88 | template<typename T> | ||
| 89 | concept GeneratorFunction = (std::invocable<T> && util::is_template_instance_v<std::invoke_result_t<T>, Generator>); | ||
| 90 | } | ||
| 91 | |||
| 92 | #endif | ||
| 93 |