Verhalten des ostream sentry destructor
-
In einem kürzlichen Unterhaltung bin ich der Verwendung des unitbuf flag begegnet, und da mir die Anwendung wieder entfallen ist und ich mir die Lücke schließen wollte, hab ich in diversen Quellen nachgelesen und dabei unter anderem auch in der MinGW Implementation nachgeschaut. Im Standard steht zum destructor von std::basic_ostream::sentry folgendes:
~sentry();
4 If ((os.flags() & ios_base::unitbuf) && !uncaught_exception() && os.good()) is true, calls
os.rdbuf()->pubsync(). If that function returns -1, sets badbit in os.rdstate() without propagating
an exception.Die Implementation sieht gekürzt so aus (entnommen aus <ostream>):
template <typename _CharT, typename _Traits> class basic_ostream<_CharT, _Traits>::sentry { // Data Members. bool _M_ok; basic_ostream<_CharT, _Traits>& _M_os; public: /* ... */ ~sentry() { // XXX MT if (bool(_M_os.flags() & ios_base::unitbuf) && !uncaught_exception()) { // Can't call flush directly or else will get into recursive lock. if (_M_os.rdbuf() && _M_os.rdbuf()->pubsync() == -1) _M_os.setstate(ios_base::badbit); } } /* ... */ };
Hierzu wollte ich fragen ob die Implementation Fehler enthält. Beispielsweise kann ich die geforderte Prüfung von os.good() nicht finden. Außerdem kann so weit ich es verstehe eine exception von pubsync() and setstate(badbit) geworfen werden, die den destructor verlässt. Das würde die Anforderung an den sentry destructor sowie eine andere Anforderung vom Standard verletzen:
17.6.5.12 Restrictions on exception handling [res.on.exception.handling]
4 Destructor operations defined in the C++ standard library shall not throw exceptions. Every destructor in
the C++ standard library shall behave as if it had a non-throwing exception specification. Any other functions
defined in the C++ standard library that do not have an exception-specification may throw implementationdefined
exceptions unless otherwise specified.192 An implementation may strengthen this implicit exceptionspecification
by adding an explicit one.193Als besonders problematisch sehe ich dies an, da der destructor unter c++11 implizit noexcept(true) ist.
12.4 Destructors [class.dtor]
3 A declaration of a destructor that does not have an exception-specification is implicitly considered to have
the same exception-specification as an implicit declaration (15.4).Ich konnte auch mit folgendem Beispiel mit -std=c++11 im GCC 4.8.1 einen Aufruf von std::terminate() bewirken:
#include <iostream> #include <iomanip> #include <ios> #include <streambuf> #include <exception> class dummy_buffer : public std::streambuf { protected: int sync() {return -1;} int_type overflow(int_type c) {return traits_type::not_eof(c);} }; dummy_buffer dummy; int main() { std::cout << std::unitbuf; std::streambuf *rm = std::cout.rdbuf(&dummy); std::cout.exceptions(std::ios_base::badbit); try { std::ostream::sentry s(std::cout); } // <-- std::terminate() erfolgt hier catch(std::exception const& exc) { std::clog << "exc.what(): " << exc.what() << '\n'; } std::cout.exceptions(std::ios_base::goodbit); std::cout.rdbuf(rm); std::cout << std::nounitbuf; }
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
terminate called after throwing an instance of 'std::ios_base::failure'
what(): basic_ios::clearIrgendwie bezweifle ich, dass das so gedacht ist. Mit Stichwörtersuche habe ich jedenfalls nichts im Internet und keine bugreports gefunden
-
Ich behaupte sogar, da ist der Standard kaputt.
void mymain() { // dein Code (uncaught_exception ist hier true) } int main() { try { struct x { ~x() { mymain(); } } x; throw 0; } catch(...) {} }