From 83b6672ebd1424f964d26fcb76fcf5083d617bb0 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 17 Jul 2024 10:26:52 +0300 Subject: [PATCH] [generator] add boolean interface and tests --- include/itlib/generator.hpp | 39 +++++++++++++++++++++++++++++++++++-- test/t-generator-20.cpp | 12 ++++++++---- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/include/itlib/generator.hpp b/include/itlib/generator.hpp index b925122..e62aa63 100644 --- a/include/itlib/generator.hpp +++ b/include/itlib/generator.hpp @@ -28,7 +28,7 @@ // // VERSION HISTORY // -// 1.00 (2024-07-xx) Initial release +// 1.00 (2024-07-17) Initial release // // // DOCUMENTATION @@ -37,7 +37,9 @@ // It defines the class generator which allows you to write simple coroutine- // based generators. // -// Example: +// The library provides two interfaces for consuming the generated values. +// +// The first is a range-for iterator-like interface. Example: // // itlib::generator range(int begin, int end) { // for (int i = begin; i < end; ++i) { @@ -49,6 +51,22 @@ // std::cout << i << std::endl; // } // +// The range-for interface would copy the return values if they are not +// references. Unfortunately this is required to make operator* work as if +// it's a real iterator (safely called multiple times) +// +// In case you want to avoid the copies, prefer using the next() interface: +// +// auto r = range(0, 10); +// while (true) { +// auto v = r.next(); +// if (!v) break; +// // v is std::optional, you can move the value out of it +// std::cout << *v << std::endl; +// } +// +// Both interfaces support reference generated values. +// // TESTS // // You can find unit tests in the official repo: @@ -121,10 +139,27 @@ class generator { using handle_t = std::coroutine_handle; + generator(generator&& other) noexcept : m_handle(std::exchange(other.m_handle, nullptr)) {} + + generator& operator=(generator&& other) noexcept { + if (m_handle) m_handle.destroy(); + m_handle = std::exchange(other.m_handle, nullptr); + return *this; + } + ~generator() { if (m_handle) m_handle.destroy(); } + void reset() noexcept { + if (m_handle) m_handle.destroy(); + m_handle = nullptr; + } + + explicit operator bool() const noexcept { + return !!m_handle; + } + // next (optional-based) interface // NOTE: this won't return true until next() has returned an empty optional at least once diff --git a/test/t-generator-20.cpp b/test/t-generator-20.cpp index 046b913..11f51cf 100644 --- a/test/t-generator-20.cpp +++ b/test/t-generator-20.cpp @@ -52,10 +52,14 @@ TEST_CASE("simple") { ); CHECK(i == 102); - auto tr = range(101, 105); - CHECK_NOTHROW(tr.next()); - CHECK_NOTHROW(tr.next()); - CHECK_THROWS_WITH_AS(tr.next(), "test exception", std::runtime_error); + r = range(101, 105); + CHECK_NOTHROW(r.next()); + CHECK_NOTHROW(r.next()); + CHECK_THROWS_WITH_AS(r.next(), "test exception", std::runtime_error); + + CHECK(!!r); + r.reset(); + CHECK_FALSE(r); } template