1183. basic_ios::set_rdbuf may break class invariants

Section: 31.5.4.3 [basic.ios.members] Status: C++11 Submitter: Daniel Krügler Opened: 2009-07-28 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [basic.ios.members].

View all issues with C++11 status.

Discussion:

The protected member function set_rdbuf had been added during the process of adding move and swap semantics to IO classes. A relevant property of this function is described by it's effects in 31.5.4.3 [basic.ios.members]/19:

Effects: Associates the basic_streambuf object pointed to by sb with this stream without calling clear().

This means that implementors of or those who derive from existing IO classes could cause an internal state where the stream buffer could be 0, but the IO class has the state good(). This would break several currently existing implementations which rely on the fact that setting a stream buffer via the currently only ways, i.e. either by calling

void init(basic_streambuf<charT,traits>* sb);

or by calling

basic_streambuf<charT,traits>* rdbuf(basic_streambuf<charT,traits>* sb);

to set rdstate() to badbit, if the buffer is 0. This has the effect that many internal functions can simply check rdstate() instead of rdbuf() for being 0.

I therefore suggest that a requirement is added for callers of set_rdbuf to set a non-0 value.

[ 2009-10 Santa Cruz: ]

Moved to Open. Martin volunteers to provide new wording, where set_rdbuf() sets the badbit but does not cause an exception to be thrown like a call to clear() would.

[ 2009-10-20 Martin provides wording: ]

Change 31.5.4.3 [basic.ios.members] around p. 19 as indicated:

void set_rdbuf(basic_streambuf<charT, traits>* sb);

Effects: Associates the basic_streambuf object pointed to by sb with this stream without calling clear(). Postconditions: rdbuf() == sb.

Effects: As if:


iostate state = rdstate();
try { rdbuf(sb); }
catch(ios_base::failure) {
   if (0 == (state & ios_base::badbit))
       unsetf(badbit);
}

Throws: Nothing.

Rationale:

We need to be able to call set_rdbuf() on stream objects for which (rdbuf() == 0) holds without causing ios_base::failure to be thrown. We also don't want badbit to be set as a result of setting rdbuf() to 0 if it wasn't set before the call. This changed Effects clause maintains the current behavior (as of N2914) without requiring that sb be non-null.

[ Post-Rapperswil ]

Several reviewers and the submitter believe that the best solution would be to add a pre-condition that the buffer shall not be a null pointer value.

Moved to Tentatively Ready with revised wording provided by Daniel after 5 positive votes on c++std-lib.

[ Adopted at 2010-11 Batavia ]

Proposed resolution:

  1. Add a new pre-condition just before 27.5.4.2 [basic.ios.members]/23 as indicated:
    void set_rdbuf(basic_streambuf<charT, traits>* sb);
    

    ?? Requires: sb != nullptr.

    23 Effects: Associates the basic_streambuf object pointed to by sb with this stream without calling clear().

    24 Postconditions: rdbuf() == sb.

    25 Throws: Nothing.

Rationale:

We believe that setting a nullptr stream buffer can be prevented.