mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-20 17:44:53 +08:00
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.
This commit is contained in:
parent
bb6226419f
commit
ecba8547dd
@ -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
|
||||
|
@ -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>,
|
||||
|
44
libstdc++-v3/testsuite/27_io/basic_ostream/emit/1.cc
Normal file
44
libstdc++-v3/testsuite/27_io/basic_ostream/emit/1.cc
Normal file
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user