John
John

Reputation: 3534

Why does `std::istream::seekg()` affect the behavior of `std::istreambuf_iterator`?

Why does std::istream::seekg() affect the behavior of std::istreambuf_iterator?

I carefully read the introduction about std::istreambuf_iterator on cppreference, but there is no direct answer for this matter.

Here is the code snippet:

#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
 
int main()
{
    //with seekg to the end
    {
        std::cout << "#1 with seekg" << std::endl;
        std::istringstream in{"Hello, world"};
        in.seekg(std::istream::end);
        std::istreambuf_iterator<char> it{in}, end;
        std::string ss{it, end};
        std::cout << ss << std::endl;
    }

    //without seekg
    {
        std::cout << "#2 without seekg" << std::endl;
        std::istringstream in{"Hello, world"};
        std::istreambuf_iterator<char> it{in}, end;
        std::string ss{it, end};
        std::cout << ss << std::endl;
    }
    
    //seekg to end and then to begin
    {
        std::cout << "#3 seekg to end and then to begin" << std::endl;
        std::istringstream in{"Hello, world"};
        in.seekg(std::istream::end);
        std::streampos pos = in.tellg();
        std::cout << "len:" << static_cast<long long>(pos) << std::endl;
        in.seekg(std::istream::beg);
        std::istreambuf_iterator<char> it{in}, end;
        std::string ss{it, end};
        std::cout << ss << std::endl;
    }
}

Here is the output:

#1 with seekg
llo, world
#2 without seekg
Hello, world
#3 seekg to end and then to begin
len:2
Hello, world

I think the the ouput should be nothing or Hello, world for the fist case while the ouput is llo,world.

Upvotes: 1

Views: 113

Answers (2)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 123114

std::iostream::seekdir which is actually std::ios_base::seekdir is an implementation defined value of implementation defined type aliased as seekdir.

It is meant to be passed as second parameter of the std::basic_istream<CharT,Traits>::seekg overload that takes two parameters.

You have bad luck and seekdir can be passed also to the overload that accepts 1 parameter. I did not find a gcc warning level that catches this.

As mentioned in comments, from the output we can infer that the value of std::istream::end is 2. The position of the stream is moved 2 steps forward. And what you probably wanted instead is

in.seekg(0, std::istream::end);

To move the cursor to the end of the input stream.

Upvotes: 4

Bohdan Lakatosh
Bohdan Lakatosh

Reputation: 206

std::basic_istream::seekg() moves input position indicator to the given position. According to the documentation the position is set by a numeric value.

The std::istreambuf_iterator, when it is created for the given istream, will point to the current input position. So since seekg() modifies the input position value for the given istream, its call before creating iterator can have some affect on the iterator.

Now let's answer the question in the end of your post: why don't you observe empty string or full string in your code?

According to documentation seekg() has two overloads:

  1. it can take as an argument single numeric value, which represents an desired position relative to the beginning of the stream.
  2. it can take as an argument numeric offset and some flag value to indicate the direction of that offset. This direction can be specified by std::ios_base::beg, std::ios_base::end, std::ios_base::curr values.

Now, in your code you pass only one argument to seekg() method. Thus you invoke the first overload. But you pass to it some std::istream::end, which is, I assume, std::ios_base::end value, somehow imported into std::istream namespace. And you are lucky enough, so that its value is (or can be implicitly converted to) integer with value 2.

So basically in your code you write in.seekg(2), which results in moving the input position of the stream to the third character in your string (the character with index 2). As a result llo, world is printed.

In order to move the input position to the end of the buffer use in.seekg(0, std::ios_base::end); In order to move the position to the beginning of the buffer use in.seekg(0, std::ios_base::beg); or in.seekg(0);

By the way, your third example, where you moved input position "to the end and back" worked because std::istream::beg value is 0. So effectively there you did call in.seekg(0) before creating iterator and it read the string from its start.

Upvotes: 3

Related Questions