lewissbaker/generator

Possible Bug when used with std::views::take ?

sherwin-dc opened this issue · 4 comments

When using the generator with std::views::take it seems that the generator function is being called an extra time. This example demonstrates the issue and has been adapted from the cppreference example of std::generator:

#include <iostream>
#include "__generator.hpp"

std::generator<char> letters(char first)
{
    while (true) {
        std::cout << first;
        co_yield first;
        first++;
    }
}

int main()
{
    for (const char ch : letters('a') | std::views::take(5))
        std::cout << ch << ' ';
    std::cout << '\n';
}

This outputs

aa bb cc dd ee f

which shows that the function letters is called 6 times instead of 5. The example is available on godbolt and has the same output when used with either GNU libstdc++ or clang libc++.

This is a known flaw when the take_view is used with an input_range such as std::generator.

The take_view uses a counted_iterator, which has a base() method that returns the underlying/adapted iterator value.
This underlying iterator is advanced whenever the counted_iterator is advanced.
So when the counted_iterator is advanced to the end, it also needs to advance the underlying iterator to one past the last element so that if you query the base() of this iterator that's equal to end() then it has moved a distance of N from begin().
This requires resuming the coroutine one more time than expected.

A counted_iterator that doesn't expose base() wouldn't need to increment the underlying iterator when it reached end().

I see, thank you for clarifying. Hopefully compiler support would come soon for std::generator and I suppose then take_view would be changed not to increment the base iterator and resume the coroutine.

This issue was raised here: https://github.org/cplusplus/nbballot/issues/523

Unfortunately it doesn't look like any fix could be agreed upon.

I see, I guess I'll keep my fingers crossed for C++26