Created
September 14, 2021 12:59
-
-
Save RedBeard0531/3e62d7d6d63884c09c8906c1d7dfd8a2 to your computer and use it in GitHub Desktop.
C++20 generator with nice semantics for simple generator<T>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <iterator> | |
#include <utility> | |
#include <coroutine> | |
// Only need one of these. Shouldn't depend on generator's T. | |
struct generator_sentinel {}; | |
template <typename T> | |
struct generator { | |
using Pointer = std::add_pointer_t<T>; // strips reference. | |
struct promise_type { | |
std::suspend_never initial_suspend() { | |
return {}; | |
} | |
std::suspend_always final_suspend() noexcept { | |
return {}; | |
} | |
auto yield_value(const T& v) requires(!std::is_reference_v<T> && | |
std::copy_constructible<T>) { | |
struct Owner : std::suspend_always { | |
Owner(const T& val, Pointer& out) : v(val) { | |
out = &v; | |
} | |
Owner(Owner&&) = delete; | |
T v; | |
}; | |
return Owner(v, ptr); | |
} | |
auto yield_value(T&& v) { // lval ref if T is lval ref | |
ptr = &v; | |
return std::suspend_always(); | |
} | |
auto get_return_object() { | |
return generator(this); | |
} | |
void return_void() {} | |
void unhandled_exception() { | |
throw; | |
} | |
auto coro() { | |
return std::coroutine_handle<promise_type>::from_promise(*this); | |
} | |
Pointer ptr = {}; | |
}; | |
generator(generator&& source) : p(std::exchange(source.p, nullptr)) {} | |
explicit generator(promise_type* p) : p(p) {} | |
~generator() { | |
if (p) | |
p->coro().destroy(); | |
} | |
// generator<T> is a Range. You can use it in a range-for loop. | |
using sentinel = generator_sentinel; | |
struct iterator { | |
using iterator_concept = std::input_iterator_tag; | |
using difference_type = ptrdiff_t; | |
using value_type = std::remove_cvref_t<T>; | |
bool operator==(sentinel) const { | |
return p->coro().done(); | |
} | |
iterator& operator++() { | |
p->ptr = {}; | |
p->coro().resume(); | |
return *this; | |
} | |
void operator++(int) { | |
operator++(); | |
} | |
Pointer operator->() const { | |
return p->ptr; | |
} | |
T&& operator*() const { // lval ref if T is lval ref | |
return static_cast<T&&>(*p->ptr); | |
} | |
promise_type* p; | |
}; | |
auto begin() { | |
return iterator{p}; | |
} | |
auto end() { | |
return sentinel(); | |
} | |
promise_type* p; | |
}; | |
#ifdef __cpp_lib_ranges | |
static_assert(std::input_iterator<generator<int>::iterator>); | |
static_assert(std::ranges::input_range<generator<int>>); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment