Page 32 - MSDN Magazine, October 2017
P. 32

void rethrow_if_failed() promise.rethrow_if_failed(); {}
if (value.index() == 1) {
return *this; }
iterator operator++(int) = delete;
Otherwise, the iterator is considered to have reached its end and its handle is simply cleared such that it will compare success- fully against the end iterator. Care needs to be taken to clear the coroutine handle prior to throwing any uncaught exception to pre- vent anyone from accidentally resuming the coroutine at the final suspension point, as this would lead to undefined behavior. The generator’s begin member function performs much the same logic, to ensure that I can consistently propagate any exception that’s thrown prior to reaching the first suspension point:
iterator begin() {
if (!handle) {
return nullptr;
} handle.resume();
if (handle.done()) {
handle.promise().rethrow_if_failed();
return nullptr; }
return handle; }
The main difference is that begin is a member of the generator, which owns the coroutine handle, and therefore must not clear the coroutine handle. Finally, and quite simply, I can implement iter- ator dereferencing simply by returning a reference to the current value stored within the promise_type:
T const& operator*() const {
return *std::get<0>(handle.promise().value); }
T const* operator->() const {
return std::addressof(operator*());
std::rethrow_exception(std::get<1>(value));
}
Enough about the promise_type. I now have a functioning gen- erator—but I’ll just add a simple iterator so that I can easily drive it from a range-based for loop. As before, the iterator will have the boilerplate type aliases to describe itself to standard algorithms. However, the iterator simply holds on to the coroutine_handle:
struct iterator {
using iterator_category = std::input_iterator_tag; using value_type = T;
using difference_type = ptrdiff_t;
using pointer = T const*;
using reference = T const&; handle_type handle;
Incrementing the iterator is a little more involved than the simpler iota iterator as this is the primary point at which the gener- ator interacts with the coroutine. Incrementing the iterator implies that the iterator is valid and may in fact be incremented. Because the “end” iterator holds a nullptr handle, I can simply provide an iterator comparison, as follows:
bool operator==(iterator const& other) const {
return handle == other.handle; }
bool operator!=(iterator const& other) const {
return !(*this == other);
Assuming it’s a valid iterator, I first resume the coroutine, allow- ing it to execute and yield up its next value. I then need to check whether this execution brought the coroutine to an end, and if so, propagate any exception that might have been raised inside the coroutine:
iterator &operator++() {
handle.resume();
if (handle.done()) {
promise_type& promise = handle.promise(); handle = nullptr;
Figure 5 A Limitless Generator
}
}
}
And I’m done. I can now write all manner of algorithms, producing a variety of generated sequences using this generalized generator. Figure 4 shows what the inspirational range generator looks like.
Who needs a limited range, anyway? As I now have a pull model, I can simply have the caller decide when they’ve had enough, as you can see in Figure 5.
The possibilities are endless! There is, of course, more to gen- erators and coroutines and I’ve only just scratched the surface here. Join me next time for more on coroutines in C++. You can find the complete example from this article over on Compiler Explorer: godbolt.org/g/NXHBZR. n
Kenny Kerr is an author, systems programmer, and the creator of C++/WinRT. He is also an engineer on the Windows team at Microsoft where he is designing the future of C++ for Windows, enabling developers to write beautiful high- performance apps and components with incredible ease.
ThanKs to the following technical expert for reviewing this article: Gor Nishanov
template <typename T> generator<int> range(T first) {
while (true) {
co_yield first++; }
}
int main() {
for (int i : range(0)) {
printf("%d\n", i);
if (...) {
break; }
} }
28 msdn magazine
C++


































































































   30   31   32   33   34