diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -286,7 +286,7 @@ --------------------------------------------------- ----------------- ``__cpp_lib_string_view`` ``201803L`` --------------------------------------------------- ----------------- - ``__cpp_lib_syncbuf`` *unimplemented* + ``__cpp_lib_syncbuf`` ``201803L`` --------------------------------------------------- ----------------- ``__cpp_lib_three_way_comparison`` *unimplemented* --------------------------------------------------- ----------------- diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst --- a/libcxx/docs/ReleaseNotes/18.rst +++ b/libcxx/docs/ReleaseNotes/18.rst @@ -47,6 +47,7 @@ - P2443R1 - ``views::chunk_by`` - P2538R1 - ADL-proof ``std::projected`` - P2614R2 - Deprecate ``numeric_limits::has_denorm`` +- P0053R7 - C++ Synchronized Buffered Ostream (in the experimental library) Improvements and New Features diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -114,7 +114,7 @@ "`3096 `__","``path::lexically_relative``\ is confused by trailing slashes","San Diego","|Complete|","" "`3116 `__","``OUTERMOST_ALLOC_TRAITS``\ needs ``remove_reference_t``\ ","San Diego","","" "`3122 `__","``__cpp_lib_chrono_udls``\ was accidentally dropped","San Diego","|Complete|","" -"`3127 `__","``basic_osyncstream::rdbuf``\ needs a ``const_cast``\ ","San Diego","","" +"`3127 `__","``basic_osyncstream::rdbuf``\ needs a ``const_cast``\ ","San Diego","|Complete|","18.0" "`3128 `__","``strstream::rdbuf``\ needs a ``const_cast``\ ","San Diego","|Nothing To Do|","" "`3129 `__","``regex_token_iterator``\ constructor uses wrong pointer arithmetic","San Diego","","" "`3130 `__","|sect|\ [input.output] needs many ``addressof``\ ","San Diego","","" @@ -254,7 +254,7 @@ "`3330 `__","Include ````\ from most library headers","Prague","|Complete|","13.0","|spaceship|" "`3331 `__","Define ``totally_ordered/_with``\ in terms of ``partially-ordered-with``\ ","Prague","|Complete|","13.0" "`3332 `__","Issue in |sect|\ [time.format]","Prague","|Complete|","16.0","|chrono| |format|" -"`3334 `__","``basic_osyncstream``\ move assignment and destruction calls ``basic_syncbuf::emit()``\ twice","Prague","","" +"`3334 `__","``basic_osyncstream``\ move assignment and destruction calls ``basic_syncbuf::emit()``\ twice","Prague","|Complete|","18.0" "`3335 `__","Resolve C++20 NB comments US 273 and GB 274","Prague","|Complete|","15.0","|ranges|" "`3338 `__","Rename ``default_constructible``\ to ``default_initializable``\ ","Prague","|Complete|","13.0" "`3340 `__","Formatting functions should throw on argument/format string mismatch in |sect|\ [format.functions]","Prague","|Complete|","14.0","|format|" diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -3,7 +3,7 @@ "`P0674R1 `__","LWG","Extending make_shared to Support Arrays","Toronto","|Complete|","15.0" "","","","","","","" "`P0020R6 `__","LWG","Floating Point Atomic","Albuquerque","","" -"`P0053R7 `__","LWG","C++ Synchronized Buffered Ostream","Albuquerque","","" +"`P0053R7 `__","LWG","C++ Synchronized Buffered Ostream","Albuquerque","|Complete|","18.0" "`P0202R3 `__","LWG","Add constexpr modifiers to functions in and Headers","Albuquerque","|Complete|","12.0" "`P0415R1 `__","LWG","Constexpr for ``std::complex``\ ","Albuquerque","|Complete|","16.0" "`P0439R0 `__","LWG","Make ``std::memory_order``\ a scoped enumeration","Albuquerque","|Complete|","" diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv --- a/libcxx/docs/Status/Cxx23Issues.csv +++ b/libcxx/docs/Status/Cxx23Issues.csv @@ -122,7 +122,7 @@ `3566 `__,"Constraint recursion for ``operator<=>(optional, U)``","October 2021","|Complete|","17.0","|spaceship|" `3567 `__,"Formatting move-only iterators take two","October 2021","|Complete|","16.0","|format| |ranges|" `3568 `__,"``basic_istream_view`` needs to initialize ``value_``","October 2021","|Complete|","16.0","|ranges|" -`3570 `__,"``basic_osyncstream::emit`` should be an unformatted output function","October 2021","","" +`3570 `__,"``basic_osyncstream::emit`` should be an unformatted output function","October 2021","|Complete|","18.0" `3571 `__,"``flush_emit`` should set ``badbit`` if the ``emit`` call fails","October 2021","","" `3572 `__,"``copyable-box`` should be fully ``constexpr``","October 2021","|Complete|","14.0","|ranges|" `3573 `__,"Missing Throws element for ``basic_string_view(It begin, End end)``","October 2021","|Complete|","14.0" @@ -275,7 +275,7 @@ "`3857 `__","``basic_string_view`` should allow explicit conversion when only traits vary","February 2023","|Complete|","17.0","" "`3860 `__","``range_common_reference_t`` is missing","February 2023","|Complete|","17.0","|ranges|" "`3866 `__","Bad Mandates for ``expected::transform_error`` overloads","February 2023","|Complete|","17.0","" -"`3867 `__","Should ``std::basic_osyncstream``'s move assignment operator be ``noexcept``?","February 2023","","","" +"`3867 `__","Should ``std::basic_osyncstream``'s move assignment operator be ``noexcept``?","February 2023","|Complete|","18.0","" "`3441 `__","Misleading note about calls to customization points","February 2023","","","" "`3622 `__","Misspecified transitivity of equivalence in ยง[unord.req.general]","February 2023","","","" "`3631 `__","``basic_format_arg(T&&)`` should use ``remove_cvref_t`` throughout","February 2023","|Complete|","17.0","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -1000,6 +1000,7 @@ string.h string_view strstream + syncstream system_error tgmath.h thread diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -452,6 +452,7 @@ # define _LIBCPP_HAS_NO_INCOMPLETE_PSTL # define _LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN # define _LIBCPP_HAS_NO_INCOMPLETE_TZDB +# define _LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM # endif // Need to detect which libc we're using if we're on Linux. diff --git a/libcxx/include/__std_clang_module b/libcxx/include/__std_clang_module --- a/libcxx/include/__std_clang_module +++ b/libcxx/include/__std_clang_module @@ -200,6 +200,9 @@ #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) # include #endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif #include #include #if !defined(_LIBCPP_HAS_NO_THREADS) diff --git a/libcxx/include/iosfwd b/libcxx/include/iosfwd --- a/libcxx/include/iosfwd +++ b/libcxx/include/iosfwd @@ -90,6 +90,18 @@ using u16streampos = fpos::state_type>; using u32streampos = fpos::state_type>; +template , class Allocator = allocator> + class basic_syncbuf; // C++20 + +using syncbuf = basic_syncbuf; // C++20 +using wsyncbuf = basic_syncbuf; // C++20 + +template , class Allocator = allocator> + class basic_osyncstream; // C++20 + +using osyncstream = basic_osyncstream; // C++20 +using wosyncstream = basic_osyncstream; // C++20 + } // std */ @@ -130,6 +142,26 @@ typedef fpos u16streampos; typedef fpos u32streampos; +#if _LIBCPP_STD_VER >= 20 + +template , class _Allocator = allocator<_CharT>> +class basic_syncbuf; + +using syncbuf = basic_syncbuf; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +using wsyncbuf = basic_syncbuf; +#endif + +template , class _Allocator = allocator<_CharT>> +class basic_osyncstream; + +using osyncstream = basic_osyncstream; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +using wosyncstream = basic_osyncstream; +#endif + +#endif // _LIBCPP_STD_VER >=20 + // Include other forward declarations here template > class _LIBCPP_TEMPLATE_VIS vector; diff --git a/libcxx/include/map b/libcxx/include/map --- a/libcxx/include/map +++ b/libcxx/include/map @@ -1700,6 +1700,8 @@ _Tp& map<_Key, _Tp, _Compare, _Allocator>::operator[](key_type&& __k) { + // TODO investigate this clang-tidy warning. + // NOLINTNEXTLINE(bugprone-use-after-move) return __tree_.__emplace_unique_key_args(__k, _VSTD::piecewise_construct, _VSTD::forward_as_tuple(_VSTD::move(__k)), diff --git a/libcxx/include/streambuf b/libcxx/include/streambuf --- a/libcxx/include/streambuf +++ b/libcxx/include/streambuf @@ -110,6 +110,7 @@ #include <__assert> // all public C++ headers provide the assertion handler #include <__config> #include <__fwd/streambuf.h> +#include #include #include #include diff --git a/libcxx/include/syncstream b/libcxx/include/syncstream new file mode 100644 --- /dev/null +++ b/libcxx/include/syncstream @@ -0,0 +1,510 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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; + + // [syncstream.syncbuf.special], specialized algorithms + template + void swap(basic_syncbuf&, + 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&&); + + // [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 <__utility/move.h> +#include // required for declaration of default arguments +#include + +#ifndef _LIBCPP_HAS_NO_THREADS +# include +# include +# include +#endif + +// standard-mandated includes + +// [syncstream.syn] +#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 >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) + +// [syncstream.syncbuf.overview]/1 +// Class template basic_syncbuf stores character data written to it, +// known as the associated output, into internal buffers allocated +// using the object's allocator. The associated output is transferred +// to the wrapped stream buffer object *wrapped when emit() is called +// or when the basic_syncbuf object is destroyed. Such transfers are +// atomic with respect to transfers by other basic_syncbuf objects +// with the same wrapped stream buffer object. +// +// This helper singleton is used to implement the required +// synchronisation guarantees. +# ifndef _LIBCPP_HAS_NO_THREADS +class __wrapped_streambuf_mutex { + _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex() = default; + +public: + __wrapped_streambuf_mutex(const __wrapped_streambuf_mutex&) = delete; + __wrapped_streambuf_mutex& operator=(const __wrapped_streambuf_mutex&) = delete; + + _LIBCPP_HIDE_FROM_ABI void __inc_reference([[maybe_unused]] void* __ptr) { + _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to"); + unique_lock __lock{__mutex_}; + ++__lut_[reinterpret_cast(__ptr)].__count; + } + + // pre: __ptr is in __lut_ + _LIBCPP_HIDE_FROM_ABI void __dec_reference([[maybe_unused]] void* __ptr) noexcept { + unique_lock __lock{__mutex_}; + + auto __it = __get_it(__ptr); + if (__it->second.__count == 1) + __lut_.erase(__it); + else + --__it->second.__count; + } + + // TODO + // This function causes emit() aquire two mutexes: + // - __mutex_ shared + // _ __get_it(__ptr)->second.__mutex exclusive + // + // Instead store a pointer to __get_it(__ptr)->second.__mutex when + // calling __inc_reference. + // + // pre: __ptr is in __lut_ + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard __get_lock([[maybe_unused]] void* __ptr) noexcept { + shared_lock __lock{__mutex_}; + return lock_guard{__get_it(__ptr)->second.__mutex}; + } + + // This function is used for testing. + // + // It is allowed to call this function with a non-registered pointer. + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __get_count([[maybe_unused]] void* __ptr) noexcept { + _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to"); + shared_lock __lock{__mutex_}; + + auto __it = __lut_.find(reinterpret_cast(__ptr)); + return __it != __lut_.end() ? __it->second.__count : 0; + } + + [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex& __instance() noexcept { + static __wrapped_streambuf_mutex __result; + return __result; + } + +private: + struct __value { + mutex __mutex; + size_t __count{0}; + }; + + shared_mutex __mutex_; + map __lut_; + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI map::iterator __get_it(void* __ptr) noexcept { + _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to"); + + auto __it = __lut_.find(reinterpret_cast(__ptr)); + _LIBCPP_ASSERT_INTERNAL(__it != __lut_.end(), "using a wrapped streambuf that has not been registered"); + _LIBCPP_ASSERT_INTERNAL(__it->second.__count >= 1, "found an inactive streambuf wrapper"); + return __it; + } +}; +# endif // _LIBCPP_HAS_NO_THREADS + +// basic_syncbuf + +// The class uses a basic_string<_CharT, _Traits, _Allocator> as +// internal buffer. Per [syncstream.syncbuf.cons]/4 +// Remarks: A copy of allocator is used to allocate memory for +// internal buffers holding the associated output. +// +// Therefore the allocator used in the constructor is passed to the +// basic_string. The class does not keep a copy of this allocator. +template +class _LIBCPP_TEMPLATE_VIS basic_syncbuf : public basic_streambuf<_CharT, _Traits> { +public: + using char_type = _CharT; + using traits_type = _Traits; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + using allocator_type = _Allocator; + + using streambuf_type = basic_streambuf<_CharT, _Traits>; + + // [syncstream.syncbuf.cons], construction and destruction + + _LIBCPP_HIDE_FROM_ABI explicit basic_syncbuf(streambuf_type* __obuf = nullptr) + : basic_syncbuf(__obuf, _Allocator()) {} + + _LIBCPP_HIDE_FROM_ABI basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc) + : __wrapped_(__obuf), __str_(__alloc) { + __inc_reference(); + } + + _LIBCPP_HIDE_FROM_ABI basic_syncbuf(basic_syncbuf&& __other) + : __wrapped_(__other.get_wrapped()), __str_(std::move(__other.__str_)), __emit_on_sync_(__other.__emit_on_sync_) { + __move_common(__other); + } + + _LIBCPP_HIDE_FROM_ABI ~basic_syncbuf() { +# ifndef _LIBCPP_HAS_NO_EXCEPTIONS + try { +# endif // _LIBCPP_HAS_NO_EXCEPTIONS + emit(); +# ifndef _LIBCPP_HAS_NO_EXCEPTIONS + } catch (...) { + } +# endif // _LIBCPP_HAS_NO_EXCEPTIONS + __dec_reference(); + } + + // [syncstream.syncbuf.assign], assignment and swap + + _LIBCPP_HIDE_FROM_ABI basic_syncbuf& operator=(basic_syncbuf&& __other) { + // The function is specified to call emit. This call should + // propagate the exception thrown. + emit(); + __dec_reference(); + + __wrapped_ = __other.get_wrapped(); + __str_ = std::move(__other.__str_); + __emit_on_sync_ = __other.__emit_on_sync_; + + __move_common(__other); + + return *this; + } + + _LIBCPP_HIDE_FROM_ABI void swap(basic_syncbuf& __other) { + _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR( + allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(), + "violates the mandated swap precondition"); + + basic_syncbuf __tmp(std::move(__other)); + __other = std::move(*this); + *this = std::move(__tmp); + } + + // [syncstream.syncbuf.members], member functions + + _LIBCPP_HIDE_FROM_ABI bool emit() { return emit(false); } + + _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; } + + _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __str_.get_allocator(); } + + _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; } + +protected: + // [syncstream.syncbuf.virtuals], overridden virtual functions + + _LIBCPP_HIDE_FROM_ABI_VIRTUAL + int sync() override { + if (__emit_on_sync_ && !emit(true)) + return -1; + return 0; + } + + _LIBCPP_HIDE_FROM_ABI_VIRTUAL + int_type overflow(int_type __c = traits_type::eof()) override { + if (traits_type::eq_int_type(__c, traits_type::eof())) + return traits_type::not_eof(__c); + + if (this->pptr() == this->epptr()) { +# ifndef _LIBCPP_HAS_NO_EXCEPTIONS + try { +# endif + size_t __size = __str_.size(); + __str_.resize(__str_.capacity() + 1); + _LIBCPP_ASSERT_INTERNAL(__str_.size() > __size, "the buffer hasn't grown"); + + char_type* __p = static_cast(__str_.data()); + this->setp(__p, __p + __str_.size()); + this->pbump(__size); + +# ifndef _LIBCPP_HAS_NO_EXCEPTIONS + } catch (...) { + return traits_type::eof(); + } +# endif + } + + return this->sputc(traits_type::to_char_type(__c)); + } + +private: + streambuf_type* __wrapped_; + + // TODO Use a more generic buffer. + // That buffer should be light with almost no additional headers. Then + // it can be use here, the __retarget_buffer, and place that use + // the now deprecated get_temporary_buffer + + basic_string<_CharT, _Traits, _Allocator> __str_; + bool __emit_on_sync_{false}; + + _LIBCPP_HIDE_FROM_ABI bool emit(bool __flush) { + if (!__wrapped_) + return false; + +# ifndef _LIBCPP_HAS_NO_THREADS + lock_guard __lock = __wrapped_streambuf_mutex::__instance().__get_lock(__wrapped_); +# endif + + bool __result = true; + if (this->pptr() != this->pbase()) { + _LIBCPP_ASSERT_INTERNAL(this->pbase() && this->pptr() && this->epptr(), "all put area pointers shold be valid"); + + // The __str_ does not know how much of its buffer is used. This + // information is extracted from the information of the base class. + __result &= (__wrapped_->sputn(this->pbase(), this->pptr() - this->pbase()) != -1); + // Clears the buffer, but keeps the contents (and) size of the + // internal buffer. + this->setp(this->pbase(), this->epptr()); + } + + if (__flush) + __result &= (__wrapped_->pubsync() != -1); + + return __result; + } + + _LIBCPP_HIDE_FROM_ABI void __move_common(basic_syncbuf& __other) { + // Adjust the put area pointers to our buffer. + char_type* __p = static_cast(__str_.data()); + this->setp(__p, __p + __str_.size()); + this->pbump(__other.pptr() - __other.pbase()); + + // Clear __other_ so the destructor will act as a NOP. + __other.setp(nullptr, nullptr); + __other.__wrapped_ = nullptr; + } + + _LIBCPP_HIDE_FROM_ABI void __inc_reference() { +# ifndef _LIBCPP_HAS_NO_THREADS + if (__wrapped_) + __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_); +# endif + } + + _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept { +# ifndef _LIBCPP_HAS_NO_THREADS + if (__wrapped_) + __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_); +# endif + } +}; + +using std::syncbuf; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +using std::wsyncbuf; +# endif + +// [syncstream.syncbuf.special], specialized algorithms +template +_LIBCPP_HIDE_FROM_ABI void +swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __lhs, basic_syncbuf<_CharT, _Traits, _Allocator>& __rhs) { + __lhs.swap(__rhs); +} + +// basic_osyncstream + +template +class _LIBCPP_TEMPLATE_VIS basic_osyncstream : public basic_ostream<_CharT, _Traits> { +public: + using char_type = _CharT; + using traits_type = _Traits; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + + using allocator_type = _Allocator; + using streambuf_type = basic_streambuf; + using syncbuf_type = basic_syncbuf; + + // [syncstream.osyncstream.cons], construction and destruction + + _LIBCPP_HIDE_FROM_ABI basic_osyncstream(streambuf_type* __obuf, allocator_type const& __alloc) + : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(__obuf, __alloc) {} + + _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(streambuf_type* __obuf) + : basic_osyncstream(__obuf, allocator_type()) {} + + _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_ostream& __os, allocator_type const& __alloc) + : basic_osyncstream(__os.rdbuf(), __alloc) {} + + _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(basic_ostream& __os) + : basic_osyncstream(__os, allocator_type()) {} + + _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_osyncstream&& __other) noexcept + : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(std::move(__other.__sb_)) { + this->set_rdbuf(std::addressof(__sb_)); + } + + // [syncstream.osyncstream.assign], assignment + + _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) = default; + + // [syncstream.osyncstream.members], member functions + + _LIBCPP_HIDE_FROM_ABI void emit() { + // The basic_ostream::put places the sentry in a try + // catch, this does not match the wording of the standard + // [ostream.unformatted] + // TODO validate other unformatted output functions. + typename basic_ostream::sentry __s(*this); + if (__s) { +# ifndef _LIBCPP_HAS_NO_EXCEPTIONS + try { +# endif + + if (__sb_.emit() == false) + this->setstate(ios::badbit); +# ifndef _LIBCPP_HAS_NO_EXCEPTIONS + } catch (...) { + this->__set_badbit_and_consider_rethrow(); + } +# endif + } + } + + _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); } + + _LIBCPP_HIDE_FROM_ABI syncbuf_type* rdbuf() const noexcept { + return const_cast(std::addressof(__sb_)); + } + +private: + syncbuf_type __sb_; +}; + +using std::osyncstream; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +using std::wosyncstream; +# endif + +#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP_SYNCSTREAM diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -401,7 +401,9 @@ # define __cpp_lib_starts_ends_with 201711L # undef __cpp_lib_string_view # define __cpp_lib_string_view 201803L -// # define __cpp_lib_syncbuf 201803L +# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) +# define __cpp_lib_syncbuf 201803L +# endif // # define __cpp_lib_three_way_comparison 201907L # define __cpp_lib_to_address 201711L # define __cpp_lib_to_array 201907L diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in --- a/libcxx/modules/std.cppm.in +++ b/libcxx/modules/std.cppm.in @@ -148,6 +148,9 @@ #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) # include #endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif #include #if !defined(_LIBCPP_HAS_NO_THREADS) # include @@ -189,9 +192,6 @@ #if __has_include() # error "update the header information for in libcxx/utils/generate_std_cppm_in.py" #endif // __has_include() -#if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() #if __has_include() # error "update the header information for in libcxx/utils/generate_std_cppm_in.py" #endif // __has_include() diff --git a/libcxx/modules/std/iosfwd.inc b/libcxx/modules/std/iosfwd.inc --- a/libcxx/modules/std/iosfwd.inc +++ b/libcxx/modules/std/iosfwd.inc @@ -16,8 +16,19 @@ using std::u32streampos; using std::u8streampos; + using std::basic_osyncstream; + using std::basic_syncbuf; + using std::istreambuf_iterator; using std::ostreambuf_iterator; + using std::osyncstream; + using std::syncbuf; + +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + using std::wosyncstream; + using std::wsyncbuf; +#endif + using std::fpos; } // namespace std diff --git a/libcxx/modules/std/syncstream.inc b/libcxx/modules/std/syncstream.inc --- a/libcxx/modules/std/syncstream.inc +++ b/libcxx/modules/std/syncstream.inc @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// export namespace std { -#if 0 +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) using std::basic_syncbuf; // [syncstream.syncbuf.special], specialized algorithms @@ -24,5 +24,5 @@ # ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS using std::wosyncstream; # endif -#endif +#endif // !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) } // namespace std diff --git a/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp b/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp --- a/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp +++ b/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp @@ -27,3 +27,7 @@ #ifdef _LIBCPP_HAS_NO_INCOMPLETE_TZDB # error "-fexperimental-library should enable the chrono TZDB" #endif + +#ifdef _LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM +# error "-fexperimental-library should enable the syncstream header" +#endif diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv --- a/libcxx/test/libcxx/transitive_includes/cxx03.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv @@ -813,6 +813,7 @@ stop_token ratio stop_token type_traits stop_token version +streambuf climits streambuf cstdint streambuf ios streambuf iosfwd @@ -859,6 +860,13 @@ strstream istream strstream ostream strstream version +syncstream cstddef +syncstream iosfwd +syncstream map +syncstream mutex +syncstream ostream +syncstream shared_mutex +syncstream string system_error cerrno system_error compare system_error cstddef diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv --- a/libcxx/test/libcxx/transitive_includes/cxx11.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv @@ -819,6 +819,7 @@ stop_token ratio stop_token type_traits stop_token version +streambuf climits streambuf cstdint streambuf ios streambuf iosfwd @@ -865,6 +866,13 @@ strstream istream strstream ostream strstream version +syncstream cstddef +syncstream iosfwd +syncstream map +syncstream mutex +syncstream ostream +syncstream shared_mutex +syncstream string system_error cerrno system_error compare system_error cstddef diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv --- a/libcxx/test/libcxx/transitive_includes/cxx14.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv @@ -821,6 +821,7 @@ stop_token ratio stop_token type_traits stop_token version +streambuf climits streambuf cstdint streambuf ios streambuf iosfwd @@ -867,6 +868,13 @@ strstream istream strstream ostream strstream version +syncstream cstddef +syncstream iosfwd +syncstream map +syncstream mutex +syncstream ostream +syncstream shared_mutex +syncstream string system_error cerrno system_error compare system_error cstddef diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -821,6 +821,7 @@ stop_token ratio stop_token type_traits stop_token version +streambuf climits streambuf cstdint streambuf ios streambuf iosfwd @@ -867,6 +868,13 @@ strstream istream strstream ostream strstream version +syncstream cstddef +syncstream iosfwd +syncstream map +syncstream mutex +syncstream ostream +syncstream shared_mutex +syncstream string system_error cerrno system_error compare system_error cstddef diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -826,6 +826,7 @@ stop_token ratio stop_token type_traits stop_token version +streambuf climits streambuf cstdint streambuf ios streambuf iosfwd @@ -872,6 +873,13 @@ strstream istream strstream ostream strstream version +syncstream cstddef +syncstream iosfwd +syncstream map +syncstream mutex +syncstream ostream +syncstream shared_mutex +syncstream string system_error cerrno system_error compare system_error cstddef diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -564,6 +564,7 @@ stop_token limits stop_token ratio stop_token version +streambuf climits streambuf ios streambuf iosfwd streambuf version @@ -597,6 +598,13 @@ strstream istream strstream ostream strstream version +syncstream cstddef +syncstream iosfwd +syncstream map +syncstream mutex +syncstream ostream +syncstream shared_mutex +syncstream string system_error cerrno system_error compare system_error cstddef diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -564,6 +564,7 @@ stop_token limits stop_token ratio stop_token version +streambuf climits streambuf ios streambuf iosfwd streambuf version @@ -597,6 +598,13 @@ strstream istream strstream ostream strstream version +syncstream cstddef +syncstream iosfwd +syncstream map +syncstream mutex +syncstream ostream +syncstream shared_mutex +syncstream string system_error cerrno system_error compare system_error cstddef 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,101 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// basic_osyncstream& operator=(basic_osyncstream&& rhs); + +#include +#include +#include + +#include "test_macros.h" +#include "test_allocator.h" + +template +static void test() { + using Traits = std::char_traits; + using Allocator = std::conditional_t, test_allocator>; + static_assert(std::allocator_traits::propagate_on_container_move_assignment::value == propagate); + + using OS = std::basic_osyncstream; + + std::basic_stringbuf base1; + std::basic_stringbuf base2; + + { + OS out1{&base1, Allocator{42}}; + assert(out1.get_wrapped() == &base1); + + typename OS::syncbuf_type* sb1 = out1.rdbuf(); + assert(sb1->get_wrapped() == &base1); + assert(sb1->get_allocator().get_data() == 42); + + out1 << CharT('A'); + + static_assert(!noexcept(out1.operator=(std::move(out1)))); // LWG-3867 + OS out2{&base2, Allocator{99}}; + + out2 << CharT('Z'); + + // Validate the data is still in the syncbuf and not in the stringbuf. + assert(base1.str().empty()); + assert(base2.str().empty()); + + out2 = std::move(out1); + + // Since sb2 is overwritten by the move its data should be in its stringbuf. + assert(base1.str().empty()); + assert(base2.str().size() == 1); + assert(base2.str()[0] == CharT('Z')); + + assert(out2.get_wrapped() == &base1); + + typename OS::syncbuf_type* sb2 = out2.rdbuf(); + assert(sb2->get_wrapped() == &base1); + if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) + assert(sb2->get_allocator().get_data() == 42); + else + assert(sb2->get_allocator().get_data() == 99); + + assert(out1.get_wrapped() == nullptr); + assert(sb1->get_wrapped() == nullptr); + + // The data written to 2 will be stored in sb1. The write happens after the destruction. + out2 << CharT('B'); + assert(base1.str().empty()); + } + + assert(base1.str().size() == 2); + assert(base1.str()[0] == CharT('A')); + assert(base1.str()[1] == CharT('B')); + assert(base2.str().size() == 1); + assert(base2.str()[0] == CharT('Z')); +} + +template +static void test() { + test(); + test(); +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/members/emit.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/members/emit.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/members/emit.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// void emit(); + +#include +#include +#include + +#include "test_macros.h" + +template +void test() { + using OS = std::basic_osyncstream; + 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); +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/members/get_wrapped.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/members/get_wrapped.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/members/get_wrapped.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// streambuf_type* get_wrapped() const noexcept; + +#include +#include +#include + +#include "test_macros.h" + +template +void test() { + std::basic_stringbuf base; + const std::basic_osyncstream out{&base}; + assert(out.get_wrapped() == &base); + ASSERT_NOEXCEPT(out.get_wrapped()); +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/members/rdbuf.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/members/rdbuf.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/members/rdbuf.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// syncbuf_type* rdbuf() const noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +void test() { + const std::basic_osyncstream out{nullptr}; + assert(out.rdbuf() != nullptr); + ASSERT_NOEXCEPT(out.rdbuf()); +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.move.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.move.pass.cpp @@ -0,0 +1,135 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// basic_osyncstream(basic_osyncstream&&) noexcept; + +// TODO Why is this noexcept? +// Does the reasoning for https://cplusplus.github.io/LWG/issue3867 not hold true here? + +#include +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +template +void test() { + { + using OS = std::basic_osyncstream; + using W = std::basic_syncbuf; + + const std::allocator alloc; + { + OS os = {OS{nullptr, alloc}}; + assert(os.get_wrapped() == nullptr); + assert(os.rdbuf()->get_wrapped() == nullptr); + assert(os.rdbuf()->get_allocator() == alloc); + ASSERT_NOEXCEPT(OS{std::move(os)}); + } + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + OS os = {OS{&w, alloc}}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(os.get_wrapped() == &w); + assert(os.rdbuf()->get_wrapped() == &w); + assert(os.rdbuf()->get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using OS = std::basic_osyncstream>; + using W = std::basic_stringbuf>; + + const std::allocator alloc; + { + OS os = {OS{nullptr, alloc}}; + assert(os.get_wrapped() == nullptr); + assert(os.rdbuf()->get_wrapped() == nullptr); + assert(os.rdbuf()->get_allocator() == alloc); + } + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + OS os = {OS{&w, alloc}}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(os.get_wrapped() == &w); + assert(os.rdbuf()->get_wrapped() == &w); + assert(os.rdbuf()->get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using OS = std::basic_osyncstream, test_allocator>; + using W = std::basic_stringbuf, test_allocator>; + + const test_allocator alloc; + { + OS os = {OS{nullptr, alloc}}; + assert(os.get_wrapped() == nullptr); + assert(os.rdbuf()->get_wrapped() == nullptr); + assert(os.rdbuf()->get_allocator() == alloc); + } + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + OS os = {OS{&w, alloc}}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(os.get_wrapped() == &w); + assert(os.rdbuf()->get_wrapped() == &w); + assert(os.rdbuf()->get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.ostream.allocator.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.ostream.allocator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.ostream.allocator.pass.cpp @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// basic_osyncstream(basic_ostream& os, const Allocator& allocator); + +#include +#include +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +template +void test() { + { + using OS = std::basic_osyncstream; + using W = std::basic_ostringstream; + + const std::allocator alloc; + + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + { + OS os = {w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 1); +#endif + assert(os.get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + } + } + { + using OS = std::basic_osyncstream>; + using W = std::basic_ostringstream>; + + const std::allocator alloc; + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + { + OS os = {w.rdbuf(), alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 1); +#endif + assert(os.get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + } + } + + { + using OS = std::basic_osyncstream, test_allocator>; + using W = std::basic_ostringstream, test_allocator>; + + const test_allocator alloc; + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + { + OS os = {w.rdbuf(), alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 1); +#endif + assert(os.get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + } + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.ostream.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.ostream.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.ostream.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// explicit basic_osyncstream(basic_ostream& os); + +#include +#include +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +template +void test() { + { + using OS = std::basic_osyncstream; + using W = std::basic_ostringstream; + + static_assert(!std::convertible_to&, OS>); + static_assert(std::constructible_from&>); + + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + { + OS os{w}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 1); +#endif + assert(os.get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_allocator() == std::allocator()); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + } + } + + { + using OS = std::basic_osyncstream>; + using W = std::basic_ostringstream>; + + static_assert(!std::convertible_to>&, OS>); + static_assert(std::constructible_from>&>); + + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + { + OS os{w}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 1); +#endif + assert(os.get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_allocator() == std::allocator()); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + } + } + + { + using OS = std::basic_osyncstream, test_allocator>; + using W = std::basic_ostringstream, test_allocator>; + + static_assert(!std::convertible_to>&, OS>); + static_assert(std::constructible_from>&>); + + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + { + OS os{w}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 1); +#endif + assert(os.get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_wrapped() == w.rdbuf()); + assert(os.rdbuf()->get_allocator() == test_allocator()); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(w.rdbuf()) == 0); +#endif + } + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.pointer.allocator.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.pointer.allocator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.pointer.allocator.pass.cpp @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// basic_osyncstream(streambuf_type* os, const Allocator& allocator); + +#include +#include +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +template +void test() { + { + using OS = std::basic_osyncstream; + using W = std::basic_stringbuf; + + const std::allocator alloc; + { + OS os = {nullptr, alloc}; + assert(os.get_wrapped() == nullptr); + assert(os.rdbuf()->get_wrapped() == nullptr); + assert(os.rdbuf()->get_allocator() == alloc); + } + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + OS os = {&w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(os.get_wrapped() == &w); + assert(os.rdbuf()->get_wrapped() == &w); + assert(os.rdbuf()->get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using OS = std::basic_osyncstream>; + using W = std::basic_stringbuf>; + + const std::allocator alloc; + { + OS os = {nullptr, alloc}; + assert(os.get_wrapped() == nullptr); + assert(os.rdbuf()->get_wrapped() == nullptr); + assert(os.rdbuf()->get_allocator() == alloc); + } + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + OS os = {&w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(os.get_wrapped() == &w); + assert(os.rdbuf()->get_wrapped() == &w); + assert(os.rdbuf()->get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using OS = std::basic_osyncstream, test_allocator>; + using W = std::basic_stringbuf, test_allocator>; + + const test_allocator alloc; + { + OS os = {nullptr, alloc}; + assert(os.get_wrapped() == nullptr); + assert(os.rdbuf()->get_wrapped() == nullptr); + assert(os.rdbuf()->get_allocator() == alloc); + } + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + OS os = {&w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(os.get_wrapped() == &w); + assert(os.rdbuf()->get_wrapped() == &w); + assert(os.rdbuf()->get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.pointer.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.pointer.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/syncstream.osyncstream.cons/cons.pointer.pass.cpp @@ -0,0 +1,140 @@ +//==----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// explicit basic_osyncstream(streambuf_type* os); + +#include +#include +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +template +void test() { + { + using OS = std::basic_osyncstream; + using W = std::basic_stringbuf; + + static_assert(!std::convertible_to*, OS>); + static_assert(std::constructible_from*>); + + { + OS os{nullptr}; + assert(os.get_wrapped() == nullptr); + assert(os.rdbuf()->get_wrapped() == nullptr); + assert(os.rdbuf()->get_allocator() == std::allocator()); + } + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + OS os{&w}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(os.get_wrapped() == &w); + assert(os.rdbuf()->get_wrapped() == &w); + assert(os.rdbuf()->get_allocator() == std::allocator()); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using OS = std::basic_osyncstream>; + using W = std::basic_stringbuf>; + + static_assert(!std::convertible_to>*, OS>); + static_assert(std::constructible_from>*>); + + { + OS os{nullptr}; + assert(os.get_wrapped() == nullptr); + assert(os.rdbuf()->get_wrapped() == nullptr); + assert(os.rdbuf()->get_allocator() == std::allocator()); + } + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + OS os{&w}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(os.get_wrapped() == &w); + assert(os.rdbuf()->get_wrapped() == &w); + assert(os.rdbuf()->get_allocator() == std::allocator()); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using OS = std::basic_osyncstream, test_allocator>; + using W = std::basic_stringbuf, test_allocator>; + + static_assert( + !std::convertible_to, test_allocator>*, OS>); + static_assert( + std::constructible_from, test_allocator>*>); + + { + OS os{nullptr}; + assert(os.get_wrapped() == nullptr); + assert(os.rdbuf()->get_wrapped() == nullptr); + assert(os.rdbuf()->get_allocator() == test_allocator()); + } + { + W w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + OS os{&w}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(os.get_wrapped() == &w); + assert(os.rdbuf()->get_wrapped() == &w); + assert(os.rdbuf()->get_allocator() == test_allocator()); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} 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,60 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: no-threads +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// Basic test whether the code works in a threaded environment. +// Using timing the output order should be stable. +// several_threads.pass.cpp tests with more threads. + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +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() { + std::basic_osyncstream out(ss); + out << a; + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + out << b; +} + +void f2() { + std::basic_osyncstream out(ss); + out << c; + out << d; +} + +int main(int, char**) { + std::thread t1(f1); + std::thread t2(f2); + t1.join(); + t2.join(); + + assert(ss.str() == "cdab"); + + return 0; +} 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,144 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: no-threads +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_osyncstream; + +// The test writes all elements in test_strings in a random order in ss. Every +// write is done by an osyncstream. This means the output is in random order, +// but the words should be written without interleaving. To increment the +// change of interleaving words are written one character at a time. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +static std::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) { + std::osyncstream out(ss); + for (char c : text) + out << c; +} + +void test() { + ss = std::basic_ostringstream(); + std::vector threads; + for (std::string const& word : test_strings) + threads.push_back(std::thread(f, word)); + + for (auto& thread : threads) + thread.join(); + + std::string output = ss.str(); + for (const std::string& word : test_strings) + assert(output.find(word) != std::string::npos); +} + +int main(int, char**) { + // The more we test, the more likely we catch an error + for (size_t i = 0; i < 1024; ++i) + test(); + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/types.compile.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/types.compile.pass.cpp @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// +// +// template, class Allocator = allocator> +// 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; + +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +static_assert(std::same_as, + std::basic_osyncstream, std::allocator>>); +static_assert(std::same_as>, + std::basic_osyncstream, std::allocator>>); + +static_assert( + std::same_as, test_allocator>::char_type, char>); +static_assert(std::same_as, test_allocator>::int_type, + constexpr_char_traits::int_type>); +static_assert(std::same_as, test_allocator>::pos_type, + constexpr_char_traits::pos_type>); +static_assert(std::same_as, test_allocator>::off_type, + constexpr_char_traits::off_type>); +static_assert(std::same_as, test_allocator>::traits_type, + constexpr_char_traits>); +static_assert( + std::same_as, test_allocator>::allocator_type, + test_allocator>); +static_assert( + std::same_as, test_allocator>::streambuf_type, + std::basic_streambuf>>); +static_assert( + std::same_as, test_allocator>::syncbuf_type, + std::basic_syncbuf, test_allocator>>); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + +static_assert(std::same_as, + std::basic_osyncstream, std::allocator>>); +static_assert(std::same_as>, + std::basic_osyncstream, std::allocator>>); + +static_assert( + std::same_as, test_allocator>::char_type, + wchar_t>); +static_assert( + std::same_as, test_allocator>::int_type, + constexpr_char_traits::int_type>); +static_assert( + std::same_as, test_allocator>::pos_type, + constexpr_char_traits::pos_type>); +static_assert( + std::same_as, test_allocator>::off_type, + constexpr_char_traits::off_type>); +static_assert( + std::same_as, test_allocator>::traits_type, + constexpr_char_traits>); +static_assert(std::same_as< + std::basic_osyncstream, test_allocator>::allocator_type, + test_allocator>); +static_assert(std::same_as< + std::basic_osyncstream, test_allocator>::streambuf_type, + std::basic_streambuf>>); +static_assert( + std::same_as, test_allocator>::syncbuf_type, + std::basic_syncbuf, test_allocator>>); + +#endif // TEST_HAS_NO_WIDE_CHARACTERS diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/helpers.h b/libcxx/test/std/input.output/syncstream/syncbuf/helpers.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/helpers.h @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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 TEST_STD_INPUT_OUTPUT_SYNCSTREAM_SYNCBUF_SYNCSTREAM_SYNCBUF_MEMBERS_H +#define TEST_STD_INPUT_OUTPUT_SYNCSTREAM_SYNCBUF_SYNCSTREAM_SYNCBUF_MEMBERS_H + +#include + +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) {} +}; + +#endif // TEST_STD_INPUT_OUTPUT_SYNCSTREAM_SYNCBUF_SYNCSTREAM_SYNCBUF_MEMBERS_H diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/sputc.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/sputc.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/sputc.pass.cpp @@ -0,0 +1,151 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// Tests the inherited function using a custom allocator. +// +// int_type basic_streambuf::sputc(char_type c); +// +// This test also validates the observable behaviour after move assignment and +// construction. This test uses a small so the underlying string is in short +// mode. + +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_allocator.h" + +template +void test() { + std::array< CharT, 17> input{ + CharT('a'), + CharT('1'), + CharT('+'), + CharT('A'), + CharT('g'), + CharT('0'), + CharT('@'), + CharT('Z'), + CharT('q'), + CharT('8'), + CharT('#'), + CharT('D'), + CharT('t'), + CharT('9'), + CharT('$'), + CharT('A'), + CharT(' ')}; + using SyncBuf = std::basic_syncbuf, test_allocator>; + + { // Normal + std::basic_string expected; + std::basic_stringbuf buf; + test_allocator_statistics stats; + test_allocator allocator{&stats}; + + { + SyncBuf sync_buf{&buf, allocator}; + for (int i = 0; i < 1024; ++i) { + CharT c = input[i % input.size()]; + expected.push_back(c); + typename SyncBuf::int_type ret = sync_buf.sputc(c); + assert(ret == typename SyncBuf::int_type(c)); + } + // The synchchronization happens upon destruction of sync_buf. + assert(buf.str().empty()); + assert(stats.allocated_size >= 1024); + } + assert(buf.str() == expected); + assert(stats.allocated_size == 0); + } + { // Move construction + std::basic_stringbuf buf; + test_allocator_statistics stats; + test_allocator allocator{&stats}; + + { + SyncBuf sync_buf{&buf, allocator}; + CharT c = CharT('4'); + typename SyncBuf::int_type ret = sync_buf.sputc(c); + assert(ret == typename SyncBuf::int_type(c)); + + { + c = CharT('2'); + + SyncBuf new_sync_buf{std::move(sync_buf)}; + ret = new_sync_buf.sputc(c); + assert(ret == typename SyncBuf::int_type(c)); + + // The synchchronization happens upon destruction of new_sync_buf. + assert(buf.str().empty()); + assert(stats.allocated_size >= 2); + } + assert(buf.str().size() == 2); + assert(buf.str()[0] == CharT('4')); + assert(buf.str()[1] == CharT('2')); + assert(stats.allocated_size == 0); + } + assert(buf.str().size() == 2); + assert(buf.str()[0] == CharT('4')); + assert(buf.str()[1] == CharT('2')); + assert(stats.allocated_size == 0); + } + { // Move assignment non-propagating allocator + std::basic_stringbuf buf; + test_allocator_statistics stats; + test_allocator allocator{&stats}; + static_assert(!std::allocator_traits>::propagate_on_container_move_assignment::value); + + { + SyncBuf sync_buf{&buf, allocator}; + CharT c = CharT('4'); + typename SyncBuf::int_type ret = sync_buf.sputc(c); + assert(ret == typename SyncBuf::int_type(c)); + + { + c = CharT('2'); + + SyncBuf new_sync_buf; + test_allocator a = new_sync_buf.get_allocator(); + new_sync_buf = std::move(sync_buf); + assert(new_sync_buf.get_allocator() == a); + + ret = new_sync_buf.sputc(c); + assert(ret == typename SyncBuf::int_type(c)); + + // The synchchronization happens upon destruction of new_sync_buf. + assert(buf.str().empty()); + } + assert(buf.str().size() == 2); + assert(buf.str()[0] == CharT('4')); + assert(buf.str()[1] == CharT('2')); + } + assert(buf.str().size() == 2); + assert(buf.str()[0] == CharT('4')); + assert(buf.str()[1] == CharT('2')); + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/sputn.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/sputn.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/sputn.pass.cpp @@ -0,0 +1,134 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// Tests the inherited function using a custom allocator. +// +// streamsize basic_streambuf::sputc(const char_type* s, streamsize n); +// +// This test also validates the observable behaviour after move assignment and +// construction. This test uses a large buffer so the underlying string is in +// long mode. + +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_allocator.h" + +template +void test() { + std::array< CharT, 17> input{ + CharT('a'), + CharT('1'), + CharT('+'), + CharT('A'), + CharT('g'), + CharT('0'), + CharT('@'), + CharT('Z'), + CharT('q'), + CharT('8'), + CharT('#'), + CharT('D'), + CharT('t'), + CharT('9'), + CharT('$'), + CharT('A'), + CharT(' ')}; + std::basic_string expected; + for (int i = 0; i < 1024; ++i) + expected.push_back(input[i % input.size()]); + + using SyncBuf = std::basic_syncbuf, test_allocator>; + { // Normal + std::basic_stringbuf buf; + test_allocator_statistics stats; + test_allocator allocator{&stats}; + + { + SyncBuf sync_buf{&buf, allocator}; + std::streamsize ret = sync_buf.sputn(expected.data(), expected.size()); + assert(ret == 1024); + + // The synchchronization happens upon destruction of sync_buf. + assert(buf.str().empty()); + assert(stats.allocated_size >= 1024); + } + assert(buf.str() == expected); + assert(stats.allocated_size == 0); + } + { // Move construction + std::basic_stringbuf buf; + test_allocator_statistics stats; + test_allocator allocator{&stats}; + + { + SyncBuf sync_buf{&buf, allocator}; + std::streamsize ret = sync_buf.sputn(expected.data(), expected.size()); + assert(ret == 1024); + { + SyncBuf new_sync_buf{std::move(sync_buf)}; + ret = new_sync_buf.sputn(expected.data(), expected.size()); + assert(ret == 1024); + + // The synchchronization happens upon destruction of new_sync_buf. + assert(buf.str().empty()); + assert(stats.allocated_size >= 2048); + } + assert(buf.str() == expected + expected); + assert(stats.allocated_size == 0); + } + assert(buf.str() == expected + expected); + assert(stats.allocated_size == 0); + } + { // Move assignment non-propagating allocator + std::basic_stringbuf buf; + test_allocator_statistics stats; + test_allocator allocator{&stats}; + static_assert(!std::allocator_traits>::propagate_on_container_move_assignment::value); + + { + SyncBuf sync_buf{&buf, allocator}; + std::streamsize ret = sync_buf.sputn(expected.data(), expected.size()); + assert(ret == 1024); + { + SyncBuf new_sync_buf; + test_allocator a = new_sync_buf.get_allocator(); + new_sync_buf = std::move(sync_buf); + assert(new_sync_buf.get_allocator() == a); + + ret = new_sync_buf.sputn(expected.data(), expected.size()); + assert(ret == 1024); + + // The synchchronization happens upon destruction of new_sync_buf. + assert(buf.str().empty()); + } + assert(buf.str() == expected + expected); + } + assert(buf.str() == expected + expected); + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/sync.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/sync.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/sync.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// protected: +// [syncstream.syncbuf.virtuals], overridden virtual functions +// int sync() override; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +void test_sync(bool emit_on_sync) { + std::basic_stringbuf base; + std::basic_syncbuf buff(&base); + std::basic_ostream out(&buff); + + buff.set_emit_on_sync(emit_on_sync); + + out << 'a'; + out.flush(); // This is an indirect call to sync. + + if (emit_on_sync) { + assert(base.str().size() == 1); + assert(base.str()[0] == CharT('a')); + } else + assert(base.str().empty()); +} + +int main(int, char**) { + test_sync(true); + test_sync(false); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_sync(true); + test_sync(false); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.assign/assign.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.assign/assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.assign/assign.pass.cpp @@ -0,0 +1,399 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf& operator=(basic_syncbuf&& rhs); + +#include +#include +#include +#include + +#include "test_macros.h" + +template +struct test_allocator : std::allocator { + using propagate_on_container_move_assignment = propagate; + + int id{-1}; + + test_allocator(int _id = -1) : id(_id) {} + test_allocator(test_allocator const& other) = default; + test_allocator(test_allocator&& other) = default; + test_allocator& operator=(const test_allocator& other) = default; + + test_allocator& operator=(test_allocator&& other) { + if constexpr (propagate_on_container_move_assignment::value) + id = other.id; + else + id = -1; + return *this; + } +}; + +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> { + using Base = std::basic_syncbuf, Alloc>; + +public: + test_syncbuf() = default; + + test_syncbuf(test_buf* buf, Alloc alloc) : Base(buf, alloc) {} + + test_syncbuf(typename Base::streambuf_type* buf, Alloc alloc) : Base(buf, alloc) {} + + void _setp(T* begin, T* end) { return this->setp(begin, end); } +}; + +// Helper wrapper to inspect the internal state of the basic_syncbuf +// +// This is used the valiate some standard requirements and libc++ +// implementation details. +template +class syncbuf_inspector : public std::basic_syncbuf { +public: + syncbuf_inspector() = default; + explicit syncbuf_inspector(std::basic_syncbuf&& base) + : std::basic_syncbuf(std::move(base)) {} + + void operator=(std::basic_syncbuf&& base) { *this = std::move(base); } + + using std::basic_syncbuf::pbase; + using std::basic_syncbuf::pptr; + using std::basic_syncbuf::epptr; +}; + +template +static void test_assign() { + test_buf base; + + { // Test using the real class, propagating allocator. + using BuffT = std::basic_syncbuf, test_allocator>; + + BuffT buff1(&base, test_allocator{42}); + buff1.sputc(CharT('A')); + + assert(buff1.get_wrapped() != nullptr); + + BuffT buff2; + assert(buff2.get_allocator().id == -1); + buff2 = std::move(buff1); + assert(buff1.get_wrapped() == nullptr); + assert(buff2.get_wrapped() == &base); + + assert(buff2.get_wrapped() == &base); + assert(buff2.get_allocator().id == 42); + } + + { // Test using the real class, non-propagating allocator. + using BuffT = std::basic_syncbuf, test_allocator>; + + BuffT buff1(&base, test_allocator{42}); + buff1.sputc(CharT('A')); + + assert(buff1.get_wrapped() != nullptr); + + BuffT buff2; + assert(buff2.get_allocator().id == -1); + buff2 = std::move(buff1); + assert(buff1.get_wrapped() == nullptr); + assert(buff2.get_wrapped() == &base); + + assert(buff2.get_wrapped() == &base); + assert(buff2.get_allocator().id == -1); + } + + { // Move assignment propagating allocator + // Test using the inspection wrapper. + // Not all these requirements are explicitly in the Standard, + // however the asserts are based on secondary requirements. The + // LIBCPP_ASSERTs are implementation specific. + + using BuffT = std::basic_syncbuf, std::allocator>; + + using Inspector = syncbuf_inspector, std::allocator>; + Inspector inspector1{BuffT(&base)}; + inspector1.sputc(CharT('A')); + + assert(inspector1.get_wrapped() != nullptr); + assert(inspector1.pbase() != nullptr); + assert(inspector1.pptr() != nullptr); + assert(inspector1.epptr() != nullptr); + assert(inspector1.pbase() != inspector1.pptr()); + assert(inspector1.pptr() - inspector1.pbase() == 1); + [[maybe_unused]] std::streamsize size = inspector1.epptr() - inspector1.pbase(); + + Inspector inspector2; + inspector2 = std::move(inspector1); + + assert(inspector1.get_wrapped() == nullptr); + LIBCPP_ASSERT(inspector1.pbase() == nullptr); + LIBCPP_ASSERT(inspector1.pptr() == nullptr); + LIBCPP_ASSERT(inspector1.epptr() == nullptr); + assert(inspector1.pbase() == inspector1.pptr()); + + assert(inspector2.get_wrapped() == &base); + LIBCPP_ASSERT(inspector2.pbase() != nullptr); + LIBCPP_ASSERT(inspector2.pptr() != nullptr); + LIBCPP_ASSERT(inspector2.epptr() != nullptr); + assert(inspector2.pptr() - inspector2.pbase() == 1); + LIBCPP_ASSERT(inspector2.epptr() - inspector2.pbase() == size); + } +} + +template +static void test_basic() { + { // Test properties + std::basic_syncbuf sync_buf1(nullptr); + std::basic_syncbuf sync_buf2(nullptr); + [[maybe_unused]] std::same_as&> decltype(auto) ret = + sync_buf1.operator=(std::move(sync_buf2)); + } + + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.sputn(expected.data(), expected.size()); + +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr1) == 1); + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr2) == 1); +#endif + + sync_buf2 = std::move(sync_buf1); + assert(sync_buf2.get_wrapped() == &sstr1); + + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr1) == 1); + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr2) == 0); +#endif + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); +} + +template +static void test_short_write_after_assign() { + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf2 = std::move(sync_buf1); + sync_buf2.sputc(CharT('Z')); + + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + } + + assert(sstr1.str().size() == 2); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr1.str()[1] == CharT('Z')); + assert(sstr2.str() == expected); +} + +template +static void test_long_write_after_assign() { + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf2 = std::move(sync_buf1); + sync_buf2.sputn(expected.data(), expected.size()); + + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + } + + assert(sstr1.str().size() == 1 + expected.size()); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr1.str().substr(1) == expected); + assert(sstr2.str() == expected); +} + +template +static void test_emit_on_assign() { + { // don't emit / don't emit + + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.set_emit_on_sync(false); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.set_emit_on_sync(false); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf2 = std::move(sync_buf1); + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + + sync_buf2.pubsync(); + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } + + { // don't emit / do emit + + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.set_emit_on_sync(true); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.set_emit_on_sync(false); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf2 = std::move(sync_buf1); + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + + sync_buf2.pubsync(); + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } + + { // do emit / don't emit + + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.set_emit_on_sync(false); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.set_emit_on_sync(true); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf2 = std::move(sync_buf1); + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + + sync_buf2.pubsync(); + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } + + { // do emit / do emit + + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.set_emit_on_sync(true); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.set_emit_on_sync(true); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf2 = std::move(sync_buf1); + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + + sync_buf2.pubsync(); + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } +} + +template +static void test() { + test_assign(); + test_basic(); + test_short_write_after_assign(); + test_long_write_after_assign(); + test_emit_on_assign(); +} + +int main(int, char**) { + test(); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.assign/swap.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.assign/swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.assign/swap.pass.cpp @@ -0,0 +1,274 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// void swap(basic_syncbuf& other) noexcept; + +#include +#include +#include + +#include "test_macros.h" + +#include + +template +static void test_basic() { + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.sputn(expected.data(), expected.size()); + +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr1) == 1); + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr2) == 1); +#endif + + sync_buf1.swap(sync_buf2); + assert(sync_buf1.get_wrapped() == &sstr2); + assert(sync_buf2.get_wrapped() == &sstr1); + + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr1) == 1); + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr2) == 1); +#endif + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); +} + +template +static void test_short_write_after_swap() { + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf1.swap(sync_buf2); + expected.push_back(sync_buf1.sputc(CharT('B'))); + sync_buf2.sputc(CharT('Z')); + + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + } + + assert(sstr1.str().size() == 2); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr1.str()[1] == CharT('Z')); + assert(sstr2.str() == expected); +} + +template +static void test_long_write_after_swap() { + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf1.swap(sync_buf2); + sync_buf1.sputn(expected.data(), expected.size()); + sync_buf2.sputn(expected.data(), expected.size()); + + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + } + + assert(sstr1.str().size() == 1 + expected.size()); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr1.str().substr(1) == expected); + assert(sstr2.str() == expected + expected); +} + +template +static void test_emit_on_sync() { + { // false false + + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.set_emit_on_sync(false); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.set_emit_on_sync(false); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf1.swap(sync_buf2); + + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + + sync_buf1.pubsync(); + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + + sync_buf2.pubsync(); + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } + + { // false true + + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.set_emit_on_sync(true); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.set_emit_on_sync(false); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf1.swap(sync_buf2); + + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + + sync_buf1.pubsync(); + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + + sync_buf2.pubsync(); + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str().empty()); + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } + + { // true false + + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.set_emit_on_sync(false); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.set_emit_on_sync(true); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf1.swap(sync_buf2); + + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + + sync_buf1.pubsync(); + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + + sync_buf2.pubsync(); + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } + + { // true true + + std::basic_stringbuf sstr1; + std::basic_stringbuf sstr2; + std::basic_string expected(42, CharT('*')); // a long string + + { + std::basic_syncbuf sync_buf1(&sstr1); + sync_buf1.set_emit_on_sync(true); + sync_buf1.sputc(CharT('A')); // a short string + + std::basic_syncbuf sync_buf2(&sstr2); + sync_buf2.set_emit_on_sync(true); + sync_buf2.sputn(expected.data(), expected.size()); + + sync_buf1.swap(sync_buf2); + + assert(sstr1.str().empty()); + assert(sstr2.str().empty()); + + sync_buf1.pubsync(); + assert(sstr1.str().empty()); + assert(sstr2.str() == expected); + + sync_buf2.pubsync(); + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } + + assert(sstr1.str().size() == 1); + assert(sstr1.str()[0] == CharT('A')); + assert(sstr2.str() == expected); + } +} + +template +static void test() { + test_basic(); + test_emit_on_sync(); + test_short_write_after_swap(); + test_long_write_after_swap(); +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.default.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.default.pass.cpp @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(); + +#include +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +template +void test() { + { + using Buf = std::basic_syncbuf; + static_assert(std::default_initializable); + Buf buf; + assert(buf.get_wrapped() == nullptr); + assert(buf.get_allocator() == std::allocator()); + } + { + using Buf = std::basic_syncbuf>; + static_assert(std::default_initializable); + Buf buf; + assert(buf.get_wrapped() == nullptr); + assert(buf.get_allocator() == std::allocator()); + } + { + using Buf = std::basic_syncbuf, test_allocator>; + static_assert(std::default_initializable); + Buf buf; + assert(buf.get_wrapped() == nullptr); + assert(buf.get_allocator() == test_allocator()); + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.move.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.move.pass.cpp @@ -0,0 +1,136 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +template +void test() { + { + using Buf = std::basic_syncbuf; + const std::allocator alloc; + { + Buf b1{nullptr, alloc}; + Buf b2{std::move(b1)}; + + assert(b2.get_wrapped() == nullptr); + assert(b2.get_allocator() == alloc); + } + { + Buf w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + Buf b1{&w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + Buf b2{std::move(b1)}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(b2.get_wrapped() == &w); + assert(b2.get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using Buf = std::basic_syncbuf>; + const std::allocator alloc; + { + Buf b1{nullptr, alloc}; + Buf b2{std::move(b1)}; + + assert(b2.get_wrapped() == nullptr); + assert(b2.get_allocator() == alloc); + } + { + Buf w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + Buf b1{&w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + Buf b2{std::move(b1)}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(b2.get_wrapped() == &w); + assert(b2.get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using Buf = std::basic_syncbuf, test_allocator>; + test_allocator alloc{42}; + { + Buf b1{nullptr, alloc}; + Buf b2{std::move(b1)}; + + assert(b2.get_wrapped() == nullptr); + assert(b2.get_allocator() == alloc); + } + { + Buf w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + Buf b1{&w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + Buf b2{std::move(b1)}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(b2.get_wrapped() == &w); + assert(b2.get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.pointer.allocator.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.pointer.allocator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.pointer.allocator.pass.cpp @@ -0,0 +1,119 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator&); + +#include +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +template +void test() { + { + using Buf = std::basic_syncbuf; + const std::allocator alloc; + { + Buf buf = {nullptr, alloc}; + assert(buf.get_wrapped() == nullptr); + assert(buf.get_allocator() == alloc); + } + { + Buf w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + Buf buf = {&w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(buf.get_wrapped() == &w); + assert(buf.get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using Buf = std::basic_syncbuf>; + const std::allocator alloc; + { + Buf buf = {nullptr, alloc}; + assert(buf.get_wrapped() == nullptr); + assert(buf.get_allocator() == alloc); + } + { + Buf w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + Buf buf{&w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(buf.get_wrapped() == &w); + assert(buf.get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using Buf = std::basic_syncbuf, test_allocator>; + test_allocator alloc{42}; + { + Buf buf = {nullptr, alloc}; + assert(buf.get_wrapped() == nullptr); + assert(buf.get_allocator() == alloc); + } + { + Buf w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + Buf buf{&w, alloc}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(buf.get_wrapped() == &w); + assert(buf.get_allocator() == alloc); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.pointer.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.pointer.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/cons.pointer.pass.cpp @@ -0,0 +1,128 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// explicit basic_syncbuf(streambuf_type* obuf); + +#include +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +template +void test() { + { + using Buf = std::basic_syncbuf; + + static_assert(!std::convertible_to*, Buf>); + static_assert(std::constructible_from*>); + + { + Buf buf{nullptr}; + assert(buf.get_wrapped() == nullptr); + assert(buf.get_allocator() == std::allocator()); + } + { + Buf w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + Buf buf{&w}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(buf.get_wrapped() == &w); + assert(buf.get_allocator() == std::allocator()); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using Buf = std::basic_syncbuf>; + + static_assert(!std::convertible_to>*, Buf>); + static_assert(std::constructible_from>*>); + + { + Buf buf{nullptr}; + assert(buf.get_wrapped() == nullptr); + assert(buf.get_allocator() == std::allocator()); + } + { + Buf w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + Buf buf{&w}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(buf.get_wrapped() == &w); + assert(buf.get_allocator() == std::allocator()); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } + + { + using Buf = std::basic_syncbuf, test_allocator>; + + static_assert(!std::convertible_to>*, Buf>); + static_assert(std::constructible_from>*>); + + { + Buf buf{nullptr}; + assert(buf.get_wrapped() == nullptr); + assert(buf.get_allocator() == test_allocator()); + } + { + Buf w; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + { + Buf buf{&w}; +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 1); +#endif + assert(buf.get_wrapped() == &w); + assert(buf.get_allocator() == test_allocator()); + } +#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS) + assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&w) == 0); +#endif + } + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/dtor.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/dtor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/dtor.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// ~basic_syncbuf(); + +#include +#include + +#include "../helpers.h" +#include "test_macros.h" + +template +void test() { + // We do this because we want to be able to use CharT + CharT arr[3] = {'a', 'b', 'c'}; + CharT* ptr = arr; + + test_buf base; + const std::allocator alloc; + { + test_syncbuf buf(&base, alloc); + + buf._setp(ptr, ptr + 3); + assert(base._pptr() == nullptr); + // The destructor calls buf.emit(); + } + CharT* pptr = base._pptr(); + while (pptr) { + assert(*pptr++ == *ptr++); + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/emit.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/emit.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/emit.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// bool emit(); + +#include +#include + +#include "../helpers.h" +#include "test_macros.h" + +template +void test() { + // We do this because we want to be able to use CharT + CharT arr[3] = {'a', 'b', 'c'}; + CharT* ptr = arr; + + test_buf base; + const 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++); + } +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/get_allocator.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/get_allocator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/get_allocator.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// allocator_type get_allocator() const noexcept; + +#include +#include + +#include "test_macros.h" +#include "../helpers.h" + +template +void test_get_allocator() { + test_buf base; + test_allocator alloc(42); + const test_syncbuf> buff(&base, alloc); + assert(buff.get_allocator().id == 42); + ASSERT_NOEXCEPT(buff.get_allocator()); +} + +int main(int, char**) { + test_get_allocator(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_get_allocator(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/get_wrapped.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/get_wrapped.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/get_wrapped.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// streambuf_type* get_wrapped() const noexcept; + +#include +#include +#include + +#include "../helpers.h" +#include "test_macros.h" + +template +void test() { + test_buf base(42); + const std::allocator alloc; + const test_syncbuf buff(&base, alloc); + std::same_as*> auto wrapped = buff.get_wrapped(); + assert(static_cast*>(wrapped)->id == 42); + ASSERT_NOEXCEPT(buff.get_wrapped()); +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/set_emit_on_sync.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/set_emit_on_sync.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/set_emit_on_sync.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// class basic_syncbuf; + +// void set_emit_on_sync(bool) noexcept; + +#include +#include + +#include "test_macros.h" +#include "../helpers.h" + +template +void test_set_emit_on_sync() { + // set_emit_on_sync tested in sync, which is called by pubsync. The assign + // and swap test use this. + test_syncbuf> buff(nullptr, std::allocator()); + ASSERT_NOEXCEPT(buff.set_emit_on_sync(false)); + buff.set_emit_on_sync(false); // Validates the function can be called. +} + +int main(int, char**) { + test_set_emit_on_sync(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_set_emit_on_sync(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.special/swap.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.special/swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/syncstream.syncbuf.special/swap.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template +// void swap(basic_syncbuf&, +// basic_syncbuf&); + +#include +#include + +#include "test_macros.h" + +template +void test() { + std::basic_syncbuf base1; + std::basic_syncbuf base2; + std::basic_syncbuf buff1(&base1); + std::basic_syncbuf buff2(&base2); + std::swap(buff1, buff2); + + assert(buff1.get_wrapped() == &base2); + assert(buff2.get_wrapped() == &base1); +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/types.compile.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/types.compile.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-localization +// UNSUPPORTED: libcpp-has-no-experimental-syncstream + +// + +// template, class Allocator = allocator> +// class basic_syncbuf { +// +// 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; +// +// ... + +#include +#include + +#include "test_macros.h" +#include "constexpr_char_traits.h" +#include "test_allocator.h" + +static_assert( + std::same_as, std::basic_syncbuf, std::allocator>>); +static_assert(std::same_as>, + std::basic_syncbuf, std::allocator>>); + +static_assert( + std::same_as, test_allocator>::char_type, char>); +static_assert(std::same_as, test_allocator>::int_type, + constexpr_char_traits::int_type>); +static_assert(std::same_as, test_allocator>::pos_type, + constexpr_char_traits::pos_type>); +static_assert(std::same_as, test_allocator>::off_type, + constexpr_char_traits::off_type>); +static_assert(std::same_as, test_allocator>::traits_type, + constexpr_char_traits>); +static_assert(std::same_as, test_allocator>::allocator_type, + test_allocator>); +static_assert(std::same_as, test_allocator>::streambuf_type, + std::basic_streambuf>>); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +static_assert(std::same_as, + std::basic_syncbuf, std::allocator>>); +static_assert(std::same_as>, + std::basic_syncbuf, std::allocator>>); + +static_assert( + std::same_as, test_allocator>::char_type, + wchar_t>); +static_assert( + std::same_as, test_allocator>::int_type, + constexpr_char_traits::int_type>); +static_assert( + std::same_as, test_allocator>::pos_type, + constexpr_char_traits::pos_type>); +static_assert( + std::same_as, test_allocator>::off_type, + constexpr_char_traits::off_type>); +static_assert( + std::same_as, test_allocator>::traits_type, + constexpr_char_traits>); +static_assert( + std::same_as, test_allocator>::allocator_type, + test_allocator>); +static_assert( + std::same_as, test_allocator>::streambuf_type, + std::basic_streambuf>>); +#endif // TEST_HAS_NO_WIDE_CHARACTERS diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/syncstream.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/syncstream.version.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/syncstream.version.compile.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// WARNING: This test was generated by generate_feature_test_macro_components.py +// and should not be edited manually. +// +// clang-format off + +// UNSUPPORTED: no-localization + +// + +// Test the feature test macros defined by + +/* Constant Value + __cpp_lib_syncbuf 201803L [C++20] +*/ + +#include +#include "test_macros.h" + +#if TEST_STD_VER < 14 + +# ifdef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 14 + +# ifdef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 17 + +# ifdef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 20 + +# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) +# ifndef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should be defined in c++20" +# endif +# if __cpp_lib_syncbuf != 201803L +# error "__cpp_lib_syncbuf should have the value 201803L in c++20" +# endif +# else +# ifdef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)' is not met!" +# endif +# endif + +#elif TEST_STD_VER == 23 + +# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) +# ifndef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should be defined in c++23" +# endif +# if __cpp_lib_syncbuf != 201803L +# error "__cpp_lib_syncbuf should have the value 201803L in c++23" +# endif +# else +# ifdef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)' is not met!" +# endif +# endif + +#elif TEST_STD_VER > 23 + +# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) +# ifndef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should be defined in c++26" +# endif +# if __cpp_lib_syncbuf != 201803L +# error "__cpp_lib_syncbuf should have the value 201803L in c++26" +# endif +# else +# ifdef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)' is not met!" +# endif +# endif + +#endif // TEST_STD_VER > 23 + diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -3822,16 +3822,16 @@ # error "__cpp_lib_submdspan should not be defined before c++26" # endif -# if !defined(_LIBCPP_VERSION) +# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) # ifndef __cpp_lib_syncbuf # error "__cpp_lib_syncbuf should be defined in c++20" # endif # if __cpp_lib_syncbuf != 201803L # error "__cpp_lib_syncbuf should have the value 201803L in c++20" # endif -# else // _LIBCPP_VERSION +# else # ifdef __cpp_lib_syncbuf -# error "__cpp_lib_syncbuf should not be defined because it is unimplemented in libc++!" +# error "__cpp_lib_syncbuf should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)' is not met!" # endif # endif @@ -5294,16 +5294,16 @@ # error "__cpp_lib_submdspan should not be defined before c++26" # endif -# if !defined(_LIBCPP_VERSION) +# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) # ifndef __cpp_lib_syncbuf # error "__cpp_lib_syncbuf should be defined in c++23" # endif # if __cpp_lib_syncbuf != 201803L # error "__cpp_lib_syncbuf should have the value 201803L in c++23" # endif -# else // _LIBCPP_VERSION +# else # ifdef __cpp_lib_syncbuf -# error "__cpp_lib_syncbuf should not be defined because it is unimplemented in libc++!" +# error "__cpp_lib_syncbuf should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)' is not met!" # endif # endif @@ -6868,16 +6868,16 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) +# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM) # ifndef __cpp_lib_syncbuf # error "__cpp_lib_syncbuf should be defined in c++26" # endif # if __cpp_lib_syncbuf != 201803L # error "__cpp_lib_syncbuf should have the value 201803L in c++26" # endif -# else // _LIBCPP_VERSION +# else # ifdef __cpp_lib_syncbuf -# error "__cpp_lib_syncbuf should not be defined because it is unimplemented in libc++!" +# error "__cpp_lib_syncbuf should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)' is not met!" # endif # endif diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h --- a/libcxx/test/support/test_allocator.h +++ b/libcxx/test/support/test_allocator.h @@ -29,8 +29,9 @@ struct test_allocator_statistics { int time_to_throw = 0; int throw_after = INT_MAX; - int count = 0; - int alloc_count = 0; + int count = 0; // the number of active instances + int alloc_count = 0; // the number of allocations not deallocating + int allocated_size = 0; // the size of allocated elements int construct_count = 0; // the number of times that ::construct was called int destroy_count = 0; // the number of times that ::destroy was called int copied = 0; @@ -42,6 +43,7 @@ count = 0; time_to_throw = 0; alloc_count = 0; + allocated_size = 0; construct_count = 0; destroy_count = 0; throw_after = INT_MAX; @@ -155,14 +157,17 @@ TEST_THROW(std::bad_alloc()); ++stats_->time_to_throw; ++stats_->alloc_count; + stats_->allocated_size += n; } return std::allocator().allocate(n); } TEST_CONSTEXPR_CXX14 void deallocate(pointer p, size_type s) { assert(data_ != test_alloc_base::destructed_value); - if (stats_ != nullptr) + if (stats_ != nullptr) { --stats_->alloc_count; + stats_->allocated_size -= s; + } std::allocator().deallocate(p, s); } @@ -275,6 +280,7 @@ } TEST_CONSTEXPR_CXX14 friend bool operator!=(const other_allocator& x, const other_allocator& y) { return !(x == y); } + TEST_CONSTEXPR int get_data() const { return data_; } typedef std::true_type propagate_on_container_copy_assignment; typedef std::true_type propagate_on_container_move_assignment; diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1023,7 +1023,8 @@ "name": "__cpp_lib_syncbuf", "values": {"c++20": 201803}, "headers": ["syncstream"], - "unimplemented": True, + "test_suite_guard": "!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)", + "libcxx_guard": "!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)", }, { "name": "__cpp_lib_text_encoding", @@ -1180,6 +1181,7 @@ "semaphore": ["UNSUPPORTED: no-threads"], "shared_mutex": ["UNSUPPORTED: no-threads"], "sstream": ["UNSUPPORTED: no-localization"], + "syncstream": ["UNSUPPORTED: no-localization"], "stdatomic.h": ["UNSUPPORTED: no-threads"], "stop_token": ["UNSUPPORTED: no-threads"], "thread": ["UNSUPPORTED: no-threads"], diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py --- a/libcxx/utils/libcxx/header_information.py +++ b/libcxx/utils/libcxx/header_information.py @@ -30,6 +30,7 @@ "sstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)", "streambuf": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)", "strstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)", + "syncstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)", # headers with #error directives "barrier": "!defined(_LIBCPP_HAS_NO_THREADS)", @@ -92,6 +93,7 @@ "stop_token": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17", "streambuf": "// UNSUPPORTED: no-localization", "strstream": "// UNSUPPORTED: no-localization", + "syncstream": "// UNSUPPORTED: no-localization", "thread": "// UNSUPPORTED: no-threads, c++03", "wchar.h": "// UNSUPPORTED: no-wide-characters", "wctype.h": "// UNSUPPORTED: no-wide-characters", @@ -127,7 +129,7 @@ "stack": ["compare", "initializer_list"], "string_view": ["compare"], "string": ["compare", "initializer_list"], - # TODO "syncstream": ["ostream"], + "syncstream": ["ostream"], "system_error": ["compare"], "tgmath.h": ["cmath", "complex"], "thread": ["compare"], @@ -156,7 +158,6 @@ "spanstream", "stacktrace", "stdfloat", - "syncstream", "text_encoding", ] diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py --- a/libcxx/utils/libcxx/test/params.py +++ b/libcxx/utils/libcxx/test/params.py @@ -267,6 +267,7 @@ AddFeature("libcpp-has-no-incomplete-pstl"), AddFeature("libcpp-has-no-experimental-stop_token"), AddFeature("libcpp-has-no-incomplete-tzdb"), + AddFeature("libcpp-has-no-experimental-syncstream"), ], ), Parameter(