-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path04_CoroFibonacciGenerator.cpp
More file actions
128 lines (104 loc) · 3.83 KB
/
04_CoroFibonacciGenerator.cpp
File metadata and controls
128 lines (104 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright (c) 2024 Damian Nowakowski. All rights reserved.
// This is the second example of c++ coroutine generators.
// For more details check: https://github.com/zompi2/cppcorosample
#include <iostream>
#include <coroutine>
// Definition of the coroutine Generator which can store a value of a generic type
template<typename T>
struct CoroGenerator
{
// Forward declaration of the Promise so it can be used for a Handle definition
struct CoroPromise;
// Tell the Generator to use our Promise
using promise_type = CoroPromise;
// Convinient alias for the coroutine Handle type which uses declared Promise
using CoroHandle = std::coroutine_handle<CoroPromise>;
// Definition of the Generator Promise
struct CoroPromise
{
// Stored value of a generic type
T Value;
// Called in order to construct the Generator
CoroGenerator get_return_object() { return { CoroGenerator(CoroHandle::from_promise(*this)) }; }
// Suspend the Generator at the beginning
std::suspend_always initial_suspend() noexcept { return {}; }
// Suspend the Generator at the end
std::suspend_always final_suspend() noexcept { return {}; }
// Called when co_return is used
void return_void() {}
// Called when exception occurs
void unhandled_exception() {}
// Called when co_yield is used. Stores the value which comes with this yield
template<std::convertible_to<T> From>
std::suspend_always yield_value(From&& from)
{
Value = std::forward<From>(from);
return {};
}
};
// Stores the coroutine Handle used within this Generator
CoroHandle Handle;
// Try to resume the Generator and check if it's execution is done
explicit operator bool()
{
Handle.resume();
return !Handle.done();
}
// Get the lastly stored by co_yield value
T operator()()
{
return std::move(Handle.promise().Value);
}
// Constructor - save the coroutine Handle given during the get_return_object call in the Promise
CoroGenerator(CoroHandle InHandle) : Handle(InHandle) {}
// Destructor - explicitly destroy the coroutine Handle, because it is not destroyed automatically, because
// of the final_suspend set to suspend_always
~CoroGenerator() { Handle.destroy(); }
};
// Fibonacci Sequence Generator. Yields every next value of the sequence and suspends it's execution.
CoroGenerator<int> FibonacciGenerator(const int Amount)
{
if (Amount <= 0)
{
co_return;
}
int n1 = 1;
int n2 = 1;
for (int i = 1; i <= Amount; i++)
{
if (i < 3)
{
co_yield 1;
}
else
{
const int tmp = n2;
n2 = n1 + n2;
n1 = tmp;
co_yield n2;
}
}
}
// Main program
int main()
{
// Constructs the Generator. Because initial_suspend is set to suspend_always the defined function
// will not start immediately.
auto generator = FibonacciGenerator(10);
// Every time a bool() operator is called the Generator resumes it's execution and checks if the coroutine has finished.
// When the coroutine has finished the wile loop will finish.
while (generator)
{
// Get and print every value yielded by the Generator.
std::cout << generator() << " ";
}
// To check if the coroutine has finished the final_suspend was set to suspend_always. Thanks to that
// the coroutine will not be destroyed automatically when it finishes. This will allow us to check the Handle
// if the handling coroutine has been finished or not. In the end, the Generator goes out of scope and it's desctructor
// destroys the coroutine Handle.
return 0;
}
/**
The program should output:
1 1 2 3 5 8 13 21 34 55
*/