moooeeeep
moooeeeep

Reputation: 32532

At what point and how to forward position in stream-like class?

I have written a small class that implements a stream-like behavior (e.g., like std::cin).

In order to implement the "consumption" of elements I increment the current element, which I later compare to a sentinel object to check for stream exhaustion.

Originally I wanted to "consume" (increment) the produced items directly at the time of the actual retrieval. But it turned out that then, the following check for termination would already mark the last produced element as invalid. Which leaves me with an off-by-one error.

I then moved to incrementing in the check function, after the comparison was done. This I find rather suboptimal, because a check is usually not expected to mutate the thing being checked. Therefore, I'd like to ask: Is there a way to implement this, performing the incrementing directly inside operator>>() ?

Here's the code:

#include <iostream>

template <typename T>
struct Range {
    T next, end;
    Range(T next, T end) : next(next), end(end) {}
    Range& operator>>(T & out) {
        out = next++; // <-- intuitively I find it cleanest to increment here
        return *this;
    }
    operator void*() {
        return next < end ? this : 0;
    }
};

int main() {
    Range<int> r(1, 5); // should produce values in [1,5)
    int i;
    while (r >> i) std::cout << i << '\n';
}

Here's the output:

$ g++ test.cc && ./a.out
1
2
3

As you can see, it's off by one.

Upvotes: 1

Views: 56

Answers (1)

moooeeeep
moooeeeep

Reputation: 32532

This is relatively easy to implement by adding an additional variable.

Here a flag is added that is true when the last retrieved item was valid. Then both incrementing and comparison can already be done within operator>>().

#include <iostream>

template <typename T>
struct Range {
    T next, end;
    bool good; // <-- flag that states whether the last retrieved item was valid
    Range(T next, T end) : next(next), end(end), good() {}
    Range& operator>>(T & out) {
        out = next;
        good = next++ < end;
        return *this;
    }
    operator void*() {
        return good ? this : 0;
    }
};

int main() {
    Range<int> r(1, 5);
    int i;
    while (r >> i) std::cout << i << '\n';
}

Upvotes: 2

Related Questions