C++ Coroutine used to make generator never stops

75 Views Asked by At

You can check out my example at: https://cpp.sh/?source=%2F%2F+Example+program%0A%23include+%3Ciostream%3E%0A%23include+%3Cstring%3E%0A%0Aint+main()%0A%7B%0A++std%3A%3Astring+name%3B%0A++std%3A%3Acout+%3C%3C+%22What+is+your+name%3F+%22%3B%0A++getline+(std%3A%3Acin%2C+name)%3B%0A++std%3A%3Acout+%3C%3C+%22Hello%2C+%22+%3C%3C+name+%3C%3C+%22!%5Cn%22%3B%0A%7D

Below is the code:

    // Example program
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <coroutine>


template <typename T>
class Generator {
public:
    struct promise_type {
        T value;
        std::suspend_always yield_value(T v) { value = v; return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
        Generator get_return_object() { return Generator{ Handle::from_promise(*this) }; }
    };

    struct Handle : std::coroutine_handle<promise_type> {
        Handle(std::coroutine_handle<promise_type> h) : std::coroutine_handle<promise_type>(h) {}
        T& operator()() { return this->promise().value; }
        void operator++() { this->resume(); }
    };

    Generator(Handle h) : handle(h) {}
    ~Generator() { if (handle) handle.destroy(); }

    T& operator()() { return handle(); }
    void operator++() { ++handle; }
    explicit operator bool() { return !handle.done(); }

//private:
    Handle handle{};
};

Generator<int> generateNumbers(int start, int end) {
   for (int i = start; i <= end; ++i) {
      co_yield i;
   }
}

int main()
{
    auto gen = generateNumbers(1, 10); // Adjust the range as needed
    while (gen) {
        if(gen.handle.done()) // I also tried directly accessing the handle,  but this did also not work.
            break;
        std::cout << gen() << std::endl;
        ++gen;
    }
}

In this code I am creating a generator as a coroutine Generator class. Now I want to use this generator to generate some numbers in the generateNumbers function. I call this from main and try to iterate over the numbers. The problem is when I want to check if the iteration is over. I try checking the generator as a bool in the while. The overwritten bool operator calls the handle.done(), I checked in the debugger and it does so. The problem is that the handle.done() always returns false, even when the loop is indeed over. I tried calling handle.done() directly from within the loop, this also did not work.

I am using a compiler supporting the C++20 standard.

Can you help me understand my mistake? Thank you very much.

1

There are 1 best solutions below

0
On BEST ANSWER

done() returns true if the coroutine is suspended at its final suspend point.

Your final_suspend() returns std::suspend_never, so the coroutine will not be suspended at its final suspend point. You need to change it to return std::suspend_always.