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.

835. Tying two streams together (correction to DR 581)

Section: 31.5.4.3 [basic.ios.members] Status: C++11 Submitter: Martin Sebor Opened: 2008-05-17 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 fix for issue 581(i), now integrated into the working paper, overlooks a couple of minor problems.

First, being an unformatted function once again, flush() is required to create a sentry object whose constructor must, among other things, flush the tied stream. When two streams are tied together, either directly or through another intermediate stream object, flushing one will also cause a call to flush() on the other tied stream(s) and vice versa, ad infinitum. The program below demonstrates the problem.

Second, as Bo Persson notes in his comp.lang.c++.moderated post, for streams with the unitbuf flag set such as std::stderr, the destructor of the sentry object will again call flush(). This seems to create an infinite recursion for std::cerr << std::flush;

#include <iostream>

int main ()
{
   std::cout.tie (&std::cerr);
   std::cerr.tie (&std::cout);
   std::cout << "cout\n";
   std::cerr << "cerr\n";
} 

[ Batavia (2009-05): ]

We agree with the proposed resolution. Move to Review.

[ 2009-05-26 Daniel adds: ]

I think that the most recently suggested change in [ostream::sentry] need some further word-smithing. As written, it would make the behavior undefined, if under conditions when pubsync() should be called, but when in this scenario os.rdbuf() returns 0.

This case is explicitly handled in flush() and needs to be taken care of. My suggested fix is:

If ((os.flags() & ios_base::unitbuf) && !uncaught_exception() && os.rdbuf() != 0) is true, calls os.flush() os.rdbuf()->pubsync().

Two secondary questions are:

  1. Should pubsync() be invoked in any case or shouldn't a base requirement for this trial be that os.good() == true as required in the original flush() case?
  2. Since uncaught_exception() is explicitly tested, shouldn't a return value of -1 of pubsync() produce setstate(badbit) (which may throw ios_base::failure)?

[ 2009-07 Frankfurt: ]

Daniel volunteered to modify the proposed resolution to address his two questions.

Move back to Open.

[ 2009-07-26 Daniel provided wording. Moved to Review. ]

[ 2009-10-13 Daniel adds: ]

This proposed wording is written to match the outcome of 397(i).

[ 2009 Santa Cruz: ]

Move to Open. Martin to propose updated wording that will also resolve issue 397(i) consistently.

[ 2010-02-15 Martin provided wording. ]

[ 2010 Pittsburgh: ]

Moved to Ready for Pittsburgh.

Proposed resolution:

  1. Just before 31.5.4.3 [basic.ios.members] p. 2 insert a new paragraph:

    Requires: If (tiestr != 0) is true, tiestr must not be reachable by traversing the linked list of tied stream objects starting from tiestr->tie().

  2. Change [ostream::sentry] p. 4 as indicated:

    If ((os.flags() & ios_base::unitbuf) && !uncaught_exception() && os.good()) is true, calls os.flush() os.rdbuf()->pubsync(). If that function returns -1 sets badbit in os.rdstate() without propagating an exception.

  3. Add after [ostream::sentry] p17, the following paragraph:

    Throws: Nothing.