Issue 1448: Concerns about basic_stringbuf::str(basic_string) postconditions (original) (raw)
This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++11 status.
1448. Concerns about basic_stringbuf::str(basic_string) postconditions
Section: 31.8.2.4 [stringbuf.members] Status: C++11 Submitter: BSI Opened: 2010-08-25 Last modified: 2016-01-28
Priority: Not Prioritized
View all other issues in [stringbuf.members].
View all issues with C++11 status.
Discussion:
Addresses GB-124
N3092 31.8.2.4 [stringbuf.members] contains this text specifying the postconditions ofbasic_stringbuf::str(basic_string):
Postconditions: If
mode & ios_base::outistrue,pbase()points to the first underlying character andepptr() >= pbase() + s.size()holds; in addition, ifmode & ios_base::inistrue,pptr() == pbase() + s.data()holds, otherwisepptr() == pbase()istrue. [...]
Firstly, there's a simple mistake: It should be pbase() + s.length(), not pbase() + s.data().
Secondly, it doesn't match existing implementations. As far as I can tell, GCC 4.5 does not test for mode & ios_base::in in the second part of that sentence, but for mode & (ios_base::app | ios_base_ate), and Visual C++ 9 for mode & ios_base::app. Besides, the wording of the C++0x draft doesn't make any sense to me. I suggest changing the second part of the sentence to one of the following:
Replace ios_base::in with (ios_base::ate | ios_base::app), but this would require Visual C++ to change (replacing only withios_base::ate would require GCC to change, and would makeios_base::app completely useless with stringstreams):
in addition, if mode & (ios_base::ate | ios_base::app) is true,pptr() == pbase() + s.length() holds, otherwise pptr() == pbase()is true.
Leave pptr() unspecified if mode & ios_base::app, but notmode & ios_base::ate (implementations already differ in this case, and it is always possible to use ios_base::ate to get the effect of appending, so it is not necessary to require any implementation to change):
in addition, if mode & ios_base::ate is true,pptr() == pbase() + s.length() holds, if neither mode & ios_base::atenor mode & ios_base::app is true, pptr() == pbase() holds, otherwise pptr() >= pbase() && pptr() <= pbase() + s.length()(which of the values in this range is unspecified).
Slightly stricter:
in addition, if mode & ios_base::ate is true,pptr() == pbase() + s.length() holds, if neithermode & ios_base::ate nor mode & ios_base::app is true,pptr() == pbase() holds, otherwise pptr() == pbase() || pptr() == pbase() + s.length()(which of these two values is unspecified). A small table might help to better explain the three cases. BTW, at the end of the postconditions is this text: "egptr() == eback() + s.size() hold". Is there a perference for basic_string::length or basic_string::size? It doesn't really matter, but it looks a bit inconsistent.
[2011-03-09: Nicolai Josuttis comments and drafts wording]
First, it seems the current wording is just an editorial mistake. When we added issue 432(i)to the draft standard (in n1733), the wording in the issue:
If
mode & ios_base::outis true, initializes the output sequence such thatpbase()points to the first underlying character,epptr()points one past the last underlying character, and if(mode & ios_base::ate)is true,pptr()is set equal toepptr()elsepptr()is set equal topbase().
became:
If
mode & ios_base::outis true, initializes the output sequence such thatpbase()points to the first underlying character,epptr()points one past the last underlying character, andpptr()is equal toepptr()ifmode & ios_base::inis true, otherwisepptr()is equal topbase().
which beside some changes of the order of words changed
ios_base::ate
into
ios_base::in
So, from this point of view, clearly mode & ios_base::ate was meant.
Nevertheless, with this proposed resolution we'd have no wording regarding ios_base::app. Currently the only statements about app in the Standard are just in two tables:
- Table 125 — "
openmodeeffects" says that the effect ofappis "seek to end before each write" - Table 132 — "File open modes" says that the stdio equivalent for
appis"a"
Indeed we seem to have different behavior currently in respect to app: For
stringstream s2(ios_base::out|ios_base::in|ios_base::app); s2.str("s2 hello"); s1 << "more";
- Visual C++ 10 does overwrite (=>
"moreello") - G++ 4.5 does append (=>
"s2 hellomore")
BTW, for fstreams, both implementations append when app is set: If f2.txt has contents "xy",
fstream f2("f2.txt",ios_base::out|ios_base::in|ios_base::app); f1 << "more";
appends "more" so that the contents is "xymore".
So IMO app should set the write pointer to the end so that each writing appends.
I don't know whether what the standard says is enough. You can argue the statement in Table 125 clearly states that such a buffer should always append, which of course also applies to str() of stringbuffer.
Nevertheless, it doesn't hurt IMO if we clarify the behavior of str()here in respect to app.
[2011-03-10: P.J.Plauger comments:]
I think we should say nothing special about app at construction time (thus leaving the write pointer at the beginning of the buffer). Leave implementers wiggle room to ensure subsequent append writes as they see fit, but don't change existing rules for initial seek position.
[Madrid meeting: It was observed that a different issue should be opened that clarifies the meaning of app for stringstream]
Proposed resolution:
Change 31.8.2.4 [stringbuf.members] p. 3 as indicated:
void str(const basic_string<charT,traits,Allocator>& s);
2 Effects: Copies the content of
sinto thebasic_stringbufunderlying character sequence and initializes the input and output sequences according tomode.3 Postconditions: If
mode & ios_base::outis true,pbase()points to the first underlying character andepptr() >= pbase() + s.size()holds; in addition, ifmode & ~~ios_base::in~~ios_base::ateis true,pptr() == pbase() + ~~s.data()~~s.size()holds, otherwisepptr() == pbase()is true. Ifmode & ios_base::inis true,eback()points to the first underlying character, and bothgptr() == eback() and egptr() == eback() + s.size()hold.