libstdc++: Implement std::emit_on_flush etc.
authorJonathan Wakely <jwakely@redhat.com>
Wed, 11 Nov 2020 00:19:40 +0000 (00:19 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Wed, 11 Nov 2020 00:19:40 +0000 (00:19 +0000)
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.

libstdc++-v3/include/std/ostream
libstdc++-v3/include/std/syncstream
libstdc++-v3/testsuite/27_io/basic_ostream/emit/1.cc [new file with mode: 0644]
libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc

index 9a80adf3a5ac62fae1f1c8d779bfefa982d2b5a0..c203e31d7c9da948f331cf2834252b3f87efb3ee 100644 (file)
@@ -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
index 9d1db0cf286e2724655aa1565051aa66833983d4..07aab65223ece97bd1b9c4b9745fd1497695eb2e 100644 (file)
@@ -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 (auto __size = __s.size())
+         {
+           auto __n = this->_M_wrapped->sputn(__s.data(), __size);
+           if (__n != __size)
+             {
+               __s.erase(0, __n);
+               _M_impl.str(std::move(__s));
+               return false;
+             }
+         }
 
-       if (_M_needs_sync)
+       if (this->_M_needs_sync)
          {
-           _M_needs_sync = false;
-           if (_M_wrapped->pubsync() != 0)
+           this->_M_needs_sync = false;
+           if (this->_M_wrapped->pubsync() != 0)
              return false;
          }
-
-       _M_impl.str("");
        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 (file)
index 0000000..c50648a
--- /dev/null
@@ -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();
+}
index ef463996b3197888a17936ffcd7d7d5a6b652faf..3ca97aa0c5bc8d6d62372bdf83b84c2bd390e38d 100644 (file)
@@ -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;
 }