GCC Code Coverage Report


Directory: ./
File: include/CXXIter/src/Generator.h
Date: 2023-01-04 16:32:12
Exec Total Coverage
Lines: 27 27 100.0%
Functions: 22 24 91.7%
Branches: 2 12 16.7%

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