When a new ostringstream object is created but nothing has been written to it yet, the tellp() method returns an offset of -1, instead of 0. Example program: #include <string> #include <iostream> #include <fstream> #include <sstream> int main ( int argc, const char * const * argv, const char * const * envp ) { std::ostringstream Stream1; std::ofstream Stream2("junk.tmp"); #if 0 Stream1 << " "; Stream2 << " "; #endif std::cout << "sstream.tellp() = " << Stream1.tellp() << ", fstream.tellp() = " << Stream2.tellp() "test.cpp" 27L, 434C written $ g++ -o test test.cpp $ less test.cpp #include <string> #include <iostream> #include <fstream> #include <sstream> int main ( int argc, const char * const * argv, const char * const * envp ) { std::ostringstream Stream1; std::ofstream Stream2("junk.tmp"); #if 0 Stream1 << " "; Stream2 << " "; #endif std::cout << "sstream.tellp() = " << Stream1.tellp() << ", fstream.tellp() = " << Stream2.tellp() << std::endl; return 0; } /*main*/ Output from above: sstream.tellp() = -1, fstream.tellp() = 0 If you change the "#if 0" line to "#if 1", the output becomes sstream.tellp() = 1, fstream.tellp() = 1
still happens on the mainline (20030525): tin:~/src/gnu/gcctest>g++ pr10975.cc tin:~/src/gnu/gcctest>./a.out sstream.tellp() = -1, fstream.tellp() = 0
The -1 that's being received from tellp() is happening because of what's being given back by pubseekoff(); that, in turn, is giving the correct value. In pubseekoff() it calls this->seekoff(), which in sstream.tcc sets up to only do anything if its _M_string.capacity() is > 0. template <class _CharT, class _Traits, class _Alloc> typename basic_stringbuf<_CharT, _Traits, _Alloc>::pos_type basic_stringbuf<_CharT, _Traits, _Alloc>:: seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode __mode) { pos_type __ret = pos_type(off_type(-1)); //... if (_M_string.capacity() && (__testin || __testout || __testboth)) { //... } return __ret; } Otherwise the method goes out with the default pos_type value of -1. (see below for a note about this) To figure out why we might not have anything allocated for the basic_string object being used here, we can to dive head-first into the standard to find out about the ostringstream object we're using. In $27.7.3 it says that basic_ostringstream uses a basic_stringbuf object to control the associated storage. Looking up basic_stringbuf, in $27.7.1.1.2/1 it notes that the basic_stringbuf default ctor "allocates no array object". Thus by default, the capacity of the string will be 0. As such, since there's no space yet for seekoff() to move inside the basic_stringbuf, it gives back the -1 value which tellp() relays to the call in the testcase. Thus I believe the library's actually behaving correctly in this example. Hope this helps, B P.S. The returning of -1 isn't precisely what's said in the current published standard. The library working group's Defect Report 55 as seen in http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-defects.html#55 adjusts the standard after the fact to say in 27.7.1.3 paragraph 13 about basic_stringbuf's seekoff() method: the return value is pos_type(off_type(-1)) instead of the object stores an invalid stream position That DR was just a clarification across the board, and isn't really a part of this issue.
brendan@zen.org wrote: >As such, since there's no space yet for seekoff() to move inside the >basic_stringbuf, it gives back the -1 value which tellp() relays to the call >in the testcase. That's an implementation issue. I don't see why such implementation issues should cause a difference in the semantics. Consider: the ostream object is fully initialized and ready for use. Nothing has been written to it yet. Therefore the output position should be 0. This is true if output is going to a file, why shouldn't it be true for output going to a string?
(In reply to comment #3) > That's an implementation issue. I don't see why such implementation > issues should cause a difference in the semantics. Consider: the > ostream object is fully initialized and ready for use. Nothing has been > written to it yet. Therefore the output position should be 0. This is true if > output is going to a file, why shouldn't it be true for output going to a > string? Unfortunately this isn't an implementation issue. Consider the following according to the standard: For ofstream: ofstream os("junk"); os.tellp(); 1) ofstream(const char*) constructs a basic_filebuf and opens "junk" 2) ofstream::tellp() returns rdbuf()->pubseekoff(0, cur, out) 3) basic_streambuf::pubseekoff(0, cur, out) returns basic_filebuf::seekoff(0, cur, out) 4) basic_filebuf::seekoff(0, cur, out) does: width = codecvt.encoding() if !is_open() return -1 std::fseek(file, 0, cur) return new position [which is 0] Now consider ostringstream: ostringstream os; os.tellp(); 1) ostringstream() constructs basic_stringbuf. 2) basic_stringbuf() constructs basic_streambuf but allocates no array object and sets pointers to null. 3) ostringstream::tellp() returns basic_streambuf::pubseekoff(0, cur, out) 4) basic_streambuf::pubseekoff(0, cur, out) returns basic_stringbuf::seekoff(0, cur, out) 5) basic_stringbuf::seekoff(0, cur, out) does: if pptr() == null return -1 Since the basic_stringbuf has no array object yet and its pointers are null, basic_streambuf::pptr() returns null, and the final return value must be -1. FWIW, I agree this is less than intuitive, but seems correct according to the standard.
This is pretty clearly a defect in the standard. I have asked Matt Austern to open an issue on the Library Working Group issues list. We probably should keep this open until that issue has been looked at by the LWG. I'd like to Do The Right Thing as soon as it looks like we can justify it.
Suspending while a DR is being filed and open.
unsuspended on accident.
I can reconfirm this bug is still present in gcc 3.3.1 running on Cygwin. I think a related problem: It turns out tellp() remains negative if the program opens a non-existant file with ios::trunc|ios::in|ios::out|ios::bin, then did a few binary writes. Very annoying.
This is now DR 453 (Open): http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#453
Subject: Bug 10975 CVSROOT: /cvs/gcc Module name: gcc Changes by: paolo@gcc.gnu.org 2004-09-30 17:23:11 Modified files: libstdc++-v3 : ChangeLog libstdc++-v3/include/bits: sstream.tcc libstdc++-v3/docs/html/ext: howto.html libstdc++-v3/testsuite/27_io/basic_istream/tellg/char: 1.cc libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char: 1.cc 2.cc Added files: libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char: 2346-fstream.cc 2346-sstream.cc libstdc++-v3/testsuite/27_io/basic_stringbuf/seekoff/char: 10975.cc libstdc++-v3/testsuite/27_io/basic_stringbuf/seekoff/wchar_t: 10975.cc Removed files: libstdc++-v3/testsuite/27_io/basic_istream/seekg/char: 2346-fstream.cc 2346-sstream.cc Log message: 2004-09-30 Paolo Carlini <pcarlini@suse.de> PR libstdc++/10975 (DR 453) * include/bits/sstream.tcc (seekoff): Don't fail if __beg == 0 and __off == 0. * docs/html/ext/howto.html: Add an entry for DR 453. * testsuite/27_io/basic_stringbuf/seekoff/char/10975.cc: New. * testsuite/27_io/basic_stringbuf/seekoff/wchar_t/10975.cc: Likewise. * testsuite/27_io/basic_istream/tellg/char/1.cc: Tweak consistently. * testsuite/27_io/basic_ostream/tellp/char/1.cc: Likewise. * testsuite/27_io/basic_ostream/tellp/char/2.cc: Likewise. * testsuite/27_io/basic_istream/seekg/char/2346-fstream.cc: Fix and move to... * testsuite/27_io/basic_istream/seekp/char/2346-fstream.cc: ... here. * testsuite/27_io/basic_istream/seekg/char/2346-sstream.cc: Fix and move to... * testsuite/27_io/basic_istream/seekp/char/2346-sstream.cc: ... here. Patches: http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/ChangeLog.diff?cvsroot=gcc&r1=1.2674&r2=1.2675 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/include/bits/sstream.tcc.diff?cvsroot=gcc&r1=1.42&r2=1.43 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/docs/html/ext/howto.html.diff?cvsroot=gcc&r1=1.50&r2=1.51 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_istream/seekg/char/2346-fstream.cc.diff?cvsroot=gcc&r1=1.3&r2=NONE http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_istream/seekg/char/2346-sstream.cc.diff?cvsroot=gcc&r1=1.3&r2=NONE http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_istream/tellg/char/1.cc.diff?cvsroot=gcc&r1=1.2&r2=1.3 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/2346-fstream.cc.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/2346-sstream.cc.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/1.cc.diff?cvsroot=gcc&r1=1.2&r2=1.3 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/2.cc.diff?cvsroot=gcc&r1=1.2&r2=1.3 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_stringbuf/seekoff/char/10975.cc.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_stringbuf/seekoff/wchar_t/10975.cc.diff?cvsroot=gcc&r1=NONE&r2=1.1
Subject: Bug 10975 CVSROOT: /cvs/gcc Module name: gcc Branch: gcc-3_4-branch Changes by: paolo@gcc.gnu.org 2004-10-05 19:50:03 Modified files: libstdc++-v3 : ChangeLog libstdc++-v3/docs/html/ext: howto.html libstdc++-v3/include/bits: sstream.tcc libstdc++-v3/testsuite/27_io/basic_istream/tellg/char: 1.cc libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char: 1.cc 2.cc Added files: libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char: 2346-fstream.cc 2346-sstream.cc libstdc++-v3/testsuite/27_io/basic_stringbuf/seekoff/char: 10975.cc Removed files: libstdc++-v3/testsuite/27_io/basic_istream/seekg/char: 2346-fstream.cc 2346-sstream.cc Log message: 2004-10-05 Paolo Carlini <pcarlini@suse.de> PR libstdc++/10975 (DR 453) * include/bits/sstream.tcc (seekoff): Don't fail if __beg == 0 and __off == 0. * docs/html/ext/howto.html: Add an entry for DR 453. * testsuite/27_io/basic_stringbuf/seekoff/char/10975.cc: New. * testsuite/27_io/basic_istream/tellg/char/1.cc: Tweak consistently. * testsuite/27_io/basic_ostream/tellp/char/1.cc: Likewise. * testsuite/27_io/basic_ostream/tellp/char/2.cc: Likewise. * testsuite/27_io/basic_istream/seekg/char/2346-fstream.cc: Fix and move to... * testsuite/27_io/basic_istream/seekp/char/2346-fstream.cc: ... here. * testsuite/27_io/basic_istream/seekg/char/2346-sstream.cc: Fix and move to... * testsuite/27_io/basic_istream/seekp/char/2346-sstream.cc: ... here. Patches: http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.2224.2.185&r2=1.2224.2.186 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/docs/html/ext/howto.html.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.42.4.6&r2=1.42.4.7 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/include/bits/sstream.tcc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.37.4.4&r2=1.37.4.5 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_istream/seekg/char/2346-fstream.cc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.3&r2=NONE http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_istream/seekg/char/2346-sstream.cc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.3&r2=NONE http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_istream/tellg/char/1.cc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.2&r2=1.2.12.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/2346-fstream.cc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=NONE&r2=1.1.10.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/2346-sstream.cc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=NONE&r2=1.1.10.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/1.cc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.2&r2=1.2.12.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/2.cc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.2&r2=1.2.12.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/testsuite/27_io/basic_stringbuf/seekoff/char/10975.cc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=NONE&r2=1.1.10.1
Fixed for 3.4.3.