[committed] libstdc++: Implement std::emit_on_flush etc.

Jonathan Wakely jwakely@redhat.com
Wed Nov 11 00:19:57 GMT 2020


This adds the manipulators for use with basic_osyncstream. In order to
detect when an arbitrary basic_ostream<C,T> is the base class of a
basic_syncbuf<C,T,A> object, introduce a new intermediate base class
that stores the data members. The new base class stores a pointer and
two bools, which wastes (sizeof(void*) - 2) bytes of padding. It would
be possible to use the two least significant bits of the pointer for the
two bools, at least for targets where alignof(basic_streambuf) > 2, but
that's left as a possible change for a future date.

Also define basic_syncbuf::overflow to override the virtual function in
the base class, so that single characters can be inserted into the
stream buffer. Previously the default basic_streambuf::overflow
implementation was used, which drops the character on the floor.

libstdc++-v3/ChangeLog:

	* include/std/ostream (__syncbuf_base): New class template.
	(emit_on_flush, noemit_on_flush, flush_emit): New manipulators.
	* include/std/syncstream (basic_syncbuf): Derive from
	__syncbuf_base instead of basic_streambuf.
	(basic_syncbuf::operator=): Remove self-assignment check.
	(basic_syncbuf::swap): Remove self-swap check.
	(basic_syncbuf::emit): Do not skip pubsync() call if sequence
	is empty.
	(basic_syncbuf::sync): Remove no-op pubsync on stringbuf.
	(basic_syncbuf::overflow): Define override.
	* testsuite/27_io/basic_syncstream/basic_ops/1.cc: Test
	basic_osyncstream::put(char_type).
	* testsuite/27_io/basic_ostream/emit/1.cc: New test.

Tested powerpc64le-linux. Committed to trunk.

-------------- next part --------------
commit ecba8547dd398ad4b627756013dbd22be417d4da
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Nov 11 00:19:40 2020

    libstdc++: Implement std::emit_on_flush etc.
    
    This adds the manipulators for use with basic_osyncstream. In order to
    detect when an arbitrary basic_ostream<C,T> is the base class of a
    basic_syncbuf<C,T,A> object, introduce a new intermediate base class
    that stores the data members. The new base class stores a pointer and
    two bools, which wastes (sizeof(void*) - 2) bytes of padding. It would
    be possible to use the two least significant bits of the pointer for the
    two bools, at least for targets where alignof(basic_streambuf) > 2, but
    that's left as a possible change for a future date.
    
    Also define basic_syncbuf::overflow to override the virtual function in
    the base class, so that single characters can be inserted into the
    stream buffer. Previously the default basic_streambuf::overflow
    implementation was used, which drops the character on the floor.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/ostream (__syncbuf_base): New class template.
            (emit_on_flush, noemit_on_flush, flush_emit): New manipulators.
            * include/std/syncstream (basic_syncbuf): Derive from
            __syncbuf_base instead of basic_streambuf.
            (basic_syncbuf::operator=): Remove self-assignment check.
            (basic_syncbuf::swap): Remove self-swap check.
            (basic_syncbuf::emit): Do not skip pubsync() call if sequence
            is empty.
            (basic_syncbuf::sync): Remove no-op pubsync on stringbuf.
            (basic_syncbuf::overflow): Define override.
            * testsuite/27_io/basic_syncstream/basic_ops/1.cc: Test
            basic_osyncstream::put(char_type).
            * testsuite/27_io/basic_ostream/emit/1.cc: New test.

diff --git a/libstdc++-v3/include/std/ostream b/libstdc++-v3/include/std/ostream
index 9a80adf3a5ac..c203e31d7c9d 100644
--- a/libstdc++-v3/include/std/ostream
+++ b/libstdc++-v3/include/std/ostream
@@ -776,6 +776,73 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __ret_os << __x;
       return __ret_os;
     }
+
+#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+  template<typename _CharT, typename _Traits>
+    class __syncbuf_base : public basic_streambuf<_CharT, _Traits>
+    {
+    public:
+      static bool*
+      _S_get(basic_streambuf<_CharT, _Traits>* __buf) noexcept
+      {
+	if (auto __p = dynamic_cast<__syncbuf_base*>(__buf))
+	  return &__p->_M_emit_on_sync;
+	return nullptr;
+      }
+
+    protected:
+      __syncbuf_base(basic_streambuf<_CharT, _Traits>* __w = nullptr)
+      : _M_wrapped(__w)
+      { }
+
+      basic_streambuf<_CharT, _Traits>* _M_wrapped = nullptr;
+      bool _M_emit_on_sync = false;
+      bool _M_needs_sync = false;
+    };
+
+  template<typename _CharT, typename _Traits>
+    inline basic_ostream<_CharT, _Traits>&
+    emit_on_flush(basic_ostream<_CharT, _Traits>& __os)
+    {
+      if (bool* __flag = __syncbuf_base<_CharT, _Traits>::_S_get(__os.rdbuf()))
+	*__flag = true;
+      return __os;
+    }
+
+  template<typename _CharT, typename _Traits>
+    inline basic_ostream<_CharT, _Traits>&
+    noemit_on_flush(basic_ostream<_CharT, _Traits>& __os)
+    {
+      if (bool* __flag = __syncbuf_base<_CharT, _Traits>::_S_get(__os.rdbuf()))
+	*__flag = false;
+      return __os;
+    }
+
+  template<typename _CharT, typename _Traits>
+    inline basic_ostream<_CharT, _Traits>&
+    flush_emit(basic_ostream<_CharT, _Traits>& __os)
+    {
+      struct _Restore
+      {
+	~_Restore() { *_M_flag = _M_prev; }
+
+	bool _M_prev = false;
+	bool* _M_flag = &_M_prev;
+      } __restore;
+
+      if (bool* __flag = __syncbuf_base<_CharT, _Traits>::_S_get(__os.rdbuf()))
+	{
+	  __restore._M_prev = *__flag;
+	  __restore._M_flag = __flag;
+	  *__flag = true;
+	}
+
+      __os.flush();
+      return __os;
+    }
+
+#endif // C++20
+
 #endif // C++11
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
index 9d1db0cf286e..07aab65223ec 100644
--- a/libstdc++-v3/include/std/syncstream
+++ b/libstdc++-v3/include/std/syncstream
@@ -52,7 +52,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _CharT, typename _Traits = char_traits<_CharT>,
 	    typename _Alloc = allocator<_CharT>>
-    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
+    class basic_syncbuf : public __syncbuf_base<_CharT, _Traits>
     {
     public:
       using char_type = _CharT;
@@ -69,22 +69,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       explicit
       basic_syncbuf(streambuf_type* __obuf)
-	: basic_syncbuf(__obuf, allocator_type{})
+      : basic_syncbuf(__obuf, allocator_type{})
       { }
 
       basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
-	: _M_wrapped(__obuf)
-	, _M_impl(__alloc)
-	, _M_mtx(__obuf)
+      : __syncbuf_base<_CharT, _Traits>(__obuf)
+      , _M_impl(__alloc)
+      , _M_mtx(__obuf)
       { }
 
       basic_syncbuf(basic_syncbuf&& __other)
-	: _M_wrapped(__other._M_wrapped)
-	, _M_impl(std::move(__other._M_impl))
-	, _M_mtx(std::move(__other._M_mtx))
-	, _M_emit_on_sync(__other._M_emit_on_sync)
-	, _M_needs_sync(__other._M_needs_sync)
+      : __syncbuf_base<_CharT, _Traits>(__other._M_wrapped)
+      , _M_impl(std::move(__other._M_impl))
+      , _M_mtx(std::move(__other._M_mtx))
       {
+	this->_M_emit_on_sync = __other._M_emit_on_sync;
+	this->_M_needs_sync = __other._M_needs_sync;
 	__other._M_wrapped = nullptr;
       }
 
@@ -98,82 +98,93 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  { }
       }
 
-      basic_syncbuf& operator=(basic_syncbuf&& __other)
+      basic_syncbuf&
+      operator=(basic_syncbuf&& __other)
       {
-	if (std::__addressof(__other) != this)
-	  {
-	    emit();
+	emit();
+
+	_M_impl = std::move(__other._M_impl);
+	this->_M_emit_on_sync = __other._M_emit_on_sync;
+	this->_M_needs_sync = __other._M_needs_sync;
+	this->_M_wrapped = __other._M_wrapped;
+	__other._M_wrapped = nullptr;
+	_M_mtx = std::move(__other._M_mtx);
 
-	    _M_impl = std::move(__other._M_impl);
-	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
-	    _M_mtx = std::move(__other._M_mtx);
-	    _M_emit_on_sync = __other._M_emit_on_sync;
-	    _M_needs_sync = __other._M_needs_sync;
-	  }
 	return *this;
       }
 
       void
-      swap(basic_syncbuf& __other)
+      swap(basic_syncbuf& __other) noexcept
       {
-	if (std::__addressof(__other) != this)
-	  {
-	    std::swap(_M_impl, __other._M_impl);
-	    std::swap(_M_wrapped, __other._M_wrapped);
-	    std::swap(_M_mtx, __other._M_mtx);
-	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
-	    std::swap(_M_needs_sync, __other._M_needs_sync);
-	  }
+	using _ATr = allocator_traits<_Alloc>;
+	if constexpr (!_ATr::propagate_on_container_swap::value)
+	  __glibcxx_assert(get_allocator() == __other.get_allocator());
+
+	std::swap(_M_impl, __other._M_impl);
+	std::swap(this->_M_emit_on_sync, __other._M_emit_on_sync);
+	std::swap(this->_M_needs_sync, __other._M_needs_sync);
+	std::swap(this->_M_wrapped, __other._M_wrapped);
+	std::swap(_M_mtx, __other._M_mtx);
       }
 
       bool
       emit()
       {
-	if (!_M_wrapped)
+	if (!this->_M_wrapped)
 	  return false;
 
-	auto __s = _M_impl.view();
-	if (__s.empty())
-	  return true;
+	auto __s = std::move(_M_impl).str();
 
 	const lock_guard<__mutex> __l(_M_mtx);
-	if (_M_wrapped->sputn(__s.data(), __s.size()) != __s.size())
-	  return false;
-
-	if (_M_needs_sync)
+	if (auto __size = __s.size())
 	  {
-	    _M_needs_sync = false;
-	    if (_M_wrapped->pubsync() != 0)
-	      return false;
+	    auto __n = this->_M_wrapped->sputn(__s.data(), __size);
+	    if (__n != __size)
+	      {
+		__s.erase(0, __n);
+		_M_impl.str(std::move(__s));
+		return false;
+	      }
 	  }
 
-	_M_impl.str("");
+	if (this->_M_needs_sync)
+	  {
+	    this->_M_needs_sync = false;
+	    if (this->_M_wrapped->pubsync() != 0)
+	      return false;
+	  }
 	return true;
       }
 
       streambuf_type*
       get_wrapped() const noexcept
-      { return _M_wrapped; }
+      { return this->_M_wrapped; }
 
-      allocator_type get_allocator() const noexcept
+      allocator_type
+      get_allocator() const noexcept
       { return _M_impl.get_allocator(); }
 
       void
       set_emit_on_sync(bool __b) noexcept
-      { _M_emit_on_sync = __b; }
+      { this->_M_emit_on_sync = __b; }
 
     protected:
       int
       sync() override
       {
-	auto __res = _M_impl.pubsync();
-	if (__res == 0)
-	  {
-	    _M_needs_sync = true;
-	    if (_M_emit_on_sync)
-	      return emit() ? 0 : -1;
-	  }
-	return __res;
+	this->_M_needs_sync = true;
+	if (this->_M_emit_on_sync && !emit())
+	  return -1;
+	return 0;
+      }
+
+      int_type
+      overflow(int_type __c) override
+      {
+	int_type __eof = traits_type::eof();
+	if (__builtin_expect(!traits_type::eq_int_type(__c, __eof), true))
+	  return _M_impl.sputc(__c);
+	return __eof;
       }
 
       streamsize
@@ -181,11 +192,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return _M_impl.sputn(__s, __n); }
 
     private:
-      streambuf_type* _M_wrapped;
-
-      using __impl_type = basic_stringbuf<char_type, traits_type,
-					  allocator_type>;
-      __impl_type _M_impl;
+      basic_stringbuf<char_type, traits_type, allocator_type> _M_impl;
 
       struct __mutex
       {
@@ -203,15 +210,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	void
 	lock()
 	{
-	  if (_M_mtx)
-	    _M_mtx->lock();
+	  _M_mtx->lock();
 	}
 
 	void
 	unlock()
 	{
-	  if (_M_mtx)
-	    _M_mtx->unlock();
+	  _M_mtx->unlock();
 	}
 
 	// FIXME: This should be put in the .so
@@ -225,31 +230,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return __m[__key];
 	}
 #else
-	__mutex(void*)
-	{ }
-
-	void
-	swap(__mutex&&) noexcept
-	{ }
-
-	void
-	lock()
-	{ }
-
-	void
-	unlock()
-	{ }
+	__mutex(void*) { }
+	void swap(__mutex&&) noexcept { }
+	void lock() { }
+	void unlock() { }
 #endif
-	__mutex(const __mutex&) = delete;
-	__mutex& operator=(const __mutex&) = delete;
-
 	__mutex(__mutex&&) = default;
 	__mutex& operator=(__mutex&&) = default;
       };
       __mutex _M_mtx;
-
-      bool _M_emit_on_sync = false;
-      bool _M_needs_sync = false;
     };
 
   template <typename _CharT, typename _Traits = char_traits<_CharT>,
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/emit/1.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/emit/1.cc
new file mode 100644
index 000000000000..c50648adf632
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_ostream/emit/1.cc
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  std::stringbuf sb;
+  std::osyncstream s(&sb);
+  s << "abc" << std::emit_on_flush << "def" << std::flush << "ghi"
+    << std::emit_on_flush << std::noemit_on_flush << std::endl;
+  VERIFY( sb.view() == "abcdef" );
+  s << "jkl" << std::flush_emit << "mno" << std::flush;
+  VERIFY( sb.view() == "abcdefghi\njkl" );
+  s.emit();
+  VERIFY( sb.view() == "abcdefghi\njklmno" );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
index ef463996b319..3ca97aa0c5bc 100644
--- a/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
@@ -123,12 +123,41 @@ test04() // emitting
     s.emit();
     VERIFY( b.str() == txt );
   }
+
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+
+    s.put('a');
+    s.put('b');
+    s.put('c');
+
+    s.emit();
+    VERIFY( b.str() == "abc" );
+  }
+
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+
+    s << "abc";
+    s.put(' ');
+    s << "def";
+    s.emit();
+    VERIFY( b.str() == "abc def" );
+
+    s << "ghi";
+    s.put(' ');
+    s << "jkl";
+    s.emit();
+    VERIFY( b.str() == "abc defghi jkl" );
+  }
 }
+
 int main()
 {
   test01();
   test02();
   test03();
   test04();
-  return 0;
 }


More information about the Gcc-patches mailing list