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/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/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -981,6 +981,7 @@ string.h string_view strstream + syncstream system_error tgmath.h thread 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 @@ -201,6 +201,7 @@ #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) # include #endif +#include #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,22 @@ typedef fpos u16streampos; typedef fpos u32streampos; +#if _LIBCPP_STD_VER >= 20 + +template , class _Allocator = allocator<_CharT>> +class basic_syncbuf; + +using syncbuf = basic_syncbuf; +using wsyncbuf = basic_syncbuf; + +template , class _Allocator = allocator<_CharT>> +class basic_osyncstream; + +using osyncstream = basic_osyncstream; +using wosyncstream = basic_osyncstream; + +#endif // _LIBCPP_STD_VER >=20 + // Include other forward declarations here template > class _LIBCPP_TEMPLATE_VIS vector; diff --git a/libcxx/include/syncstream b/libcxx/include/syncstream new file mode 100644 --- /dev/null +++ b/libcxx/include/syncstream @@ -0,0 +1,406 @@ +// -*- C++ -*- +//===--------------------------- sstream ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_SYNCSTREAM +#define _LIBCPP_SYNCSTREAM + +/* + syncstream synopsis + +#include // see [ostream.syn] + +namespace std { + template + class basic_syncbuf; + + using syncbuf = basic_syncbuf; + using wsyncbuf = basic_syncbuf; + + template + class basic_osyncstream; + + using osyncstream = basic_osyncstream; + using wosyncstream = basic_osyncstream; + + template + class basic_syncbuf : public basic_streambuf { + public: + using char_type = charT; + using int_type = typename traits::int_type; + using pos_type = typename traits::pos_type; + using off_type = typename traits::off_type; + using traits_type = traits; + using allocator_type = Allocator; + + using streambuf_type = basic_streambuf; + + // [syncstream.syncbuf.cons], construction and destruction + explicit basic_syncbuf(streambuf_type* obuf = nullptr) + : basic_syncbuf(obuf, Allocator()) {} + basic_syncbuf(streambuf_type*, const Allocator&); + basic_syncbuf(basic_syncbuf&&); + ~basic_syncbuf(); + + // [syncstream.syncbuf.assign], assignment and swap + basic_syncbuf& operator=(basic_syncbuf&&); + void swap(basic_syncbuf&); + + // [syncstream.syncbuf.members], member functions + bool emit(); + streambuf_type* get_wrapped() const noexcept; + allocator_type get_allocator() const noexcept; + void set_emit_on_sync(bool) noexcept; + + protected: + // [syncstream.syncbuf.virtuals], overridden virtual functions + int sync() override; + + private: + streambuf_type* wrapped; // exposition only + bool emit_on_sync{}; // exposition only + }; + + // [syncstream.syncbuf.special], specialized algorithms + template + void swap(basic_syncbuf&, + basic_syncbuf&); + + template + class basic_osyncstream : public basic_ostream { + public: + using char_type = charT; + using int_type = typename traits::int_type; + using pos_type = typename traits::pos_type; + using off_type = typename traits::off_type; + using traits_type = traits; + + using allocator_type = Allocator; + using streambuf_type = basic_streambuf; + using syncbuf_type = basic_syncbuf; + + // [syncstream.osyncstream.cons], construction and destruction + basic_osyncstream(streambuf_type*, const Allocator&); + explicit basic_osyncstream(streambuf_type* obuf) + : basic_osyncstream(obuf, Allocator()) {} + basic_osyncstream(basic_ostream& os, const Allocator& allocator) + : basic_osyncstream(os.rdbuf(), allocator) {} + explicit basic_osyncstream(basic_ostream& os) + : basic_osyncstream(os, Allocator()) {} + basic_osyncstream(basic_osyncstream&&) noexcept; + ~basic_osyncstream(); + + // [syncstream.osyncstream.assign], assignment + basic_osyncstream& operator=(basic_osyncstream&&) noexcept; + + // [syncstream.osyncstream.members], member functions + void emit(); + streambuf_type* get_wrapped() const noexcept; + syncbuf_type* rdbuf() const noexcept { return const_cast(addressof(sb)); } + + private: + syncbuf_type sb; // exposition only + }; +} + +*/ + +#include <__config> +#include +#include +#include +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +// basic_syncbuf + +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>; + using mutex_ptr_type = shared_ptr; + using string_type = basic_string<_CharT, _Traits, _Allocator>; + + // [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), __alloc_(__alloc) { + __make_mtx_for_ptr(__obuf); + } + + _LIBCPP_HIDE_FROM_ABI basic_syncbuf(basic_syncbuf&& __other) + : __wrapped_(std::move(__other.get_wrapped())), __alloc_(std::move(__other.get_allocator())) { + __other.__wrapped_ = nullptr; + } + + _LIBCPP_HIDE_FROM_ABI ~basic_syncbuf() { +# ifndef _LIBCPP_NO_EXCEPTIONS + try { +# endif // _LIBCPP_NO_EXCEPTIONS + emit(); +# ifndef _LIBCPP_NO_EXCEPTIONS + } catch (...) { + } +# endif // _LIBCPP_NO_EXCEPTIONS + + __destroy_mtx_for_ptr(__wrapped_); + } + + // [syncstream.syncbuf.assign], assignment and swap + + _LIBCPP_HIDE_FROM_ABI basic_syncbuf& operator=(basic_syncbuf&& __other) { + __wrapped_ = std::move(__other.get_wrapped()); + __alloc_ = std::move(__other.get_allocator()); + __other.__wrapped_ = nullptr; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI void swap(basic_syncbuf&& __other) { + 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() { + bool __result = true; + auto* __mtx = __get_mtx_for_ptr(__wrapped_); + + if (__wrapped_ == nullptr || __mtx == nullptr) + __result = false; + + if (__result) { + lock_guard __guard(*__mtx); + + if (this->pptr()) { + __result &= (__wrapped_->sputn(__str_.data(), __str_.size()) != -1); + this->setp(nullptr, nullptr); + } + + if (__should_flush_) + __result &= (__wrapped_->pubsync() != -1); + } + + __should_flush_ = false; + return __result; + } + + _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; } + + _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __alloc_; } + + _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; } + +protected: + // [syncstream.syncbuf.virtuals], overridden virtual functions + + int sync() override { + __should_flush_ = true; + + if (__emit_on_sync_ && !emit()) + return -1; + return 0; + } + + 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_NO_EXCEPTIONS + try { +# endif // _LIBCPP_NO_EXCEPTIONS + __str_.resize(__str_.capacity()); + + char_type* __p = static_cast(__str_.data()); + this->setp(__p, __p + __str_.size()); +# ifndef _LIBCPP_NO_EXCEPTIONS + } catch (...) { + return traits_type::eof(); + } +# endif // _LIBCPP_NO_EXCEPTIONS + } + + return this->sputc(traits_type::to_char_type(__c)); + } + +private: + streambuf_type* __wrapped_; + allocator_type __alloc_; + string_type __str_; + bool __emit_on_sync_; + bool __should_flush_; +}; + +struct __count_mutex_pair { + mutex* __pointer = nullptr; + size_t __count = 0; +}; + +// Each basic_streambuf needs to have exactly one mutex that it corresponds to. +// If there are 100 osyncstreams we still need each one to map to the correct mutex. +// So, below is a global map that maps streambuf pointers to mutex. This is used in emit. +// void* used as key so that only one instance of this function is created (no template). +// +// An unordered_map is used here because pointers are easy to hash, copy, and store, so +// it makes a lot of sense to prioritize constant lookup. Additionally, using a comparison +// operator, such as a std::map might do, is UB for unrelated pointers. +_LIBCPP_HIDE_FROM_ABI inline unordered_map& __get_mtx_pair_map() { + static unordered_map __mtx_ptr_map; + static_assert(alignof(void*) % 4 == 0, "This pointer alignment means that the resize constant must be updated."); + // The hash function for a pointer will simply return the pointer. We want the + // number of buckets to be relatively prime to the alignment of "void*" so that + // elements are evenly distributed across the unordered_map. + __mtx_ptr_map.reserve(64 - 1); + return __mtx_ptr_map; +} + +_LIBCPP_HIDE_FROM_ABI inline mutex& __get_map_mtx() { + static mutex __mtx; + return __mtx; +} + +_LIBCPP_HIDE_FROM_ABI inline mutex* __get_mtx_for_ptr(void* __ptr) { + if (!__ptr) + return nullptr; + + mutex& __mtx = __get_map_mtx(); + lock_guard __guard(__mtx); + + auto& __mtx_pair_map = __get_mtx_pair_map(); + auto& __mtx_pair = __mtx_pair_map[__ptr]; + _LIBCPP_ASSERT(__mtx_pair.__pointer != nullptr, "This should never be nullptr"); + return __mtx_pair.__pointer; +} + +_LIBCPP_HIDE_FROM_ABI inline void __make_mtx_for_ptr(void* __ptr) { + mutex& __mtx = __get_map_mtx(); + lock_guard __guard(__mtx); + + auto& __mtx_pair_map = __get_mtx_pair_map(); + if (!__mtx_pair_map.contains(__ptr)) + __mtx_pair_map[__ptr] = __count_mutex_pair{new mutex, 0}; + __mtx_pair_map[__ptr].__count += 1; +} + +_LIBCPP_HIDE_FROM_ABI inline void __destroy_mtx_for_ptr(void* __ptr) { + if (!__ptr) + return; + + mutex& __mtx = __get_map_mtx(); + lock_guard __guard(__mtx); + + auto& __mtx_pair_map = __get_mtx_pair_map(); + _LIBCPP_ASSERT(__mtx_pair_map.contains(__ptr), "Expected to have already created mutex."); + auto& __mtx_pair = __mtx_pair_map[__ptr]; + if (--__mtx_pair.__count == 0) { + delete __mtx_pair.__pointer; + __mtx_pair_map.erase(__ptr); + } +} + +// 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>(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>(addressof(__sb_)), __sb_(std::move(__other.__sb_)) { + this->set_rdbuf(addressof(__sb_)); + } + + _LIBCPP_HIDE_FROM_ABI ~basic_osyncstream() { +# ifndef _LIBCPP_NO_EXCEPTIONS + try { +# endif // _LIBCPP_NO_EXCEPTIONS + emit(); +# ifndef _LIBCPP_NO_EXCEPTIONS + } catch (...) { + } +# endif // _LIBCPP_NO_EXCEPTIONS + } + + // [syncstream.osyncstream.assign], assignment + + _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) noexcept { +# ifndef _LIBCPP_NO_EXCEPTIONS + try { +# endif // _LIBCPP_NO_EXCEPTIONS + emit(); +# ifndef _LIBCPP_NO_EXCEPTIONS + } catch (...) { + } +# endif // _LIBCPP_NO_EXCEPTIONS + + __sb_ = std::move(__other.__sb_); + return *this; + } + + // [syncstream.osyncstream.members], member functions + + _LIBCPP_HIDE_FROM_ABI void emit() { + if (__sb_.emit() == false) + this->setstate(ios::badbit); + } + + _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(addressof(__sb_)); } + +private: + syncbuf_type __sb_; +}; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_SYNCSTREAM diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -401,7 +401,7 @@ # define __cpp_lib_starts_ends_with 201711L # undef __cpp_lib_string_view # define __cpp_lib_string_view 201803L -// # define __cpp_lib_syncbuf 201803L +# define __cpp_lib_syncbuf 201803L // # 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,7 @@ #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) # include #endif +#include #include #if !defined(_LIBCPP_HAS_NO_THREADS) # include @@ -189,9 +190,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/syncstream.inc b/libcxx/modules/std/syncstream.inc --- a/libcxx/modules/std/syncstream.inc +++ b/libcxx/modules/std/syncstream.inc @@ -8,21 +8,19 @@ //===----------------------------------------------------------------------===// export namespace std { -#if 0 using std::basic_syncbuf; // [syncstream.syncbuf.special], specialized algorithms using std::swap; using std::syncbuf; -# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS using std::wsyncbuf; -# endif +#endif using std::basic_osyncstream; using std::osyncstream; -# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS using std::wosyncstream; -# endif #endif } // namespace std 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,67 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// class basic_osyncstream; + +// basic_osyncstream& operator=(basic_osyncstream&& rhs) noexcept; + +#include +#include + +#include "test_macros.h" + +template +struct test_allocator : std::allocator { + int id; + int moves = 0; + int copies = 0; + + test_allocator(int id = 0) : id(id) {} + test_allocator(test_allocator const& other) : id(other.id), moves(other.moves), copies(other.copies) {} + test_allocator(test_allocator&& other) + : id(std::move(other.id)), moves(std::move(other.moves)), copies(std::move(other.copies)) {} +}; + +template +class test_buf : public std::basic_streambuf {}; + +template +void test_assign() { + using OS = std::basic_osyncstream, test_allocator>; + + test_buf base; + test_allocator alloc(42); + OS out1(&base, alloc); + typename OS::syncbuf_type* buff1 = out1.rdbuf(); + + assert(out1.get_wrapped() == &base); + assert(buff1->get_wrapped() == &base); + + OS out2 = std::move(out1); + typename OS::syncbuf_type* buff2 = out2.rdbuf(); + + assert(out2.get_wrapped() == &base); + assert(buff2->get_wrapped() == &base); + assert(buff2->get_allocator().id == alloc.id); + assert(out1.get_wrapped() == nullptr); + assert(buff1->get_wrapped() == nullptr); +} + +int main(int, char**) { + test_assign(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_assign(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/const.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/const.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/const.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// class basic_osyncstream; + +// basic_osyncstream(streambuf_type* buf, const Allocator& allocator); +// basic_osyncstream(basic_osyncstream&& other) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +struct test_allocator : std::allocator { + int id; + int moves = 0; + int copies = 0; + + test_allocator(int id = 0) : id(id) {} + test_allocator(test_allocator const& other) : id(other.id), moves(other.moves), copies(other.copies) {} + test_allocator(test_allocator&& other) + : id(std::move(other.id)), moves(std::move(other.moves)), copies(std::move(other.copies)) {} +}; + +template +class test_buf : public std::basic_streambuf {}; + +template +void test_const() { + using OS = std::basic_osyncstream, test_allocator>; + + test_buf base; + test_allocator alloc(42); + OS out(&base, alloc); + typename OS::syncbuf_type* buff = out.rdbuf(); + + assert(out.get_wrapped() == &base); + assert(buff->get_wrapped() == &base); + assert(buff->get_allocator().id == alloc.id); +}; + +template +void test_rvalue() { + using OS = std::basic_osyncstream, test_allocator>; + + test_buf base; + test_allocator alloc(42); + OS out1(&base, alloc); + typename OS::syncbuf_type* buff1 = out1.rdbuf(); + + assert(out1.get_wrapped() == &base); + assert(buff1->get_wrapped() == &base); + + OS out2(std::move(out1)); + typename OS::syncbuf_type* buff2 = out2.rdbuf(); + + assert(out2.get_wrapped() == &base); + assert(buff2->get_wrapped() == &base); + assert(buff2->get_allocator().id == alloc.id); + assert(out1.get_wrapped() == nullptr); + assert(buff1->get_wrapped() == nullptr); +} + +template +void test_dest() { + using OS = std::basic_osyncstream, std::allocator>; + using SS = std::basic_ostringstream; + CharT c = 'f'; + + SS ss; + { + OS out(ss); + out << c; + assert(ss.str().empty()); + } + assert(ss.str()[0] == c); +} + +int main(int, char**) { + test_const(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_const(); +#endif + + test_rvalue(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_rvalue(); +#endif + + test_dest(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_dest(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/members.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/members.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/members.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// class basic_osyncstream; + +// void emit(); +// streambuf_type* get_wrapped() const noexcept; + +#include +#include +#include + +#include "test_macros.h" + +template +class test_buf : public std::basic_streambuf {}; + +template +void test_emit() { + using OS = std::basic_osyncstream, std::allocator>; + using SS = std::basic_ostringstream; + CharT c = 'f'; + + SS ss; + OS out(ss); + out << c; + assert(ss.str().empty()); + out.emit(); + assert(ss.str()[0] == c); +} + +template +void test_get_wrapped() { + using OS = std::basic_osyncstream, std::allocator>; + + test_buf base; + std::allocator alloc; + OS out(&base, alloc); + assert(out.get_wrapped() == &base); + ASSERT_NOEXCEPT(out.get_wrapped()); +} + +template +void test_rdbuf() { + using OS = std::basic_osyncstream, std::allocator>; + + test_buf base; + std::allocator alloc; + OS out(&base, alloc); + assert(out.rdbuf() != nullptr); + ASSERT_SAME_TYPE(decltype(out.rdbuf()), std::basic_syncbuf, std::allocator>*); + ASSERT_NOEXCEPT(out.rdbuf()); +} + +int main(int, char**) { + test_emit(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_emit(); +#endif + + test_get_wrapped(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_get_wrapped(); +#endif + + test_rdbuf(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_rdbuf(); +#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,64 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// class basic_osyncstream; + +// void emit(); +// streambuf_type* get_wrapped() const noexcept; + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +using ms = std::chrono::milliseconds; +using OS = std::basic_osyncstream, std::allocator>; + +static std::basic_ostringstream ss; +static const char a = 'a'; +static const char b = 'b'; +static const char c = 'c'; +static const char d = 'd'; + +void f1() { + OS out(ss); + out << a; + std::this_thread::sleep_for(ms(250)); + out << b; +} + +void f2() { + OS out(ss); + out << c; + out << d; +} + +void test_mutlithread() { + std::thread t1(f1); + std::thread t2(f2); + t1.join(); + t2.join(); + + assert(strcmp(ss.str().data(), "cdab")); +} + +int main(int, char**) { + // tested with char only for now + test_mutlithread(); + + return 0; +} 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,169 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// class basic_osyncstream; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +using OS = std::basic_osyncstream, std::allocator>; + +static std::basic_ostringstream ss; +static std::unordered_multiset test_strings = { + "C++", + "is", + "a", + "general-purpose", + "programming", + "language", + "created", + "by", + "Bjarne", + "Stroustrup", + "as", + "an", + "extension", + "of", + "the", + "C", + "programming", + "language,", + "or", + "C", + "with", + "Classes", + "The", + "language", + "has", + "expanded", + "significantly", + "over", + "time,", + "and", + "modern", + "C++", + "has", + "object-oriented,", + "generic,", + "and", + "functional", + "features", + "in", + "addition", + "to", + "facilities", + "for", + "low-level", + "memory", + "manipulation.", + "It", + "is", + "almost", + "always", + "implemented", + "as", + "a", + "compiled", + "language,", + "and", + "many", + "vendors", + "provide", + "C++", + "compilers,", + "including", + "the", + "Free", + "Software", + "Foundation,", + "LLVM,", + "Microsoft,", + "Intel,", + "and", + "IBM,", + "so", + "it", + "is", + "available", + "on", + "many", + "platforms."}; + +void f(std::string text) { + OS out(ss); + out << text; +} + +void test() { + ss = std::basic_ostringstream(); + std::vector threads; + for (std::string const& word : test_strings) + threads.push_back(std::thread(f, word)); + for (size_t i = 0; i < test_strings.size(); (void)++i) + threads[i].join(); + + std::string output = ss.str(); + for (std::string const& word : test_strings) + assert(output.find(word) != std::string::npos); +} + +static std::basic_ostringstream ss2; + +void simple_fn() { + OS out(ss2); + out << "X"; +} + +unsigned measure_average_operation(unsigned thread_count) { + std::vector threads; + threads.reserve(thread_count); + auto start = std::chrono::high_resolution_clock::now(); + for (unsigned i = 0; i < thread_count; ++i) + threads.push_back(std::thread(simple_fn)); + for (unsigned i = 0; i < thread_count; ++i) + threads[i].join(); + auto end = std::chrono::high_resolution_clock::now(); + auto total_duration = std::chrono::duration_cast(end - start).count(); + return total_duration / thread_count; +} + +// TODO: this isn't a great test because the overhead of everything else if far more than any +// performance issues syncstream might cause. +void test_constant_performance() { + // Make sure that doing 10x the work takes no longer than 10x the time. + unsigned ten_threads = measure_average_operation(10); + unsigned hundred_threads = measure_average_operation(100); + unsigned thousand_threads = measure_average_operation(1000); + assert(ten_threads * 10 > hundred_threads); + assert(hundred_threads * 10 > thousand_threads); +} + +int main(int, char**) { + // The more we test, the more likely we catch an error + for (size_t i = 0; i < 10000; ++i) + test(); + + // Test that the complexity is linear (adding x threads takes roughly x times longer). + test_constant_performance(); + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/types.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/types.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/osyncstream/types.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// + +#include +#include +#include + +#include "test_macros.h" + +template +void test_member_types() { + using Buf = std::basic_syncbuf; + using OSS = std::basic_osyncstream; + + ASSERT_SAME_TYPE(typename Buf::char_type, CharT); + ASSERT_SAME_TYPE(typename Buf::traits_type, std::char_traits); + ASSERT_SAME_TYPE(typename Buf::allocator_type, std::allocator); + + ASSERT_SAME_TYPE(typename OSS::char_type, CharT); + ASSERT_SAME_TYPE(typename OSS::traits_type, std::char_traits); + ASSERT_SAME_TYPE(typename OSS::allocator_type, std::allocator); +} + +int main(int, char**) { + ASSERT_SAME_TYPE(std::syncbuf, std::basic_syncbuf); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + ASSERT_SAME_TYPE(std::wsyncbuf, std::basic_syncbuf); +#endif + ASSERT_SAME_TYPE(std::osyncstream, std::basic_osyncstream); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + ASSERT_SAME_TYPE(std::wosyncstream, std::basic_osyncstream); +#endif + + test_member_types(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_member_types(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/assign.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/assign.pass.cpp @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf& operator=(basic_syncbuf&& rhs) noexcept; +// void swap(basic_syncbuf& other) noexcept; + +#include +#include + +#include "test_macros.h" + +template +class test_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); } +}; + +template +void test_emit_on_assign() { + CharT arr[3] = {'a', 'b', 'c'}; + CharT* ptr = arr; + + test_buf base; + std::allocator alloc; + test_syncbuf buff(&base, alloc); + + buff._setp(ptr, ptr + 3); + assert(base._pptr() == nullptr); + buff = test_syncbuf(); + assert(buff.get_wrapped() == nullptr); + CharT* pptr = base._pptr(); + while (pptr) { + assert(*pptr++ == *ptr++); + } +} + +template +void test_assign() { + using BuffT = std::basic_syncbuf, std::allocator>; + + test_syncbuf base; + std::allocator alloc; + BuffT buff1(&base, alloc); + BuffT buff2 = std::move(buff1); + + assert(buff1.get_wrapped() == nullptr); + assert(buff2.get_wrapped() == &base); + + test_emit_on_assign(); + + // Also LWG issue? + // assert(buff1.rdbuf()->pbase() == buff1.rdbuf()->pptr()); +} + +template +void test_swap() { + using BuffT = std::basic_syncbuf, std::allocator>; + + test_syncbuf base1; + test_syncbuf base2; + std::allocator alloc; + BuffT buff1(&base1, alloc); + BuffT buff2(&base2, alloc); + buff1.swap(std::move(buff2)); + + assert(buff1.get_wrapped() == &base2); + assert(buff2.get_wrapped() == &base1); +} + +int main(int, char**) { + test_assign(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_assign(); +#endif + + test_swap(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_swap(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/cons.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/cons.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/cons.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" + +template +struct test_allocator : std::allocator { + int id; + int moves = 0; + int copies = 0; + + test_allocator(int id = 0) : id(id) {} + test_allocator(test_allocator const& other) : id(other.id), moves(other.moves), copies(other.copies) {} + test_allocator(test_allocator&& other) + : id(std::move(other.id)), moves(std::move(other.moves)), copies(std::move(other.copies)) {} +}; + +template +class test_buf : public std::basic_streambuf {}; + +template +void test_streambuf_const() { + using BuffT = std::basic_syncbuf, test_allocator>; + + test_buf base; + test_allocator alloc(42); + BuffT buff(&base, alloc); + + assert(buff.get_wrapped() == &base); + assert(buff.get_allocator().id == alloc.id); +} + +template +void test_move_const() { + using BuffT = std::basic_syncbuf, test_allocator>; + + test_buf base; + test_allocator alloc(42); + BuffT buff1(&base, alloc); + BuffT buff2(std::move(buff1)); + + assert(buff1.get_wrapped() == nullptr); + assert(buff2.get_wrapped() == &base); + + // LWG issue? + // assert(buff1.rdbuf()->pbase() == buff1.rdbuf()->pptr()); +} + +int main(int, char**) { + test_streambuf_const(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_streambuf_const(); +#endif + + test_move_const(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_move_const(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/members.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/members.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/members.pass.cpp @@ -0,0 +1,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 + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" + +template +class test_buf : public std::basic_streambuf { +public: + int id; + + test_buf(int id = 0) : id(id) {} + + T* _pptr() { return this->pptr(); } +}; + +template > +class test_syncbuf : public std::basic_syncbuf, Alloc> { +public: + test_syncbuf(test_buf* buf, Alloc alloc) : std::basic_syncbuf, Alloc>(buf, alloc) {} + + void _setp(T* begin, T* end) { return this->setp(begin, end); } +}; + +template +struct test_allocator : std::allocator { + int id; + test_allocator(int id = 0) : id(id) {} +}; + +template +void test_emit() { + // We do this because we want to be able to any CharT + CharT arr[3] = {'a', 'b', 'c'}; + CharT* ptr = arr; + + test_buf base; + std::allocator alloc; + test_syncbuf buff(&base, alloc); + + buff._setp(ptr, ptr + 3); + assert(base._pptr() == nullptr); + buff.emit(); + CharT* pptr = base._pptr(); + while (pptr) { + assert(*pptr++ == *ptr++); + } +} + +template +void test_get_wrapped() { + test_buf base(42); + std::allocator alloc; + test_syncbuf buff(&base, alloc); + assert(static_cast*>(buff.get_wrapped())->id == 42); + ASSERT_NOEXCEPT(buff.get_wrapped()); +} + +template +void test_get_allocator() { + test_buf base; + test_allocator alloc(42); + test_syncbuf> buff(&base, alloc); + assert(buff.get_allocator().id == 42); + ASSERT_NOEXCEPT(buff.get_allocator()); +} + +template +void test_set_emit_on_sync() { + // set_emit_on_sync tested in sync + test_syncbuf> buff(nullptr, std::allocator()); + ASSERT_NOEXCEPT(buff.set_emit_on_sync(false)); +} + +int main(int, char**) { + test_emit(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_emit(); +#endif + + test_get_wrapped(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_get_wrapped(); +#endif + + test_get_allocator(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_get_allocator(); +#endif + + 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/special.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/special.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/special.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" + +template +class test_streambuf : public std::basic_streambuf {}; + +template +void test_swap() { + using BuffT = std::basic_syncbuf, std::allocator>; + + test_streambuf base1; + test_streambuf base2; + std::allocator alloc; + BuffT buff1(&base1, alloc); + BuffT buff2(&base2, alloc); + std::swap(buff1, buff2); + + assert(buff1.get_wrapped() == &base2); + assert(buff2.get_wrapped() == &base1); +} + +int main(int, char**) { + test_swap(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_swap(); +#endif + + return 0; +} diff --git a/libcxx/test/std/input.output/syncstream/syncbuf/virtuals.pass.cpp b/libcxx/test/std/input.output/syncstream/syncbuf/virtuals.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/syncstream/syncbuf/virtuals.pass.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include +#include +#include + +#include "test_macros.h" + +template +class test_buf : public std::basic_streambuf { +public: + T* _pbase() { return this->pbase(); } +}; + +template +class test_syncbuf : public std::basic_syncbuf, std::allocator> { +public: + test_syncbuf(test_buf* buf, std::allocator alloc) + : std::basic_syncbuf, std::allocator>(buf, alloc) {} + + int _sync() { return this->sync(); } +}; + +template +void test_sync(bool emit_on_sync) { + std::basic_ostringstream ss; + test_buf* base = reinterpret_cast*>(ss.rdbuf()); + std::allocator alloc; + test_syncbuf buff(base, alloc); + std::basic_ostream out(&buff); + + buff.set_emit_on_sync(emit_on_sync); + out << 'a'; + assert(base->_pbase() == nullptr); + assert(buff._sync() == 0); + + if (emit_on_sync) + assert(*base->_pbase() == 'a'); + else + assert(base->_pbase() == nullptr); +} + +int main(int, char**) { + test_sync(true); + test_sync(false); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_sync(true); + test_sync(false); +#endif + + return 0; +} diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/syncstream.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/syncstream.version.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/syncstream.version.pass.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// 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. + +// + +// 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 + +# 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 + +#elif TEST_STD_VER > 20 + +# ifndef __cpp_lib_syncbuf +# error "__cpp_lib_syncbuf should be defined in c++2b" +# endif +# if __cpp_lib_syncbuf != 201803L +# error "__cpp_lib_syncbuf should have the value 201803L in c++2b" +# endif + +#endif // TEST_STD_VER > 20 + +int main(int, char**) { return 0; } 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,17 +3822,11 @@ # error "__cpp_lib_submdspan should not be defined before c++26" # endif -# if !defined(_LIBCPP_VERSION) -# 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 -# ifdef __cpp_lib_syncbuf -# error "__cpp_lib_syncbuf should not be defined because it is unimplemented in libc++!" -# endif +# 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 # ifdef __cpp_lib_text_encoding @@ -5306,17 +5300,11 @@ # error "__cpp_lib_submdspan should not be defined before c++26" # endif -# if !defined(_LIBCPP_VERSION) -# 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 -# ifdef __cpp_lib_syncbuf -# error "__cpp_lib_syncbuf should not be defined because it is unimplemented in libc++!" -# endif +# 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 # ifdef __cpp_lib_text_encoding @@ -6892,17 +6880,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# 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 -# ifdef __cpp_lib_syncbuf -# error "__cpp_lib_syncbuf should not be defined because it is unimplemented in libc++!" -# endif +# 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 # if !defined(_LIBCPP_VERSION) 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 @@ -1026,7 +1026,6 @@ "name": "__cpp_lib_syncbuf", "values": {"c++20": 201803}, "headers": ["syncstream"], - "unimplemented": True, }, { "name": "__cpp_lib_text_encoding", 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 @@ -127,7 +127,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 +156,6 @@ "spanstream", "stacktrace", "stdfloat", - "syncstream", "text_encoding", ]