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.

commit ecba8547dd398ad4b627756013dbd22be417d4da
Author: Jonathan Wakely <jwak...@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;
 }

Reply via email to