Reputation: 1057
I upgraded my compiler from gcc-4.4 to gcc-4.8 and one project fails miserably stemming from the following (false) assumptions:
#include <sstream>
#include <assert.h>
int main()
{
using namespace std;
istringstream iScan;
int num;
//iScan.unsetf(std::ios::skipws);
iScan.str("5678");
iScan >> num;
assert(iScan.tellg() == istringstream::pos_type(4));
assert(!iScan.fail());
assert(!iScan.good());
assert(iScan.eof());
assert(num == 5678);
assert(false && "We passed the above assertions.");
return 0;
}
On gcc-4.4, relevant assertions pass. On gcc-4.8, tellg() returns -1 and and fail() returns !false, apparently since it hit eof.
My target is MinGW 32-bit as shipped with Qt 5.1 (gcc-4.8).
Questions:
Further, various online compilers give different behavior. Is that a function of their lib?
Other similar but not duplicate questions:
The body of code, with these assumptions running through it all, is large.
Update: Here's a key part of the answer, which should work across all versions, all compilers:
// istream::tellg() is unreliable at eof(): works w/gcc-4.4, doesn't w/gcc-4.8.
#include <sstream>
#include <assert.h>
using namespace std;
typedef istream::pos_type pos_type;
pos_type reliable_tellg(istream &iScan)
{
bool wasEOF = iScan.eof();
if (wasEOF)
iScan.clear(iScan.rdstate() & ~ios::eofbit); // so tellg() works.
pos_type r = iScan.tellg();
if (wasEOF)
iScan.clear(iScan.rdstate() | ios::eofbit); // restore it.
return r;
}
int main()
{
istringstream iScan;
int num, n2;
//iScan.unsetf(std::ios::skipws);
iScan.str("5678");
assert(!iScan.eof() && !iScan.fail()); // pre-conditions.
assert(reliable_tellg(iScan) == pos_type(0));
iScan >> num;
assert(!iScan.fail());
assert(reliable_tellg(iScan) == pos_type(4));
assert(iScan.eof());
assert(reliable_tellg(iScan) == pos_type(4)); // previous calls don't bungle it.
assert(num == 5678);
iScan >> n2; // at eof(), so this should fail.
assert(iScan.fail());
assert(reliable_tellg(iScan) == pos_type(-1)); // as expected on fail()
assert(iScan.eof());
assert(false && "We passed the above assertions.");
return 0;
}
Upvotes: 4
Views: 1666
Reputation: 154007
The behavior you seem to expect is probably wrong. Both C++11
and C++03 start the description of tellg
with "Behaves as an
unformatted input function[...]". An "unformatted input
function" starts by constructing a sentry
object, and will
fail, doing nothing and returning a failure status, if the
sentry
object converts to false
. And the sentry
object
will convert to false
if the eofbit
is set.
The standard is slightly less clear about whether reading the
number sets the eofbit
, but only slightly (with the
information spread out over several different sections).
Basically, when inputting a numeric value, the stream (actually,
the num_get
facet) must read one character ahead, in order to
know where the number ends. In your case, it will see the end
of file when this occurs, and will thus set eofbit
. So your
first assert
will fail with a conforming implementation.
One could very easily consider this a defect in the standard, or unintentional. It's very easy to imagine some implementations doing the sensible thing (which is what you seem to expect), perhaps because the original implementors didn't realize the full implications in the standard (or unconsciously read it as they thought it should read). I would guess that this is the case for g++, and when they realized that their behavior was non-conformant, they fixed it.
As for work-arounds... I'm not sure what the real problem is,
that you're trying to work around. But I think that if you
clear the error bits before the tellg
, it should work. (Of
course, then iScan.good()
will be true
, and iScan.eof()
false
. But can this really matter?) Just be sure to check
that the extraction actually succeeded before you clear the
status.
Upvotes: 2