diff --git a/libcxx/include/iosfwd b/libcxx/include/iosfwd --- a/libcxx/include/iosfwd +++ b/libcxx/include/iosfwd @@ -87,6 +87,18 @@ typedef fpos::state_type> streampos; typedef fpos::state_type> wstreampos; +template , class Allocator = allocator> + class basic_syncbuf; + +using syncbuf = basic_syncbuf; +using wsyncbuf = basic_syncbuf; + +template , class Allocator = allocator> + class basic_osyncstream; + +using osyncstream = basic_osyncstream; +using wosyncstream = basic_osyncstream; + } // std */ @@ -196,6 +208,22 @@ typedef fpos u32streampos; #endif // _LIBCPP_HAS_NO_UNICODE_CHARS +#if _LIBCPP_STD_VER > 17 + +template , class _Allocator = allocator<_CharT>> +class basic_syncbuf; + +using syncbuf = basic_syncbuf; +using wsyncbuf = basic_syncbuf; + +template , class _Allocator = allocator<_CharT>> +class basic_osyncstream; + +using osyncstream = basic_osyncstream; +using wosyncstream = basic_osyncstream; + +#endif // _LIBCPP_STD_VER > 17 + #if defined(_NEWLIB_VERSION) // On newlib, off_t is 'long int' typedef long int streamoff; // for char_traits in diff --git a/libcxx/include/syncstream b/libcxx/include/syncstream new file mode 100644 --- /dev/null +++ b/libcxx/include/syncstream @@ -0,0 +1,494 @@ +// -*- C++ -*- +//===--------------------------- sstream ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_SYNCSTREAM +#define _LIBCPP_SYNCSTREAM + +/* + syncstream synopsis + +#include // see [ostream.syn] + +namespace std { + template + class basic_syncbuf; + + using syncbuf = basic_syncbuf; + using wsyncbuf = basic_syncbuf; + + template + class basic_osyncstream; + + using osyncstream = basic_osyncstream; + using wosyncstream = basic_osyncstream; + + template + class basic_syncbuf : public basic_streambuf { + public: + using char_type = charT; + using int_type = typename traits::int_type; + using pos_type = typename traits::pos_type; + using off_type = typename traits::off_type; + using traits_type = traits; + using allocator_type = Allocator; + + using streambuf_type = basic_streambuf; + + // [syncstream.syncbuf.cons], construction and destruction + explicit basic_syncbuf(streambuf_type* obuf = nullptr) + : basic_syncbuf(obuf, Allocator()) {} + basic_syncbuf(streambuf_type*, const Allocator&); + basic_syncbuf(basic_syncbuf&&); + ~basic_syncbuf(); + + // [syncstream.syncbuf.assign], assignment and swap + basic_syncbuf& operator=(basic_syncbuf&&); + void swap(basic_syncbuf&); + + // [syncstream.syncbuf.members], member functions + bool emit(); + streambuf_type* get_wrapped() const noexcept; + allocator_type get_allocator() const noexcept; + void set_emit_on_sync(bool) noexcept; + + protected: + // [syncstream.syncbuf.virtuals], overridden virtual functions + int sync() override; + + private: + streambuf_type* wrapped; // exposition only + bool emit_on_sync{}; // exposition only + }; + + // [syncstream.syncbuf.special], specialized algorithms + template + void swap(basic_syncbuf&, + basic_syncbuf&); + + template + class basic_osyncstream : public basic_ostream { + public: + using char_type = charT; + using int_type = typename traits::int_type; + using pos_type = typename traits::pos_type; + using off_type = typename traits::off_type; + using traits_type = traits; + + using allocator_type = Allocator; + using streambuf_type = basic_streambuf; + using syncbuf_type = basic_syncbuf; + + // [syncstream.osyncstream.cons], construction and destruction + basic_osyncstream(streambuf_type*, const Allocator&); + explicit basic_osyncstream(streambuf_type* obuf) + : basic_osyncstream(obuf, Allocator()) {} + basic_osyncstream(basic_ostream& os, const Allocator& allocator) + : basic_osyncstream(os.rdbuf(), allocator) {} + explicit basic_osyncstream(basic_ostream& os) + : basic_osyncstream(os, Allocator()) {} + basic_osyncstream(basic_osyncstream&&) noexcept; + ~basic_osyncstream(); + + // [syncstream.osyncstream.assign], assignment + basic_osyncstream& operator=(basic_osyncstream&&) noexcept; + + // [syncstream.osyncstream.members], member functions + void emit(); + streambuf_type* get_wrapped() const noexcept; + syncbuf_type* rdbuf() const noexcept { return const_cast(addressof(sb)); } + + private: + syncbuf_type sb; // exposition only + }; +} + +*/ + +#include <__config> +#include +#include +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// basic_syncbuf + + + +template +class _LIBCPP_TEMPLATE_VIS basic_syncbuf + : public basic_streambuf<_CharT, _Traits> +{ +public: + typedef _CharT char_type; + typedef _Traits traits_type; + typedef typename traits_type::int_type int_type; + typedef typename traits_type::pos_type pos_type; + typedef typename traits_type::off_type off_type; + typedef _Allocator allocator_type; + + typedef basic_streambuf<_CharT, _Traits> streambuf_type; + typedef _VSTD::shared_ptr<_VSTD::mutex> mutex_ptr_type; + typedef _VSTD::basic_string<_CharT, _Traits, _Allocator> string_type; + + // [syncstream.syncbuf.cons], construction and destruction + + inline _LIBCPP_INLINE_VISIBILITY + explicit basic_syncbuf(streambuf_type* __obuf = nullptr) + : basic_syncbuf(__obuf, _Allocator()) { } + + inline _LIBCPP_INLINE_VISIBILITY + basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc); + + inline _LIBCPP_INLINE_VISIBILITY + basic_syncbuf(basic_syncbuf&& __other); + + inline _LIBCPP_INLINE_VISIBILITY + ~basic_syncbuf(); + + // [syncstream.syncbuf.assign], assignment and swap + + inline _LIBCPP_INLINE_VISIBILITY + basic_syncbuf& operator=(basic_syncbuf&& __other); + + inline _LIBCPP_INLINE_VISIBILITY + void swap(basic_syncbuf&& __other); + + // [syncstream.syncbuf.members], member functions + + inline _LIBCPP_INLINE_VISIBILITY + bool emit(); + + inline _LIBCPP_INLINE_VISIBILITY + streambuf_type* get_wrapped() const noexcept; + + inline _LIBCPP_INLINE_VISIBILITY + allocator_type get_allocator() const noexcept; + + inline _LIBCPP_INLINE_VISIBILITY + void set_emit_on_sync(bool __b) noexcept; + +protected: + + // [syncstream.syncbuf.virtuals], overridden virtual functions + + int sync() override; + + int_type overflow(int_type __c = traits_type::eof()) override; + +private: + streambuf_type* __wrapped; + allocator_type __alloc; + string_type __str; + bool __emit_on_sync; + bool __should_flush; +}; + +// Each basic_streambuf needs to have exactly one mutex that it corresponds to. +// If there are 100 osyncstreams we still need each one to map to the correct mutex. +// So, below is a global map that maps streambuf pointers to mutex. This is used in emit. +// void* used as key so that only one instance of this function is created (no template). +_VSTD::map>& +__get_mtx_ptr_map() { + static _VSTD::map> __mtx_ptr_map; + return __mtx_ptr_map; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +basic_syncbuf<_CharT, _Traits, _Allocator>::basic_syncbuf(streambuf_type* __obuf, + _Allocator const& __alloc) + : __wrapped(__obuf), + __alloc(__alloc) +{ + auto& __mtx_ptr_map = __get_mtx_ptr_map(); + if (!__mtx_ptr_map.contains(__obuf)) + __mtx_ptr_map[__obuf] = _VSTD::make_shared<_VSTD::mutex>(); +} + +template +inline _LIBCPP_INLINE_VISIBILITY +basic_syncbuf<_CharT, _Traits, _Allocator>::basic_syncbuf(basic_syncbuf&& __other) + : __wrapped(_VSTD::move(__other.get_wrapped())), + __alloc (_VSTD::move(__other.get_allocator())) +{ + __other.__wrapped = nullptr; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +basic_syncbuf<_CharT, _Traits, _Allocator>::~basic_syncbuf() +{ +#ifndef _LIBCPP_NO_EXCEPTIONS + try { +#endif // _LIBCPP_NO_EXCEPTIONS + emit(); +#ifndef _LIBCPP_NO_EXCEPTIONS + } catch(...) { } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +// [syncstream.syncbuf.assign], assignment and swap + +template +inline _LIBCPP_INLINE_VISIBILITY +basic_syncbuf<_CharT, _Traits, _Allocator>& +basic_syncbuf<_CharT, _Traits, _Allocator>::operator=(basic_syncbuf&& __other) +{ + __wrapped = _VSTD::move(__other.get_wrapped()); + __alloc = _VSTD::move(__other.get_allocator()); + __other.__wrapped = nullptr; + return *this; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +void +basic_syncbuf<_CharT, _Traits, _Allocator>::swap(basic_syncbuf&& __other) +{ + basic_syncbuf __tmp(_VSTD::move(__other)); + __other = _VSTD::move(*this); + *this = _VSTD::move(__tmp); +} + +// [syncstream.syncbuf.members], member functions + +template +inline _LIBCPP_INLINE_VISIBILITY +bool +basic_syncbuf<_CharT, _Traits, _Allocator>::emit() +{ + bool __result = true; + auto& __mtx_ptr_map = __get_mtx_ptr_map(); + + if (__wrapped == nullptr || !__mtx_ptr_map.contains(__wrapped)) + __result = false; + + if (__result) + { + mutex_ptr_type __mtx = __mtx_ptr_map[__wrapped]; + if (__mtx == nullptr) return false; // this should NEVER happen. + + _VSTD::lock_guard<_VSTD::mutex> gaurd(*__mtx); + + if (this->pptr()) + { + __result &= (__wrapped->sputn(__str.data(), __str.size()) != -1); + this->setp(nullptr, nullptr); + } + + if (__should_flush) + __result &= (__wrapped->pubsync() != -1); + } + + __should_flush = false; + return __result; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +typename basic_syncbuf<_CharT, _Traits, _Allocator>::streambuf_type* +basic_syncbuf<_CharT, _Traits, _Allocator>::get_wrapped() const noexcept +{ + return __wrapped; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +typename basic_syncbuf<_CharT, _Traits, _Allocator>::allocator_type +basic_syncbuf<_CharT, _Traits, _Allocator>::get_allocator() const noexcept +{ + return __alloc; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +void +basic_syncbuf<_CharT, _Traits, _Allocator>::set_emit_on_sync(bool __b) noexcept +{ + __emit_on_sync = __b; +} + +// [syncstream.syncbuf.virtuals], overridden virtual functions + +template +int +basic_syncbuf<_CharT, _Traits, _Allocator>::sync() +{ + __should_flush = true; + + if (__emit_on_sync && !emit()) + return -1; + return 0; +} + +template +typename basic_syncbuf<_CharT, _Traits, _Allocator>::int_type +basic_syncbuf<_CharT, _Traits, _Allocator>::overflow(int_type __c) +{ + if (traits_type::eq_int_type(__c, traits_type::eof())) + return traits_type::not_eof(__c); + + if (this->pptr() == this->epptr()) + { +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + __str.resize(__str.capacity()); + + char_type *__p = static_cast(__str.data()); + this->setp(__p, __p + __str.size()); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + return traits_type::eof(); + } +#endif // _LIBCPP_NO_EXCEPTIONS + } + + return this->sputc(traits_type::to_char_type(__c)); +} + +// basic_osyncstream + +template +class _LIBCPP_TEMPLATE_VIS basic_osyncstream + : public basic_ostream<_CharT, _Traits> +{ +public: + typedef _CharT char_type; + typedef _Traits traits_type; + typedef typename traits_type::int_type int_type; + typedef typename traits_type::pos_type pos_type; + typedef typename traits_type::off_type off_type; + typedef _Allocator allocator_type; + + typedef basic_streambuf streambuf_type; + typedef basic_syncbuf syncbuf_type; + + // [syncstream.osyncstream.cons], construction and destruction + + basic_osyncstream(streambuf_type *__obuf, allocator_type const& __alloc) + : basic_ostream<_CharT, _Traits>(addressof(__sb)), + __sb(__obuf, __alloc) { } + + explicit basic_osyncstream(streambuf_type *__obuf) + : basic_osyncstream(__obuf, allocator_type()) { } + + basic_osyncstream(basic_ostream& __os, allocator_type const& __alloc) + : basic_osyncstream(__os.rdbuf(), __alloc) { } + + explicit basic_osyncstream(basic_ostream& __os) + : basic_osyncstream(__os, allocator_type()) { } + + basic_osyncstream(basic_osyncstream&& __other) noexcept; + + ~basic_osyncstream(); + + // [syncstream.osyncstream.assign], assignment + + basic_osyncstream& operator=(basic_osyncstream&& __other) noexcept; + + // [syncstream.osyncstream.members], member functions + + void emit(); + + streambuf_type* get_wrapped() const noexcept; + + syncbuf_type* rdbuf() const noexcept; + +private: + syncbuf_type __sb; +}; + +// [syncstream.osyncstream.cons], construction and destruction + +template +basic_osyncstream<_CharT, _Traits, _Allocator>::basic_osyncstream(basic_osyncstream&& __other) + noexcept + : basic_ostream<_CharT, _Traits>(addressof(__sb)), + __sb(_VSTD::move(__other.__sb)) +{ + this->set_rdbuf(addressof(__sb)); +} + +template +basic_osyncstream<_CharT, _Traits, _Allocator>::~basic_osyncstream() +{ +#ifndef _LIBCPP_NO_EXCEPTIONS + try { +#endif // _LIBCPP_NO_EXCEPTIONS + emit(); +#ifndef _LIBCPP_NO_EXCEPTIONS + } catch(...) { } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +// [syncstream.osyncstream.assign], assignment + +template +basic_osyncstream<_CharT, _Traits, _Allocator>& +basic_osyncstream<_CharT, _Traits, _Allocator>::operator=(basic_osyncstream&& __other) noexcept +{ +#ifndef _LIBCPP_NO_EXCEPTIONS + try { +#endif // _LIBCPP_NO_EXCEPTIONS + emit(); +#ifndef _LIBCPP_NO_EXCEPTIONS + } catch(...) { } +#endif // _LIBCPP_NO_EXCEPTIONS + + __sb = _VSTD::move(__other.__sb); + return *this; +} + +// [syncstream.osyncstream.members], member functions + +template +void +basic_osyncstream<_CharT, _Traits, _Allocator>::emit() +{ + if (__sb.emit() == false) + this->setstate(ios::badbit); +} + +template +typename basic_osyncstream<_CharT, _Traits, _Allocator>::streambuf_type* +basic_osyncstream<_CharT, _Traits, _Allocator>::get_wrapped() const noexcept +{ + return __sb.get_wrapped(); +} + +template +typename basic_osyncstream<_CharT, _Traits, _Allocator>::syncbuf_type* +basic_osyncstream<_CharT, _Traits, _Allocator>::rdbuf() const noexcept +{ + return const_cast(addressof(__sb)); +} + +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP_SYNCSTREAM diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/assign.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/assign.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_osyncstream; + +// basic_osyncstream& operator=(basic_osyncstream&& rhs) noexcept; + +#include +#include + +#include "test_macros.h" + +template +struct test_allocator : std::allocator +{ + int id; + int moves = 0; + int copies = 0; + + test_allocator(int id = 0) : id(id) { } + test_allocator(test_allocator const& other) + : id(other.id), moves(other.moves), copies(other.copies) { } + test_allocator(test_allocator&& other) + : id(std::move(other.id)), + moves(std::move(other.moves)), + copies(std::move(other.copies)) { } +}; + +template +class test_buf + : public std::basic_streambuf { +}; + +template +void test_assign() +{ + using OS = std::basic_osyncstream, test_allocator>; + + test_buf base; + test_allocator alloc(42); + OS out1(&base, alloc); + typename OS::syncbuf_type *buff1 = out1.rdbuf(); + + assert(out1.get_wrapped() == &base); + assert(buff1->get_wrapped() == &base); + + OS out2 = std::move(out1); + typename OS::syncbuf_type *buff2 = out2.rdbuf(); + + assert(out2.get_wrapped() == &base); + assert(buff2->get_wrapped() == &base); + assert(buff2->get_allocator().id == alloc.id); + assert(out1.get_wrapped() == nullptr); + assert(buff1->get_wrapped() == nullptr); +} + +int main(int, char**) +{ + test_assign(); + test_assign(); + test_assign(); + test_assign(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/const.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/const.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/const.pass.cpp @@ -0,0 +1,117 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_osyncstream; + +// basic_osyncstream(streambuf_type* buf, const Allocator& allocator); +// basic_osyncstream(basic_osyncstream&& other) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +struct test_allocator : std::allocator +{ + int id; + int moves = 0; + int copies = 0; + + test_allocator(int id = 0) : id(id) { } + test_allocator(test_allocator const& other) + : id(other.id), moves(other.moves), copies(other.copies) { } + test_allocator(test_allocator&& other) + : id(std::move(other.id)), + moves(std::move(other.moves)), + copies(std::move(other.copies)) { } +}; + +template +class test_buf + : public std::basic_streambuf { +}; + +template +void test_const() +{ + using OS = std::basic_osyncstream, test_allocator>; + + test_buf base; + test_allocator alloc(42); + OS out(&base, alloc); + typename OS::syncbuf_type *buff = out.rdbuf(); + + assert(out.get_wrapped() == &base); + assert(buff->get_wrapped() == &base); + assert(buff->get_allocator().id == alloc.id); +}; + +template +void test_rvalue() +{ + using OS = std::basic_osyncstream, test_allocator>; + + test_buf base; + test_allocator alloc(42); + OS out1(&base, alloc); + typename OS::syncbuf_type *buff1 = out1.rdbuf(); + + assert(out1.get_wrapped() == &base); + assert(buff1->get_wrapped() == &base); + + OS out2(std::move(out1)); + typename OS::syncbuf_type *buff2 = out2.rdbuf(); + + assert(out2.get_wrapped() == &base); + assert(buff2->get_wrapped() == &base); + assert(buff2->get_allocator().id == alloc.id); + assert(out1.get_wrapped() == nullptr); + assert(buff1->get_wrapped() == nullptr); +} + +template +void test_dest() +{ + using OS = std::basic_osyncstream, std::allocator>; + using SS = std::basic_ostringstream; + CharT c = 'f'; + + SS ss; + { + OS out(ss); + out << c; + assert(ss.str().empty()); + } + std::cout << ss.str() << std::endl; + assert(ss.str()[0] == c); +} + +int main(int, char**) +{ + test_const(); + test_const(); + test_const(); + test_const(); + + test_rvalue(); + test_rvalue(); + test_rvalue(); + test_rvalue(); + + test_dest(); +// test_dest(); + // other overloads aren't tested because of bast_ostringstream + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/members.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/members.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/members.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_osyncstream; + +// void emit(); +// streambuf_type* get_wrapped() const noexcept; + +#include +#include +#include + +#include "test_macros.h" + +template +class test_buf + : public std::basic_streambuf { +}; + +template +void test_emit() +{ + using OS = std::basic_osyncstream, std::allocator>; + using SS = std::basic_ostringstream; + CharT c = 'f'; + + SS ss; + OS out(ss); + out << c; + assert(ss.str().empty()); + out.emit(); + assert(ss.str()[0] == c); +} + +template +void test_get_wrapped() +{ + using OS = std::basic_osyncstream, std::allocator>; + + test_buf base; + std::allocator alloc; + OS out(&base, alloc); + assert(out.get_wrapped() == &base); + ASSERT_NOEXCEPT(out.get_wrapped()); +} + +template +void test_rdbuf() +{ + using OS = std::basic_osyncstream, std::allocator>; + + test_buf base; + std::allocator alloc; + OS out(&base, alloc); + assert(out.rdbuf() != nullptr); + ASSERT_SAME_TYPE(decltype(out.rdbuf()), + std::basic_syncbuf, std::allocator>*); + ASSERT_NOEXCEPT(out.rdbuf()); +} + +int main(int, char**) +{ + test_emit(); + test_emit(); + // other overloads aren't tested because of bast_ostringstream + + test_get_wrapped(); + test_get_wrapped(); + test_get_wrapped(); + + test_rdbuf(); + test_rdbuf(); + test_rdbuf(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/basic.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/basic.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/basic.pass.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_osyncstream; + +// void emit(); +// streambuf_type* get_wrapped() const noexcept; + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +using ms = std::chrono::milliseconds; +using OS = std::basic_osyncstream, std::allocator>; + + +static std::basic_ostringstream ss; +static const char a = 'a'; +static const char b = 'b'; +static const char c = 'c'; +static const char d = 'd'; + +void f1() { + OS out(ss); + out << a; + std::this_thread::sleep_for(ms(250)); + out << b; +} + +void f2() { + OS out(ss); + out << c; + out << d; +} + +void test_mutlithread() +{ + std::thread t1(f1); + std::thread t2(f2); + t1.join(); + t2.join(); + + assert(strcmp(ss.str().data(), "cdab")); +} + +int main(int, char**) +{ + // tested with char only for now + test_mutlithread(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_osyncstream; + +// void emit(); +// streambuf_type* get_wrapped() const noexcept; + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +using OS = std::basic_osyncstream, std::allocator>; + +static std::basic_ostringstream ss; +static std::unordered_multiset test_strings = +{ +"C++", "is", "a", "general-purpose", "programming", "language", "created", "by", "Bjarne", "Stroustrup", "as", "an", "extension", "of", "the", "C", "programming", "language,", "or", "C", "with", "Classes", "The", "language", "has", "expanded", "significantly", "over", "time,", "and", "modern", "C++", "has", "object-oriented,", "generic,", "and", "functional", "features", "in", "addition", "to", "facilities", "for", "low-level", "memory", "manipulation.", "It", "is", "almost", "always", "implemented", "as", "a", "compiled", "language,", "and", "many", "vendors", "provide", "C++", "compilers,", "including", "the", "Free", "Software", "Foundation,", "LLVM,", "Microsoft,", "Intel,", "and", "IBM,", "so", "it", "is", "available", "on", "many", "platforms." +}; + +void f(std::string text) { + OS out(ss); + out << text; +} + +int main(int, char**) +{ + std::vector threads; + for (std::string const& word : test_strings) + threads.push_back(std::thread(f, word)); + for (size_t i = 0; i < test_strings.size(); (void)++i) + threads[i].join(); + + std::string output = ss.str(); + for (std::string const& word : test_strings) + assert(output.find(word) != std::string::npos); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/types.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/types.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/types.pass.cpp @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// +// + +#include +#include +#include + +#include "test_macros.h" + +template +void test_member_types() +{ + using Buf = std::basic_syncbuf; + using OSS = std::basic_osyncstream; + + ASSERT_SAME_TYPE(typename Buf::char_type, CharT); + ASSERT_SAME_TYPE(typename Buf::traits_type, std::char_traits); + ASSERT_SAME_TYPE(typename Buf::allocator_type, std::allocator); + + ASSERT_SAME_TYPE(typename OSS::char_type, CharT); + ASSERT_SAME_TYPE(typename OSS::traits_type, std::char_traits); + ASSERT_SAME_TYPE(typename OSS::allocator_type, std::allocator); +} + +int main(int, char**) +{ + ASSERT_SAME_TYPE(std::syncbuf, std::basic_syncbuf); + ASSERT_SAME_TYPE(std::wsyncbuf, std::basic_syncbuf); + ASSERT_SAME_TYPE(std::osyncstream, std::basic_osyncstream); + ASSERT_SAME_TYPE(std::wosyncstream, std::basic_osyncstream); + + test_member_types(); + test_member_types(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/assign.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/assign.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf& operator=(basic_syncbuf&& rhs) noexcept; +// void swap(basic_syncbuf& other) noexcept; + +#include +#include + +#include "test_macros.h" + +template +class test_syncbuf + : public std::basic_syncbuf, std::allocator> { +}; + +template +void test_assign() +{ + using BuffT = std::basic_syncbuf, std::allocator>; + + test_syncbuf base; + std::allocator alloc; + BuffT buff1(&base, alloc); + BuffT buff2 = std::move(buff1); + + assert(buff1.get_wrapped() == nullptr); + assert(buff2.get_wrapped() == &base); + + // Also LWG issue? + // assert(buff1.rdbuf()->pbase() == buff1.rdbuf()->pptr()); +} + +template +void test_swap() +{ + using BuffT = std::basic_syncbuf, std::allocator>; + + test_syncbuf base1; + test_syncbuf base2; + std::allocator alloc; + BuffT buff1(&base1, alloc); + BuffT buff2(&base2, alloc); + buff1.swap(std::move(buff2)); + + assert(buff1.get_wrapped() == &base2); + assert(buff2.get_wrapped() == &base1); +} + +int main(int, char**) +{ + test_assign(); + test_assign(); + test_assign(); + test_assign(); + + test_swap(); + test_swap(); + test_swap(); + test_swap(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/cons.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/cons.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/cons.pass.cpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" + +template +struct test_allocator : std::allocator +{ + int id; + int moves = 0; + int copies = 0; + + test_allocator(int id = 0) : id(id) { } + test_allocator(test_allocator const& other) + : id(other.id), moves(other.moves), copies(other.copies) { } + test_allocator(test_allocator&& other) + : id(std::move(other.id)), + moves(std::move(other.moves)), + copies(std::move(other.copies)) { } +}; + +template +class test_buf + : public std::basic_streambuf { +}; + +template +void test_streambuf_const() +{ + using BuffT = std::basic_syncbuf, test_allocator>; + + test_buf base; + test_allocator alloc(42); + BuffT buff(&base, alloc); + + assert(buff.get_wrapped() == &base); + assert(buff.get_allocator().id == alloc.id); +} + +template +void test_move_const() +{ + using BuffT = std::basic_syncbuf, test_allocator>; + + test_buf base; + test_allocator alloc(42); + BuffT buff1(&base, alloc); + BuffT buff2(std::move(buff1)); + + assert(buff1.get_wrapped() == nullptr); + assert(buff2.get_wrapped() == &base); + + // LWG issue? + // assert(buff1.rdbuf()->pbase() == buff1.rdbuf()->pptr()); +} + +int main(int, char**) +{ + test_streambuf_const(); + test_streambuf_const(); + test_streambuf_const(); + test_streambuf_const(); + + test_move_const(); + test_move_const(); + test_move_const(); + test_move_const(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/members.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/members.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/members.pass.cpp @@ -0,0 +1,120 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" + +template +class test_buf : public std::basic_streambuf { +public: + int id; + + test_buf(int id = 0) : id(id) { } + + T *_pptr() { return this->pptr(); } +}; + +template> +class test_syncbuf + : public std::basic_syncbuf, Alloc> { +public: + test_syncbuf(test_buf* buf, Alloc alloc) + : std::basic_syncbuf, Alloc>(buf, alloc) { } + + void _setp(T *begin, T *end) { + return this->setp(begin, end); + } +}; + +template +struct test_allocator : std::allocator +{ + int id; + test_allocator(int id = 0) : id(id) { } +}; + +template +void test_emit() +{ + // We do this because we want to be able to any CharT + CharT arr[3] = { 'a', 'b', 'c' }; + CharT *ptr = arr; + + test_buf base; + std::allocator alloc; + test_syncbuf buff(&base, alloc); + + buff._setp(ptr, ptr + 3); + assert(base._pptr() == nullptr); + buff.emit(); + CharT *pptr = base._pptr(); + while(pptr) { assert(*pptr++ == *ptr++); } +} + +template +void test_get_wrapped() +{ + test_buf base(42); + std::allocator alloc; + test_syncbuf buff(&base, alloc); + assert(static_cast*>(buff.get_wrapped())->id == 42); + ASSERT_NOEXCEPT(buff.get_wrapped()); +} + +template +void test_get_allocator() +{ + test_buf base; + test_allocator alloc(42); + test_syncbuf> buff(&base, alloc); + assert(buff.get_allocator().id == 42); + ASSERT_NOEXCEPT(buff.get_allocator()); +} + +template +void test_set_emit_on_sync() +{ + // set_emit_on_sync tested in sync + test_syncbuf> buff(nullptr, std::allocator()); + ASSERT_NOEXCEPT(buff.set_emit_on_sync(false)); +} + +int main(int, char**) +{ + test_emit(); + test_emit(); + test_emit(); + test_emit(); + + test_get_wrapped(); + test_get_wrapped(); + test_get_wrapped(); + test_get_wrapped(); + + test_get_allocator(); + test_get_allocator(); + test_get_allocator(); + test_get_allocator(); + + test_set_emit_on_sync(); + test_set_emit_on_sync(); + test_set_emit_on_sync(); + test_set_emit_on_sync(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/special.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/special.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/special.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" + +template +class test_streambuf + : public std::basic_streambuf { +}; + +template +void test_swap() +{ + using BuffT = std::basic_syncbuf, std::allocator>; + + test_streambuf base1; + test_streambuf base2; + std::allocator alloc; + BuffT buff1(&base1, alloc); + BuffT buff2(&base2, alloc); + std::swap(buff1, buff2); + + assert(buff1.get_wrapped() == &base2); + assert(buff2.get_wrapped() == &base1); +} + +int main(int, char**) +{ + test_swap(); + test_swap(); + test_swap(); + test_swap(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/virtuals.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/virtuals.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/virtuals.pass.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include +#include +#include + +#include "test_macros.h" + +template +class test_buf : public std::basic_streambuf { +public: + T *_pbase() { return this->pbase(); } +}; + +template +class test_syncbuf + : public std::basic_syncbuf, std::allocator> { +public: + test_syncbuf(test_buf* buf, std::allocator alloc) + : std::basic_syncbuf, std::allocator>(buf, alloc) { } + + int _sync() { + return this->sync(); + } +}; + +template +void test_sync(bool emit_on_sync) +{ + std::basic_ostringstream ss; + test_buf *base = reinterpret_cast*>(ss.rdbuf()); + std::allocator alloc; + test_syncbuf buff(base, alloc); + std::basic_ostream out(&buff); + + buff.set_emit_on_sync(emit_on_sync); + out << 'a'; + assert(base->_pbase() == nullptr); + assert(buff._sync() == 0); + + if (emit_on_sync) + assert(*base->_pbase() == 'a'); + else + assert(base->_pbase() == nullptr); +} + +int main(int, char**) +{ + test_sync(true); + test_sync(false); + test_sync(true); + test_sync(false); + + return 0; +} \ No newline at end of file diff --git a/libcxx/www/cxx2a_status.html b/libcxx/www/cxx2a_status.html --- a/libcxx/www/cxx2a_status.html +++ b/libcxx/www/cxx2a_status.html @@ -58,7 +58,7 @@ P0020R6LWGFloating Point AtomicAlbuquerque - P0053R7LWGC++ Synchronized Buffered OstreamAlbuquerque + P0053R7LWGC++ Synchronized Buffered OstreamAlbuquerqueComplete P0202R3LWGAdd constexpr modifiers to functions in <algorithm> and <utility> HeadersAlbuquerqueIn Progress7.0 P0415R1LWGConstexpr for std::complexAlbuquerqueIn Progress7.0 P0439R0LWGMake std::memory_order a scoped enumerationAlbuquerqueComplete