///
// generator - Single-header, ranges-compatible generator type built
// on C++20 coroutines
// Written in 2021 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
//
// Documentation available at https://tl.tartanllama.xyz/
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to the
// public domain worldwide. This software is distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software. If not, see
// .
///
#ifndef TL_GENERATOR_HPP
#define TL_GENERATOR_HPP
#define TL_GENERATOR_VERSION_MAJOR 0
#define TL_GENERATOR_VERSION_MINOR 3
#define TL_GENERATOR_VERSION_PATCH 0
#include
#include
#include
#include
#include
namespace tl {
template
class generator {
struct promise {
using value_type = std::remove_reference_t;
using reference_type = value_type&;
using pointer_type = value_type*;
promise() = default;
generator get_return_object() {
return generator(std::coroutine_handle::from_promise(*this));
}
std::suspend_always initial_suspend() const { return {}; }
std::suspend_always final_suspend() const noexcept { return {}; }
void return_void() const noexcept { return; }
void unhandled_exception() noexcept {
exception_ = std::current_exception();
}
void rethrow_if_exception() {
if (exception_) {
std::rethrow_exception(exception_);
}
}
std::suspend_always yield_value(reference_type v) noexcept {
value_ = std::addressof(v);
return {};
}
std::exception_ptr exception_;
pointer_type value_;
};
public:
using promise_type = promise;
class sentinel {};
class iterator {
using handle_type = std::coroutine_handle;
public:
using value_type = typename promise_type::value_type;
using reference_type = typename promise_type::reference_type;
using pointer_type = typename promise_type::pointer_type;
using difference_type = std::ptrdiff_t;
iterator() = default;
~iterator() {
if (handle_) handle_.destroy();
}
//Non-copyable because coroutine handles point to a unique resource
iterator(iterator const&) = delete;
iterator(iterator&& rhs) noexcept : handle_(std::exchange(rhs.handle_, nullptr)) {}
iterator& operator=(iterator const&) = delete;
iterator& operator=(iterator&& rhs) noexcept {
handle_ = std::exchange(rhs.handle_, nullptr);
return *this;
}
friend bool operator==(iterator const& it, sentinel) noexcept {
return (!it.handle_ || it.handle_.done());
}
iterator& operator++() {
handle_.resume();
if (handle_.done()) {
handle_.promise().rethrow_if_exception();
}
return *this;
}
void operator++(int) {
(void)this->operator++();
}
reference_type operator*() const
noexcept(noexcept(std::is_nothrow_copy_constructible_v)){
return *handle_.promise().value_;
}
private:
friend class generator;
iterator(handle_type handle) : handle_(handle) {}
handle_type handle_;
};
using handle_type = std::coroutine_handle;
generator() noexcept = default;
~generator() {
if (handle_) handle_.destroy();
}
generator(generator const&) = delete;
generator(generator&& rhs) noexcept : handle_(std::exchange(rhs.handle_, nullptr)) {}
generator& operator=(generator const&) = delete;
generator& operator=(generator&& rhs) noexcept {
swap(rhs);
return *this;
}
iterator begin() {
handle_.resume();
if (handle_.done()) {
handle_.promise().rethrow_if_exception();
}
return {std::exchange(handle_, nullptr)};
}
sentinel end() const noexcept {
return {};
}
void swap(generator& other) noexcept {
std::swap(handle_, other.handle_);
}
private:
friend class iterator;
explicit generator(handle_type handle) noexcept : handle_(handle) {}
handle_type handle_ = nullptr;
};
}
template
inline constexpr bool std::ranges::enable_view> = true;
#endif