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.193

    Als 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::clear

    Irgendwie 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(...) {}
    }
    

Anmelden zum Antworten