* [committed] libstdc++: Implement std::emit_on_flush etc.
@ 2020-11-11 0:19 Jonathan Wakely
0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2020-11-11 0:19 UTC (permalink / raw)
To: libstdc++, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 1545 bytes --]
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.
[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 13037 bytes --]
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;
}
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2020-11-11 0:20 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-11 0:19 [committed] libstdc++: Implement std::emit_on_flush etc Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).