/* * Copyright 2022 Axel Waggershauser */ // SPDX-License-Identifier: Apache-2.0 #pragma once #ifdef __ANDROID__ #include #endif #ifdef __cpp_impl_coroutine #if defined __ANDROID__ && __NDK_MAJOR__ < 26 // NDK 25.1.8937393 can compile this code with c++20 but needs a few tweaks: #include namespace std { using experimental::suspend_always; using experimental::coroutine_handle; struct default_sentinel_t {}; } #else #include #include #endif #include #include // this code is based on https://en.cppreference.com/w/cpp/coroutine/coroutine_handle#Example // but modified trying to prevent accidental copying of generated objects #if defined __ANDROID__ && __NDK_MAJOR__ < 26 template #else template #endif class Generator { public: struct promise_type { Generator get_return_object() { return Generator{Handle::from_promise(*this)}; } static std::suspend_always initial_suspend() noexcept { return {}; } static std::suspend_always final_suspend() noexcept { return {}; } std::suspend_always yield_value(T&& value) noexcept { current_value = std::move(value); return {}; } // void return_value(T&& value) noexcept { current_value = std::move(value); } static void return_void() {} // required to compile in VisualStudio, no idea why clang/gcc are happy without // Disallow co_await in generator coroutines. void await_transform() = delete; [[noreturn]] static void unhandled_exception() { throw; } std::optional current_value; }; using Handle = std::coroutine_handle; explicit Generator(const Handle coroutine) : _coroutine{coroutine} {} Generator() = default; ~Generator() { if (_coroutine) _coroutine.destroy(); } Generator(const Generator&) = delete; Generator& operator=(const Generator&) = delete; Generator(Generator&& other) noexcept : _coroutine{other._coroutine} { other._coroutine = {}; } // Generator& operator=(Generator&& other) noexcept // { // if (this != &other) { // if (_coroutine) // _coroutine.destroy(); // _coroutine = other._coroutine; // other._coroutine = {}; // } // return *this; // } // Range-based for loop support. class Iter { public: void operator++() { _coroutine.resume(); } T&& operator*() const { return std::move(*_coroutine.promise().current_value); } bool operator==(std::default_sentinel_t) const { return !_coroutine || _coroutine.done(); } explicit Iter(const Handle coroutine) : _coroutine{coroutine} {} private: Handle _coroutine; }; Iter begin() { if (_coroutine) _coroutine.resume(); return Iter{_coroutine}; } std::default_sentinel_t end() { return {}; } private: Handle _coroutine; }; #endif /* usage example: template Generator range(T first, const T last) { while (first < last) co_yield first++; } int main() { for (const char i : range(65, 91)) std::cout << i << ' '; std::cout << '\n'; } */