C++ Logo

std-discussion

Advanced search

Re: Setting badbit when outputting to an unopened fstream in eof state

From: Gennaro Prota <gennaro.prota_at_[hidden]>
Date: Thu, 7 Nov 2019 15:23:48 +0100
On Thu, Nov 7, 2019 at 1:04 PM Bo Persson via Std-Discussion
<std-discussion_at_[hidden]> wrote:
>
> On 2019-11-07 at 11:52, Gennaro Prota via Std-Discussion wrote:
> > On Sun, Nov 3, 2019 at 9:23 PM Gennaro Prota <gennaro.prota_at_[hidden]> wrote:
> >>
> >> { Resending this, as it was apparently lost. }
> >>
> >> Hi,
> >>
> >> this program:
> >>
> >> #include <fstream>
> >> #include <iostream>
> >>
> >> int
> >> main()
> >> {
> >> std::fstream fs ;
> >> fs.clear( std::ios_base::eofbit ) ;
> >>
> >> fs << "this isn't actually output" ;
> >>
> >> if ( ( fs.rdstate() & std::ios_base::badbit ) != 0 ) {
> >> std::cout << "badbit set" << std::endl ;
> >> }
> >> }
> >>
> >> prints "badbit set" on Visual C++ 2015 and prints nothing with GCC or
> >> Clang+libstdc++.
> >>
> >> Does the standard allow both behaviors?
> >
> > Since, differently from other questions, I didn't get any reply, I
> > wonder: was this question off-topic? In fact, I didn't find any page
> > explaining what is on topic and what is not for this list, so I just
> > "went intuitively", so to speak. Sorry for that.
> >
>
> Not getting a reply could also mean that nobody knows the answer. :-)

Yeah, but that seemed strange :-)

> Here's my take:
>
> The requirements for formatted output
> http://eel.is/c++draft/ostream.formatted.reqmts is:
>
> "Each formatted output function begins execution by constructing an
> object of class sentry. If this object returns true when converted to a
> value of type bool, the function endeavors to generate the requested
> output. If the generation fails, then the formatted output function does
> setstate(ios_base::failbit), which might throw an exception. If an
> exception is thrown during output, then ios::badbit is turned on in
> *this's error state. If (exceptions()&badbit) != 0 then the exception is
> rethrown. Whether or not an exception is thrown, the sentry object is
> destroyed before leaving the formatted output function. If no exception
> is thrown, the result of the formatted output function is *this."
>
> Huh!
>
> Nowhere does this say anything about badbit (except as a result of
> exceptions). Doesn't seem to happen in your case.

I had looked at a different part of the standard:

  from C++03 [lib.ostream]/2 and /3: Two groups of member function
  signatures share common properties: the formatted output functions (or
  inserters) and the unformatted output functions. Both groups of output
  functions generate (or insert) output characters by actions equivalent
  to calling rdbuf()->sputc(int_type). They may use other public members
  of basic_ostream except that they do not invoke any virtual members of
  rdbuf() except overflow().

  If one of these called functions throws an exception, then unless
  explicitly noted otherwise the output function set [sic] badbit in
  error state. If badbit is on in exceptions(), the output function
  rethrows the exception without completing its actions, otherwise it
  does not throw anything and treat [sic] as an error.

So, the question seems to be whether sputc() or any of the called
functions may emit an exception. I think that's allowed, but doing so
for what is semantically a non-permanent error doesn't seem the correct
intent, because it causes badbit to be set (I'd expect failbit, which
means an operation failed, not badbit, which should mean permanent
error).

> But, anyway, the libary code does one thing for string output (after
> creating the sentry object):
>
> if (!_Ok) {
> _State |= _Ostr_t::badbit;
> } else { // state okay, insert characters
>
> }
>
> and another thing for all other types
>
> if (_Ok) { // state okay, insert
>
> }
>
>
> So, why do they do that? Historical reasons? I don't know.

Ah, thanks for finding that. I hadn't looked at Microsoft sources.

> My guess is that this is not what the standard intended, but perhaps
> something MS has done "always". Or that writing to a closed file with
> eof-status isn't on the top list of use cases tested.

Yeah. It occurred in one of my tests for stream_equivalent, a facility I
use in my stream inserters to avoid modifying the flags of the passed-in
stream:

  <https://github.com/gennaroprota/breath/blob/master/breath/stream/test/stream_equivalent_test.cpp>

Basically, I wanted to check that state bits turned on in the original
stream *after* creating the stream_equivalent (in this case, eofbit)
would be merged with those turned on in the stream_equivalent. Hence I
did:

  BREATH_CHECK( fs.rdstate() == ( std::ios_base::failbit |
                                  std::ios_base::eofbit ) ) ;

And that failed under Visual C++ 2015 (I didn't check with later Visual
C++ versions).

Thanks Bo, your reply was very useful.

-- 
--
.:: Gennaro Prota ::.
.:: https://about.me/gennaro.prota ::.

Received on 2019-11-07 08:26:45