The return value of ostringstream::str seems to be depednent on what
the ostringstream object is constructed with (default vs. a string
construction). Here's the example code demonstrating the issue
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
void copy(istream & istr, ostream & ostr)
{
istr.clear();
ostr.clear();
istr.seekg(0);
ostr.seekp(0);
ostr << istr.rdbuf();
}
int main(int argc, char * argv[])
{
istringstream istr("passed");
ostringstream ostr
#if 1
("failed")
#endif
;
string line;
while(getline(cin, line))
{
copy(istr, ostr);
cout << "test " << ostr.str() << endl;
}
return 0;
}
The output should be "test passed" everytime a line is pressed, no
matter what. However, with the implicit constructor of "ostr", the
output is "test " every time. Passing in an initial string changes
whether or not ostringstream::str() ALWAYS returns an empty string,
despite data being streamed to it after construction. Is this a broken
implementation (gcc 3.4.6)?
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
lindahlb (26)
|
10/4/2006 9:25:17 PM |
|
lindahlb@hotmail.com wrote:
> The return value of ostringstream::str seems to be depednent
> on what the ostringstream object is constructed with (default
> vs. a string construction).
Just a co�ncidence. The code has undefined behavior.
> Here's the example code demonstrating the issue
> #include <iostream>
> #include <fstream>
> #include <sstream>
> using namespace std;
> void copy(istream & istr, ostream & ostr)
> {
> istr.clear();
> ostr.clear();
> istr.seekg(0);
> ostr.seekp(0);
When calling the single argument versions of seek[pg] on an
[io]stringstream, the argument must be the return value of a
previous call to a seek or a tell function, otherwise, the
behavior is undefined. The seek functions you want are the two
argument variants:
istr.seekg( 0, ios::beg ) ;
ostr.seekp( 0, ios::beg ) ;
In C, fpos_t is not even convertible from an int. The pos_type
in C++ is the logical equivalent. For some reason, C++ makes it
convertible---and even defines arithmetic on it---while at the
same time saying that using the results of the conversion or the
arithmetic is undefined behavior. A situation which, of course,
leads to no end of confusion.
In general, if you want to go to the beginning or the end of a
stream, use the two argument version of seek, with 0 as the
first argument. If you want to note the position where you are,
and come back to it later, use tell and the one argument version
of seek. For anything else, it depends on the stream type and
state, and in general, may not work. (Seeking to an arbitrary
position in a file, for example, only works if the file was
opened in binary mode, and if the OS supports the
operation---under Unix, for example, you read the keyboard using
a ifstream, and a seek is not going to work, binary mode or
not.)
Since what you are doing is undefined behavior, anything g++
does is correct. In this case, it refuses the seek (with an
error) if the string is empty, and not otherwise. And once an
error has been set, the stream is inactive until the next clear,
so the writing to it never takes place.
(That's with regards to your program. I tried it saving the
results of tellp() of the just constructed empyt ostringstream
in a global variable, and positionning to it, and I still get an
error. And that IS an error in the implementation.)
> ostr << istr.rdbuf();
Since the seekp failed, ostream is in an error state, and the <<
operation doesn't do anything.
> }
> int main(int argc, char * argv[])
> {
> istringstream istr("passed");
> ostringstream ostr
> #if 1
> ("failed")
> #endif
> ;
> string line;
> while(getline(cin, line))
> {
> copy(istr, ostr);
> cout << "test " << ostr.str() << endl;
> }
> return 0;
> }
> The output should be "test passed" everytime a line is
> pressed, no matter what.
Which it is if you replace the seekp(0) with seekg(0,ios::beg).
> However, with the implicit constructor of "ostr", the
> output is "test " every time.
Formally, you haven't a leg to stand on, since your code has
undefined behavior. Practically, I'd say that there is a bug in
the g++ implementation, in that it sets an error if the position
you seek to is end of file. Even if this position is the result
of a call to tellp().
> Passing in an initial string changes whether or not
> ostringstream::str() ALWAYS returns an empty string, despite
> data being streamed to it after construction.
Because the beginning of the file is no longer end of file.
> Is this a broken implementation (gcc 3.4.6)?
Yes, although your exact program doesn't show it, because you
formally have undefined behavior. (In fact, ostr.tellp()
returns 0, so there is no way that seekp(0) can tell that you
didn't really get the 0 from there. But this is not
guaranteed.)
With regards to your program, if what you want to do is seek to
the beginning of the file, you should use the two argument form
of seek, as explained above. However, I think an error report
to g++ is also in order, and have submitted one. (FWIW: VC++
8.0 seems to have a related problem; in VC++, a tell on an empty
ostringstream fails.)
--
James Kanze GABI Software
Conseils en informatique orient�e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S�mard, 78210 St.-Cyr-l'�cole, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
kanze
|
10/5/2006 1:32:07 PM
|
|
lindahlb@hotmail.com ha scritto:
>
> The output should be "test passed" everytime a line is pressed, no
> matter what. However, with the implicit constructor of "ostr", the
> output is "test " every time. Passing in an initial string changes
> whether or not ostringstream::str() ALWAYS returns an empty string,
> despite data being streamed to it after construction. Is this a broken
> implementation (gcc 3.4.6)?
>
This program has undefined behavior, so whatever output you get is
correct. The sources of undefined-ness are the two calls to seekg/seekp.
In fact they both eventually call basic_stringbuf::seekpos, and its
definition requires that the position must have been obtained by "a
previous successful call to one of the positioning functions(seekoff,
seekpos, tellg, tellp)" otherwise the effect is undefined
(�27.7.1.3/14). As you are passing 0, the requirement has not been
satisfied.
How do you fix that? Two ways:
1) (recommended) use the two-parameter forms of seekp and seekg (which
eventually call seekoff instead of seekpos), like this:
istr.seekg(0, ios_base::beg);
ostr.seekp(0, ios_base::beg);
2) use a tellp/tellg to get pos_type to the beginning of the stream and
use that:
in function main, just after the creation of streams:
istream::pos_type istart = istr.tellp();
ostream::pos_type ostart = ostr.tellg();
in function copy:
istr.seekg(istart);
ostr.seekp(ostart);
If you try any of this two approaches you get well-defined behavior,
but... well, the result will be *exactly* the same as the one you
described! (Which therefore is the *correct* result!)
That's because when you initialize ostr with the default constructor, no
array will be allocated (27.7.1.1/2). The absence of an output sequence
makes seekoff() on ostr fail (27.7.1.3/11). So according to which of the
two approaches above you followed, either the call to seekp(0, beg) or
the call to tellp() fail (both these calls eventually call seekoff()).
Morale: don't try to reposition an empty string-based stream.
Regards,
Ganesh
PS: According to my interpretation, using the non-default constructor
with empty string like this:
ostringstream ostr("");
should initialize the output sequence to an empty sequence, therefore
both seekp(0, beg) and tellp() should work. However, at least one
implementation (VC++ 7.1) still refuses to initialize the output
sequence, so you'd better not rely on this corner case.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
10/5/2006 6:19:46 PM
|
|
Alberto Ganesh Barbati wrote:
> lindahlb@hotmail.com ha scritto:
> > The output should be "test passed" everytime a line is pressed, no
> > matter what. However, with the implicit constructor of "ostr", the
> > output is "test " every time. Passing in an initial string changes
> > whether or not ostringstream::str() ALWAYS returns an empty string,
> > despite data being streamed to it after construction. Is this a broken
> > implementation (gcc 3.4.6)?
> This program has undefined behavior, so whatever output you get is
> correct. The sources of undefined-ness are the two calls to seekg/seekp.
> In fact they both eventually call basic_stringbuf::seekpos, and its
> definition requires that the position must have been obtained by "a
> previous successful call to one of the positioning functions(seekoff,
> seekpos, tellg, tellp)" otherwise the effect is undefined
> (�27.7.1.3/14). As you are passing 0, the requirement has not been
> satisfied.
This is true, but...
In at least one implementation (g++), and probably a lot of
others, tellg() will return 0. (Or rather something that will
convert to zero, and to which 0 will convert.)
> How do you fix that? Two ways:
[...]
> 2) use a tellp/tellg to get pos_type to the beginning of the stream and
> use that:
> in function main, just after the creation of streams:
> istream::pos_type istart = istr.tellp();
> ostream::pos_type ostart = ostr.tellg();
> in function copy:
> istr.seekg(istart);
> ostr.seekp(ostart);
> If you try any of this two approaches you get well-defined
> behavior, but... well, the result will be *exactly* the same
> as the one you described! (Which therefore is the *correct*
> result!)
No it's not. She'll get the same results, but only due to a bug
in the library. (Well, she'll also get the same results because
g++ returns 0 from the tellp(), so it cannot distinguish whether
her 0 came from tellp() or not, and so must handle it
correctly.) You can still legally position at the start of an
empty stream. In general, positionning at the end of file
position is legal; why should this be an exception. (FWIW: g++
has accepted it as an error.)
> That's because when you initialize ostr with the default
> constructor, no array will be allocated (27.7.1.1/2).
I don't see this in my copy of the standard (the latest draft).
The cited paragraph says simply that there is a post-condition
of `str() == ""'.
> The absence of an output sequence makes seekoff() on ostr fail
> (27.7.1.3/11).
That looks like a bug in the standard. Seeking should obey the
same half-open interval semantics as are used in the STL. You
can seek to one past the end of the sequence, but you cannot
read from the position. (This is the way a file system seek has
worked on all non-Unix systems I've worked on. Unix has
somewhat wierd semantics for seeking beyond the end of file.)
> So according to which of the two approaches above you
> followed, either the call to seekp(0, beg) or the call to
> tellp() fail (both these calls eventually call seekoff()).
> Morale: don't try to reposition an empty string-based stream.
In other words, you're saying that you cannot risk a seek if you
don't know whether the sequence is empty or not. If that's the
case, it's an error in the standard.
--
James Kanze GABI Software
Conseils en informatique orient�e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S�mard, 78210 St.-Cyr-l'�cole, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
kanze
|
10/6/2006 12:18:47 PM
|
|
kanze ha scritto:
> Alberto Ganesh Barbati wrote:
>
>> If you try any of this two approaches you get well-defined
>> behavior, but... well, the result will be *exactly* the same
>> as the one you described! (Which therefore is the *correct*
>> result!)
>
> No it's not. She'll get the same results, but only due to a bug
> in the library. (Well, she'll also get the same results because
> g++ returns 0 from the tellp(), so it cannot distinguish whether
> her 0 came from tellp() or not, and so must handle it
> correctly.) You can still legally position at the start of an
> empty stream. In general, positionning at the end of file
> position is legal; why should this be an exception. (FWIW: g++
> has accepted it as an error.)
There seems to be a significant difference between the current standard
and the draft. In C++03, paragraph 27.7.1.3/11 says:
"For a sequence to be positioned, if its next pointer (either gptr() or
pptr()) is a null pointer, the positioning operation fails."
while paragraph 27.7.1.3/10 in the draft says (emphasis added):
"For a sequence to be positioned, if its next pointer (either gptr() or
pptr()) is a null pointer *and the new offset newoff is nonzero*, the
positioning operation fails."
So, the library shipped with VC++ 7.1 conforms to the current standard,
but it doesn't conform with the draft.
>
>> That's because when you initialize ostr with the default
>> constructor, no array will be allocated (27.7.1.1/2).
>
> I don't see this in my copy of the standard (the latest draft).
> The cited paragraph says simply that there is a post-condition
> of `str() == ""'.
Also that statement has changed in the draft. In C++03, the paragraph
27.7.1.1/2 literally says "The function allocates no array object."
Anyway, the new wording is even less clear than the old one, IMHO,
because it doesn't say anything about the value of gptr()/pptr(): shall
they be null or point to an array or length zero? The old wording hinted
(although it wasn't explicit either) that they should be null. The
difference is relevant, because seekoff() makes explicit reference of
the value of gptr()/pptr() as we say above.
>
>> Morale: don't try to reposition an empty string-based stream.
>
> In other words, you're saying that you cannot risk a seek if you
> don't know whether the sequence is empty or not. If that's the
> case, it's an error in the standard.
>
It was an error in the current standard. It isn't in the draft. However
I would add either remove the checks about the value of gptr()/pptr()
from paragraph 27.7.1.3/10 (as they seems to be redundant anyway) or add
to the postcondition of the default constructor in 27.7.1.1/2 that
pptr() and gptr() must be null.
Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
10/7/2006 12:58:11 AM
|
|
"Alberto Ganesh Barbati" <AlbertoBarbati@libero.it> wrote in message
news:eJBVg.138318$_J1.903261@twister2.libero.it...
> .....
> There seems to be a significant difference between the current standard
> and the draft. In C++03, paragraph 27.7.1.3/11 says:
>
> "For a sequence to be positioned, if its next pointer (either gptr() or
> pptr()) is a null pointer, the positioning operation fails."
>
> while paragraph 27.7.1.3/10 in the draft says (emphasis added):
>
> "For a sequence to be positioned, if its next pointer (either gptr() or
> pptr()) is a null pointer *and the new offset newoff is nonzero*, the
> positioning operation fails."
>
> So, the library shipped with VC++ 7.1 conforms to the current standard,
> but it doesn't conform with the draft.
Yep. We (and others) requested that change because it's stupid for
stringstream to be obliged to fail an innocuous positioning request
before anything has been written to it. V7.1 would have matched the
new draft were it not for a validation suite or two that went gunning
for this issue and disallowed sensible behavior.
P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
P
|
10/7/2006 3:22:39 PM
|
|
|
5 Replies
120 Views
(page loaded in 0.128 seconds)
|