srctree

Robin Linden parent d68d2869 9443d041
util: Remove coroutine generator helper

This was being used in a JS parser I was working on, but now there'sstd::generator along with reference implementations we can use while wewait for stdlib-releases with that in, so this is no longer needed formy prototyping.

inlinesplit
ev/null added: 2, removed: 227, total 0
@@ -1,100 +0,0 @@
// SPDX-FileCopyrightText: 2021-2022 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
#ifndef UTIL_GENERATOR_H_
#define UTIL_GENERATOR_H_
 
#include <cassert>
#include <concepts>
#include <coroutine>
#include <exception>
#include <optional>
#include <utility>
 
namespace util {
 
template<std::movable T>
class Generator {
public:
// https://en.cppreference.com/w/cpp/coroutine/coroutine_traits
// NOLINTNEXTLINE(readability-identifier-naming)
struct promise_type {
std::optional<T> maybe_value;
 
Generator<T> get_return_object() { return Generator{Handle::from_promise(*this)}; }
 
void return_void() {}
 
static std::suspend_never initial_suspend() noexcept { return {}; }
static std::suspend_always final_suspend() noexcept { return {}; }
 
std::suspend_always yield_value(T value) noexcept {
maybe_value = std::move(value);
return {};
}
 
static void unhandled_exception() { std::terminate(); }
};
 
using Handle = std::coroutine_handle<promise_type>;
 
explicit Generator(Handle handle) : handle_(std::move(handle)) {}
~Generator() {
if (handle_) {
handle_.destroy();
}
}
 
Generator(Generator const &) = delete;
Generator &operator=(Generator const &) = delete;
 
Generator(Generator &&other) noexcept : handle_{std::exchange(other.handle_, {})} {}
Generator &operator=(Generator &&other) noexcept {
if (this != &other) {
handle_ = std::exchange(other.handle_, handle_);
}
 
return *this;
}
 
bool has_next() const { return !handle_.done(); }
T next() {
assert(has_next());
// NOLINTNEXTLINE(bugprone-unchecked-optional-access): Usage error, will crash, this is fine.
auto v = std::move(handle_.promise().maybe_value.value());
handle_.resume();
return v;
}
 
private:
struct CoroutineSentinel {
friend constexpr bool operator!=(auto const &generator_iterator, CoroutineSentinel const &) {
return !generator_iterator.handle.done();
}
};
 
struct GeneratorIterator {
Handle handle;
 
GeneratorIterator &operator++() {
handle.resume();
return *this;
}
 
// NOLINTNEXTLINE(bugprone-unchecked-optional-access): Usage error, will crash, this is fine.
T &operator*() const { return handle.promise().maybe_value.value(); }
T *operator->() const { return &**this; }
};
 
public:
GeneratorIterator begin() { return {handle_}; }
CoroutineSentinel end() { return {}; }
 
private:
Handle handle_{};
};
 
} // namespace util
 
#endif
 
ev/null added: 2, removed: 227, total 0
@@ -1,125 +0,0 @@
// SPDX-FileCopyrightText: 2021-2022 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
#include "util/generator.h"
 
#include "etest/etest.h"
 
using etest::expect;
using etest::expect_eq;
using etest::require;
 
int main() {
etest::test("next", [] {
auto gen = []() -> util::Generator<int> {
int i{0};
while (true) {
i += 1;
co_yield i;
}
}();
 
expect_eq(gen.next(), 1);
expect_eq(gen.next(), 2);
expect_eq(gen.next(), 3);
});
 
etest::test("has_next", [] {
auto gen = []() -> util::Generator<int> {
co_yield 1;
}();
 
require(gen.has_next());
expect_eq(gen.next(), 1);
expect(!gen.has_next());
});
 
etest::test("has_next with no yields", [] {
auto gen = []() -> util::Generator<int> {
co_return;
}();
expect(!gen.has_next());
});
 
etest::test("move constructor", [] {
auto gen1 = []() -> util::Generator<int> {
co_yield 1;
co_yield 2;
}();
 
expect_eq(1, gen1.next());
auto gen2{std::move(gen1)};
expect_eq(2, gen2.next());
expect(!gen2.has_next());
});
 
etest::test("move assign", [] {
auto gen1 = []() -> util::Generator<int> {
co_yield 1;
co_yield 2;
}();
 
expect_eq(1, gen1.next());
 
auto gen2 = []() -> util::Generator<int> {
co_yield 5;
}();
 
expect_eq(5, gen2.next());
 
gen2 = std::move(gen1);
expect_eq(2, gen2.next());
expect(!gen2.has_next());
});
 
// util/generator_test.cpp:84:13: error: explicitly moving variable of type
// 'util::Generator<int>' to itself [-Werror,-Wself-move]
#ifndef __clang__
etest::test("move assign to self", [] {
auto gen = []() -> util::Generator<int> {
co_yield 1;
co_yield 2;
}();
 
expect_eq(1, gen.next());
 
gen = std::move(gen);
expect_eq(2, gen.next());
expect(!gen.has_next());
});
#endif
 
etest::test("range-based for loop", [] {
auto gen = []() -> util::Generator<char> {
co_yield 'a';
co_yield 'b';
co_yield 'c';
}();
 
char current_expectation = 'a';
for (auto c : gen) {
expect_eq(current_expectation, c);
++current_expectation;
}
 
expect_eq('d', current_expectation);
});
 
etest::test("boring for loop", [] {
auto gen = []() -> util::Generator<std::pair<char, int>> {
co_yield {'a', 2};
}();
 
int iterations{};
for (auto it = std::begin(gen); it != std::end(gen); ++it) {
expect_eq(it->first, 'a');
expect_eq(it->second, 2);
++iterations;
}
 
expect_eq(1, iterations);
});
 
return etest::run_all_tests();
}