Home > OS >  Regarding the pause-resume data loss in MSVC std::experimental::generator
Regarding the pause-resume data loss in MSVC std::experimental::generator

Time:08-03

Since the std::generator is making it into CPP23, I am playing around with MSVC's incomplete version.

However, I notice that it seems lose exactly one yield when used with std::views::take. Here is the example:

#include <iostream>
#include <ranges>

#include <experimental/generator>

std::experimental::generator<int> GeneratorFn(void) noexcept
{
    co_yield 1;
    co_yield 2;
    co_yield 3;
    co_yield 4;
    co_yield 5;
    co_yield 6;
    co_yield 7;
    co_yield 8;
    co_yield 9;
    co_return;
}

int main(int argc, char** args) noexcept
{
    auto Ret = GeneratorFn();
    for (auto&& i : Ret | std::views::take(2))
        std::cout << i << '\n';
    for (auto&& i : Ret | std::views::take(3))
        std::cout << i << '\n';
    for (auto&& i : Ret | std::views::take(4))
        std::cout << i << '\n';
}

The output of this code would be

1
2
4
5
6
8
9

and clearly, the 3 and 7 is missing. It seems like std::views::take drops the last value the generator yields.

Is this normal and to be expected in the formal version of C 23?
(Try online: https://godbolt.org/z/v6MModvaz)

CodePudding user response:

std::generator is an input_range, its begin() does not guarantee equality-preserving:

auto Ret = GeneratorFn();
std::cout << *Ret.begin() << "\n"; // 1
std::cout << *Ret.begin() << "\n"; // 2

When your first for-loop finishes, Ret's iterator has already incremented to the value 3. When you apply views::take to Ret in the second for-loop, this will call Ret's begin again, and the iterator return by begin will be the next value 4.

If you don't want to discard the value of the end iterator, you can reuse the last end iterator like this

auto Ret = GeneratorFn();
auto c = std::views::counted(Ret.begin(), 2);
for (auto i : c)
  std::cout << i << '\n';
for (auto i : std::views::counted(c.begin(), 3))
  std::cout << i << '\n';
for (auto i : std::views::counted(c.begin(), 4))
  std::cout << i << '\n';

Demo

  • Related