-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path03_CoroGenerators.cpp
110 lines (87 loc) · 3.42 KB
/
03_CoroGenerators.cpp
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
// Copyright (c) 2024 Damian Nowakowski. All rights reserved.
// This is the 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(); }
};
// Generator that will count to three
CoroGenerator<int> CountToThree()
{
co_yield 1;
co_yield 2;
co_yield 3;
}
// Main program
int main()
{
// Constructs the Generator. Because initial_suspend is set to suspend_always the defined function
// will not start immediately.
auto generator = CountToThree();
// 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 2 3
*/