622. behavior of filebuf dtor and close on error

Section: 30.9.5.3 [fstream.members] Status: CD1 Submitter: Martin Sebor Opened: 2007-01-20 Last modified: 2016-02-10

Priority: Not Prioritized

View all issues with CD1 status.

Discussion:

basic_filebuf dtor is specified to have the following straightforward effects:

Effects: Destroys an object of class basic_filebuf. Calls close().

close() does a lot of potentially complicated processing, including calling overflow() to write out the termination sequence (to bring the output sequence to its initial shift state). Since any of the functions called during the processing can throw an exception, what should the effects of an exception be on the dtor? Should the dtor catch and swallow it or should it propagate it to the caller? The text doesn't seem to provide any guidance in this regard other than the general restriction on throwing (but not propagating) exceptions from destructors of library classes in 20.5.5.12 [res.on.exception.handling].

Further, the last thing close() is specified to do is call fclose() to close the FILE pointer. The last sentence of the Effects clause reads:

... If any of the calls to overflow or std::fclose fails then close fails.

This suggests that close() might be required to call fclose() if and only if none of the calls to overflow() fails, and avoid closing the FILE otherwise. This way, if overflow() failed to flush out the data, the caller would have the opportunity to try to flush it again (perhaps after trying to deal with whatever problem may have caused the failure), rather than losing it outright.

On the other hand, the function's Postcondition specifies that is_open() == false, which suggests that it should call fclose() unconditionally. However, since Postcondition clauses are specified for many functions in the standard, including constructors where they obviously cannot apply after an exception, it's not clear whether this Postcondition clause is intended to apply even after an exception.

It might be worth noting that the traditional behavior (Classic Iostreams fstream::close() and C fclose()) is to close the FILE unconditionally, regardless of errors.

[ See 397 and 418 for related issues. ]

Proposed resolution:

After discussing this on the reflector (see the thread starting with c++std-lib-17650) we propose that close() be clarified to match the traditional behavior, that is to close the FILE unconditionally, even after errors or exceptions. In addition, we propose the dtor description be amended so as to explicitly require it to catch and swallow any exceptions thrown by close().

Specifically, we propose to make the following edits in 30.9.2.3 [filebuf.members]:


basic_filebuf<charT,traits>* close();

            

Effects: If is_open() == false, returns a null pointer. If a put area exists, calls overflow(traits::eof()) to flush characters. If the last virtual member function called on *this (between underflow, overflow, seekoff, and seekpos) was overflow then calls a_codecvt.unshift (possibly several times) to determine a termination sequence, inserts those characters and calls overflow(traits::eof()) again. Finally, regardless of whether any of the preceding calls fails or throws an exception, the function it closes the file ("as if" by calling std::fclose(file)).334) If any of the calls made by the functionto overflow or, including std::fclose, fails then close fails by returning a null pointer. If one of these calls throws an exception, the exception is caught and rethrown after closing the file.

And to make the following edits in 30.9.2.1 [filebuf.cons].


virtual ~basic_filebuf();

            

Effects: Destroys an object of class basic_filebuf<charT,traits>. Calls close(). If an exception occurs during the destruction of the object, including the call to close(), the exception is caught but not rethrown (see 20.5.5.12 [res.on.exception.handling]).