std::istream::tellg vs eofbit flag

Discussion in 'C++' started by mathieu, Jul 25, 2013.

  1. mathieu

    mathieu Guest

    Hi,

    I'd like to check if my understanding of the standard is correct. Is the following program supposed to work ?

    $ cat file.cxx
    #include <cassert>
    #include <fstream>
    #include <sstream>

    int main ()
    {
    std::ifstream ifs (__FILE__, std::ios::binary);
    std::stringstream ss;

    assert (0 == ifs.tellg ());

    ifs >> ss.rdbuf ();

    assert (ifs.eofbit == ifs.rdstate ());
    assert (ifs.eof());
    assert (-1 != ifs.tellg ()); // works with g++ 4.4

    return 0;
    }

    I am getting different results using either g++ 4.4 or g++ 4.7.

    Thanks.
    mathieu, Jul 25, 2013
    #1
    1. Advertising

  2. On 7/25/2013 12:41 PM, mathieu wrote:
    > I'd like to check if my understanding of the standard is correct. Is the following program supposed to work ?
    >
    > $ cat file.cxx
    > #include <cassert>
    > #include <fstream>
    > #include <sstream>
    >
    > int main ()
    > {
    > std::ifstream ifs (__FILE__, std::ios::binary);


    The macro __FILE__ does not necessarily expand into a valid file name in
    the environment where the resulting binary is going to run, you realize
    that, yes?

    > std::stringstream ss;
    >
    > assert (0 == ifs.tellg ());
    >
    > ifs >> ss.rdbuf ();
    >
    > assert (ifs.eofbit == ifs.rdstate ());
    > assert (ifs.eof());
    > assert (-1 != ifs.tellg ()); // works with g++ 4.4
    >
    > return 0;
    > }
    >
    > I am getting different results using either g++ 4.4 or g++ 4.7.


    Different in what way?

    'tellg' returns pos_type(-1) to indicate *failure*. Are you sure
    reading out the entire file is supposed to set the failbit? If you try
    *reading* after the 'eof' condition has been achieved, *then* you get a
    failure (because that input operation fails), and *then* 'tellg' should
    return pos_type(-1). That's my take on it.

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Jul 25, 2013
    #2
    1. Advertising

  3. mathieu

    mathieu Guest

    On Thursday, July 25, 2013 7:05:00 PM UTC+2, Victor Bazarov wrote:
    > On 7/25/2013 12:41 PM, mathieu wrote:
    >
    > > I'd like to check if my understanding of the standard is correct. Is the following program supposed to work ?

    >
    > >

    >
    > > $ cat file.cxx

    >
    > > #include <cassert>

    >
    > > #include <fstream>

    >
    > > #include <sstream>

    >
    > >

    >
    > > int main ()

    >
    > > {

    >
    > > std::ifstream ifs (__FILE__, std::ios::binary);

    >
    >
    >
    > The macro __FILE__ does not necessarily expand into a valid file name in
    >
    > the environment where the resulting binary is going to run, you realize
    >
    > that, yes?
    >
    >
    >
    > > std::stringstream ss;

    >
    > >

    >
    > > assert (0 == ifs.tellg ());

    >
    > >

    >
    > > ifs >> ss.rdbuf ();

    >
    > >

    >
    > > assert (ifs.eofbit == ifs.rdstate ());

    >
    > > assert (ifs.eof());

    >
    > > assert (-1 != ifs.tellg ()); // works with g++ 4.4

    >
    > >

    >
    > > return 0;

    >
    > > }

    >
    > >

    >
    > > I am getting different results using either g++ 4.4 or g++ 4.7.

    >
    >
    >
    > Different in what way?
    >
    >
    >
    > 'tellg' returns pos_type(-1) to indicate *failure*. Are you sure
    >
    > reading out the entire file is supposed to set the failbit? If you try
    >
    > *reading* after the 'eof' condition has been achieved, *then* you get a
    >
    > failure (because that input operation fails), and *then* 'tellg' should
    >
    > return pos_type(-1). That's my take on it.


    Not according to cplusplus.com:

    http://www.cplusplus.com/reference/istream/istream/tellg/

    "Notice that the function will work even if the eofbit flag is set before the call."

    Hence, my question...
    mathieu, Jul 26, 2013
    #3
  4. On 7/26/2013 2:37 AM, mathieu wrote:
    > On Thursday, July 25, 2013 7:05:00 PM UTC+2, Victor Bazarov wrote:
    >> On 7/25/2013 12:41 PM, mathieu wrote:
    >>
    >>> I'd like to check if my understanding of the standard is correct. Is the following program supposed to work ?

    >>
    >>>

    >>
    >>> $ cat file.cxx

    >>
    >>> #include <cassert>

    >>
    >>> #include <fstream>

    >>
    >>> #include <sstream>

    >>
    >>>

    >>
    >>> int main ()

    >>
    >>> {

    >>
    >>> std::ifstream ifs (__FILE__, std::ios::binary);

    >>
    >>
    >>
    >> The macro __FILE__ does not necessarily expand into a valid file name in
    >>
    >> the environment where the resulting binary is going to run, you realize
    >>
    >> that, yes?
    >>
    >>
    >>
    >>> std::stringstream ss;

    >>
    >>>

    >>
    >>> assert (0 == ifs.tellg ());

    >>
    >>>

    >>
    >>> ifs >> ss.rdbuf ();

    >>
    >>>

    >>
    >>> assert (ifs.eofbit == ifs.rdstate ());

    >>
    >>> assert (ifs.eof());

    >>
    >>> assert (-1 != ifs.tellg ()); // works with g++ 4.4

    >>
    >>>

    >>
    >>> return 0;

    >>
    >>> }

    >>
    >>>

    >>
    >>> I am getting different results using either g++ 4.4 or g++ 4.7.

    >>
    >>
    >>
    >> Different in what way?


    ....and you didn't answer this question.

    >>
    >>
    >>
    >> 'tellg' returns pos_type(-1) to indicate *failure*. Are you sure
    >>
    >> reading out the entire file is supposed to set the failbit? If you try
    >>
    >> *reading* after the 'eof' condition has been achieved, *then* you get a
    >>
    >> failure (because that input operation fails), and *then* 'tellg' should
    >>
    >> return pos_type(-1). That's my take on it.

    >
    > Not according to cplusplus.com:


    What is the meaning of "not" in your statement here? Somehow
    cplusplus.com has something to say about /my take on/ how tellg
    operates? Or is there something in its explanation that contradicts
    what I wrote on how tellg works (or supposed to, anyway)?

    >
    > http://www.cplusplus.com/reference/istream/istream/tellg/
    >
    > "Notice that the function will work even if the eofbit flag is set before the call."


    Yes. Do you realize that 'eofbit' and 'badbit' are not the same? When
    we say "failure", it means the 'badbit' is set. 'eofbit' is only set
    when the reading *reaches* the end of the stream.

    >
    > Hence, my question...


    So, to answer your question, yes, the program is supposed to work. If
    it does not (it would be nice to see your explanation of what "does not"
    is here), then it is likely due to a bug in the implementation of the
    standard I/O library that you're using. <shrug>

    The function 'tellg' is supposed to return rdbuf()->pubseekoff(0, cur,
    in), if the failbit is *not* set (i.e. fail() == false). If the failbit
    *is* set (indicating the failure of the last operation) *then* it
    returns -1.

    Instead of asserting on 'tellg', see if you get the 'fail()' to return
    'false' in that case (isn't that what you suppose should hold in your
    situation?) and then we could start trying to understand what *exactly*
    is going wrong in one of your programs.

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Jul 26, 2013
    #4
  5. mathieu

    James Kanze Guest

    On Friday, July 26, 2013 2:01:14 PM UTC+1, Victor Bazarov wrote:
    > On 7/26/2013 2:37 AM, mathieu wrote:


    > > On Thursday, July 25, 2013 7:05:00 PM UTC+2, Victor Bazarov wrote:
    > >> On 7/25/2013 12:41 PM, mathieu wrote:
    > >>> I'd like to check if my understanding of the standard is correct. Is the following program supposed to work ?
    > >>> $ cat file.cxx
    > >>> #include <cassert>
    > >>> #include <fstream>
    > >>> #include <sstream>


    > >>> int main ()
    > >>> {
    > >>> std::ifstream ifs (__FILE__, std::ios::binary);


    > >> The macro __FILE__ does not necessarily expand into a valid file name in
    > >> the environment where the resulting binary is going to run, you realize
    > >> that, yes?


    > >>> std::stringstream ss;
    > >>> assert (0 == ifs.tellg ());
    > >>> ifs >> ss.rdbuf ();
    > >>> assert (ifs.eofbit == ifs.rdstate ());
    > >>> assert (ifs.eof());


    I'm not sure that the two asserts above are guaranteed to pass
    (although it's hard to imagine an implementation where they
    didn't).

    > >>> assert (-1 != ifs.tellg ()); // works with g++ 4.4
    > >>> return 0;
    > >>> }


    > >>> I am getting different results using either g++ 4.4 or g++ 4.7.


    The requirements have changed between C++03 and C++11. C++03
    requires std::istream::seekg to fail if eofbit is set. C++11
    says that eofbit will first be reset, before attempting the
    seek. (I believe that the behavior specified by C++11 was what
    was wanted from the beginning, but C++03 definitly says that
    seekg should fail if eofbit is set.)

    [...]
    > > http://www.cplusplus.com/reference/istream/istream/tellg/


    > > "Notice that the function will work even if the eofbit flag
    > > is set before the call."


    Only in C++11. The language has changed in this regard.

    > Yes. Do you realize that 'eofbit' and 'badbit' are not the same? When
    > we say "failure", it means the 'badbit' is set.


    No. When we say "failure", we mean that either 'failbit' or
    'badbit' are set. Most implementations of istream will never
    set 'badbit'. (They should, in case there is a real hardware
    read error, but they don't.)

    > 'eofbit' is only set
    > when the reading *reaches* the end of the stream.


    More correctly, 'eofbit' is only set when the standard says it
    should be set. In general, I would expect it to be set whenever
    streambuf::sgetc, streambuf::sbumpc or streambuf::snextc returns
    end of file. But the standard is fairly vague about this; it
    doesn't mention setting eofbit in the description of operator>>
    to a streambuf. One could argue that this means that eofbit
    shouldn't be set. (One could also argue that this is an
    oversight. C++03 also says that this >> behaves as a formatted
    input function, which means that it should skip leading
    whitespace. It didn't in classical iostreams, and this has been
    corrected in C++11. Perhaps not setting eofbit is a similar
    oversight, which just hasn't been corrected yet.)

    > > Hence, my question...


    > So, to answer your question, yes, the program is supposed to work. If
    > it does not (it would be nice to see your explanation of what "does not"
    > is here), then it is likely due to a bug in the implementation of the
    > standard I/O library that you're using. <shrug>


    His program is based on unspecified behavior at one point, and
    behavior which has changed between C++03 and C++11 at another.
    My guess is that g++ 4.4 is implementing exactly what is
    specified in C++03, and that g++ 4.7 is implementing the C++11
    behavior, considering the change as a "correction", and so not
    requiring -std=c++11 to get it.

    --
    James
    James Kanze, Jul 28, 2013
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Fred Ma
    Replies:
    2
    Views:
    585
    Fred Ma
    May 24, 2004
  2. Gernot Frisch

    from std::string to std::istream?

    Gernot Frisch, Mar 17, 2005, in forum: C++
    Replies:
    4
    Views:
    14,287
    Victor Bazarov
    Mar 18, 2005
  3. Jason K
    Replies:
    6
    Views:
    3,986
    Jeff Flinn
    May 12, 2005
  4. Ian Collins
    Replies:
    0
    Views:
    634
    Ian Collins
    Nov 13, 2009
  5. xmllmx
    Replies:
    5
    Views:
    587
    Jorgen Grahn
    Jun 15, 2010
Loading...

Share This Page