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 of basic_stringbuf::str(basic_string):

Postconditions: If mode & ios_base::out is true, pbase() points to the first underlying character and epptr() >= pbase() + s.size() holds; in addition, if mode & ios_base::in is true, pptr() == pbase() + s.data() holds, otherwise pptr() == pbase() is true. [...]

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 with ios_base::ate would require GCC to change, and would make ios_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 not mode & 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::ate nor 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 neither mode & 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 to the draft standard (in n1733), the wording in the issue:

If mode & ios_base::out is true, initializes the output sequence such that pbase() 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 to epptr() else pptr() is set equal to pbase().

became:

If mode & ios_base::out is true, initializes the output sequence such that pbase() points to the first underlying character, epptr() points one past the last underlying character, and pptr() is equal to epptr() if mode & ios_base::in is true, otherwise pptr() is equal to pbase().

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:

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";

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 s into the basic_stringbuf underlying character sequence and initializes the input and output sequences according to mode.

3 Postconditions: If mode & ios_base::out is true, pbase() points to the first underlying character and epptr() >= pbase() + s.size() holds; in addition, if mode & ios_base::inios_base::ate is true, pptr() == pbase() + s.data()s.size() holds, otherwise pptr() == pbase() is true. If mode & ios_base::in is true, eback() points to the first underlying character, and both gptr() == eback() and egptr() == eback() + s.size() hold.