Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the expected behavior of tellg() when std::stringstream is constructed with ("some content", std::ios::in|std::ios::ate)?

I have the following piece of code that surprised me (using libstdc++4.8)...

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main() {
    std::string s("some content");
    std::stringstream ss(s, std::ios::in|std::ios::ate);
    std::istream& file = ss;
    //ss.clear(); Makes no difference...
    std::cout << "tellg() pos: " << file.tellg() << std::endl;
    return 0;
}

... which has the following output.

tellg() pos: 0

This behaviour is different to when using std::ifstream(std::ios::ate).

  • Is this behaviour correct/expected?
  • Does one need to explictly seekg(0, std::ios::end) despite opening with ate?
  • clearing the state doesn't make a difference.
  • Please note that the string has content.
like image 939
Werner Erasmus Avatar asked Apr 05 '16 13:04

Werner Erasmus


2 Answers

This is fully in-line with what standard tells us. Here are relevant details:

The version of constructor you've used:

Constructs a std::basic_stringbuf object by calling the default constructor of std::basic_streambuf, initializes the character sequence with an empty string, and sets the mode to which, than followed by initializing the associated character sequence as if by calling str(new_str).

Default constructor of basic_stringbuf is not interesting here, and than std::basic_stringbuf::str:

Deletes the entire underlying character sequence of this std::basic_stringbuf and then configures a new underlying character sequence containing a copy of the contents of s. ... For append streams (mode & ios_base::ate == true), pptr() == pbase() + s.size(), so that subsequent output will be appended to the last character copied from s

And finally, tellg(), which calls pubseekoff on the buffer:

If which includes ios_base::in and this buffer is open for reading (that is, if ((which & ios_base::in) == ios_base::in), then repositions the read pointer std::basic_streambuf::gptr: .. then newoff is the current position of the pointer (gptr()-eback() in this case)

To sum it up: since you didn't modify get position in any way (constructor only modifies put position), it returns 0.

like image 187
SergeyA Avatar answered Oct 02 '22 13:10

SergeyA


In short:

tellg() returns gptr()-eback() and providing the ios_base::in flag in the stringstream (and therefore the basic_stringbuf) constructor has the postcondition gptr() == eback().

Thus, 0 is expected / enforced.

In long:

  • tellg() returns rdbuf()->pubseekoff(0, std::ios_base::cur, std::ios_base::in)
  • rdbuf() returns const_cast<basic_stringbuf*>(&sb).
  • pubseekoff(0, std::ios_base::cur, std::ios_base::in) calls seekoff(0, std::ios_base::cur, std::ios_base::in)
  • seekoff returns gptr()-eback()
  • eback() returns a pointer to the beginning of the get area
  • gptr() returns the current get point

stringstream Initialization:

basic_stringstream (basic_string const &str, ios_base::openmode which);

Effects: Constructs an object of class basic_stringstream, initializing the base class with basic_iostream(&sb) and initializing sb with basic_stringbuf(str, which).

 

basic_stringbuf(basic_string const &str, ios_base::openmode which)

Effects: Constructs an object of class basic_stringbuf, initializing the base class with basic_streambuf(), and initializing mode with which. Then calls str(s).

 

void basic_stringbuf::str(const basic_string<charT,traits,Allocator>& s);

Effects: Copies the content of s into the basic_stringbuf underlying character sequence and initializes the input and output sequences according to mode.

Postconditions:

  • If mode & ios_base::out is true, pbase() points to the first underlying character and epptr() >= pbase() + s.size() holds;

  • if mode & ios_base::ate is true, pptr() == pbase() + s.size() holds, otherwise pptr() == pbase() is true.

  • If mode & ios_base::in is true, eback() points to the first underlying character, and both gptr() == eback() and egptr() == eback() + s.size() hold.

Where the last bit is relevant: providing ios_base::in has the postcondition gptr() == eback() and since tellg() returns gptr()-eback() the result is required to be zero.

like image 34
Pixelchemist Avatar answered Oct 02 '22 15:10

Pixelchemist