diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 6f2686e6de2..298f1439d03 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -186,6 +186,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/string ${CMAKE_CURRENT_LIST_DIR}/inc/string_view ${CMAKE_CURRENT_LIST_DIR}/inc/strstream + ${CMAKE_CURRENT_LIST_DIR}/inc/syncstream ${CMAKE_CURRENT_LIST_DIR}/inc/system_error ${CMAKE_CURRENT_LIST_DIR}/inc/thread ${CMAKE_CURRENT_LIST_DIR}/inc/tuple @@ -396,6 +397,7 @@ set(SOURCES_SATELLITE_2 set(SOURCES_SATELLITE_ATOMIC_WAIT ${CMAKE_CURRENT_LIST_DIR}/src/atomic_wait.cpp ${CMAKE_CURRENT_LIST_DIR}/src/parallel_algorithms.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/syncstream.cpp ) set(SOURCES_SATELLITE_CODECVT_IDS @@ -495,7 +497,7 @@ function(add_stl_dlls D_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIO file(WRITE "${_ATOMIC_WAIT_DEF_NAME}" "${_ATOMIC_WAIT_DEF_CONTENTS}") add_library(msvcp${D_SUFFIX}_atomic_wait SHARED "${_ATOMIC_WAIT_DEF_NAME}") - target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib") + target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects msvcp${D_SUFFIX}_implib_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_NAME "msvcp140_atomic_wait${D_SUFFIX}${VCLIBS_SUFFIX}") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES OUTPUT_NAME "${_ATOMIC_WAIT_OUTPUT_NAME}") diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index b238fdb7602..65b45f62770 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -120,6 +120,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index cb1ef9efeb5..66cb2996698 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -96,6 +96,7 @@ "string", "string_view", "strstream", + "syncstream", "system_error", "thread", "tuple", diff --git a/stl/inc/iosfwd b/stl/inc/iosfwd index 2eea4b96749..0aec6258a95 100644 --- a/stl/inc/iosfwd +++ b/stl/inc/iosfwd @@ -200,6 +200,14 @@ template > class basic_ofstream; template > class basic_fstream; +#if _HAS_CXX20 +template > +class _Basic_syncbuf_impl; +template , class _Alloc = allocator<_Elem>> +class basic_syncbuf; +template , class _Alloc = allocator<_Elem>> +class basic_osyncstream; +#endif // _HAS_CXX20 #if defined(_DLL_CPPLIB) template @@ -224,6 +232,10 @@ using filebuf = basic_filebuf>; using ifstream = basic_ifstream>; using ofstream = basic_ofstream>; using fstream = basic_fstream>; +#if _HAS_CXX20 +using syncbuf = basic_syncbuf; +using osyncstream = basic_osyncstream; +#endif // _HAS_CXX20 // wchar_t TYPEDEFS using wios = basic_ios>; @@ -239,6 +251,10 @@ using wfilebuf = basic_filebuf>; using wifstream = basic_ifstream>; using wofstream = basic_ofstream>; using wfstream = basic_fstream>; +#if _HAS_CXX20 +using wsyncbuf = basic_syncbuf; +using wosyncstream = basic_osyncstream; +#endif // _HAS_CXX20 #if defined(_CRTBLD) // unsigned short TYPEDEFS diff --git a/stl/inc/ostream b/stl/inc/ostream index 671f8d0e0bd..61766bf311e 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -993,6 +993,45 @@ basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL flush(basic_ostream<_Elem, _Tr return _Ostr; } +#if _HAS_CXX20 +#ifdef _CPPRTTI +template +basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { + const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + if (_Sync_buf_ptr) { + _Sync_buf_ptr->set_emit_on_sync(true); + } + return _Ostr; +} + +template +basic_ostream<_Elem, _Traits>& noemit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { + const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + if (_Sync_buf_ptr) { + _Sync_buf_ptr->set_emit_on_sync(false); + } + return _Ostr; +} + +template +basic_ostream<_Elem, _Traits>& flush_emit(basic_ostream<_Elem, _Traits>& _Ostr) { + _Ostr.flush(); + const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + if (_Sync_buf_ptr) { + _Sync_buf_ptr->_Do_emit(); + } + return _Ostr; +} +#else // _CPPRTTI +template +basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>&) = delete; // requires /GR option +template +basic_ostream<_Elem, _Traits>& noemit_on_flush(basic_ostream<_Elem, _Traits>&) = delete; // requires /GR option +template +basic_ostream<_Elem, _Traits>& flush_emit(basic_ostream<_Elem, _Traits>&) = delete; // requires /GR option +#endif // _CPPRTTI +#endif // _HAS_CXX20 + // INSERTER FOR error_category template basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, diff --git a/stl/inc/syncstream b/stl/inc/syncstream new file mode 100644 index 00000000000..434a1cd7a7f --- /dev/null +++ b/stl/inc/syncstream @@ -0,0 +1,381 @@ +// syncstream standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _SYNCSTREAM_ +#define _SYNCSTREAM_ +#include +#if _STL_COMPILER_PREPROCESSOR +#if !_HAS_CXX20 +#pragma message("The contents of are available only with C++20 or later.") +#else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv +#include +#include +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#pragma push_macro("emit") +#undef new +#undef emit + +_EXTERN_C +_NODISCARD _STD shared_mutex* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept; +void __stdcall __std_release_shared_mutex_for_instance(void* _Ptr) noexcept; +_END_EXTERN_C + +_STD_BEGIN + +// CLASS TEMPLATE _Basic_syncbuf_impl +template +class _Basic_syncbuf_impl : public basic_streambuf<_Elem, _Traits> { +public: + void set_emit_on_sync(const bool _Val) noexcept { + _Emit_on_sync = _Val; + } + + virtual bool _Do_emit() = 0; + +#ifdef _ENABLE_STL_INTERNAL_CHECK + _NODISCARD bool _Stl_internal_check_get_emit_on_sync() const noexcept { + return _Emit_on_sync; + } +#endif // _ENABLE_STL_INTERNAL_CHECK + +protected: + using _Mysb = basic_streambuf<_Elem, _Traits>; + + _Basic_syncbuf_impl() = default; + + _Basic_syncbuf_impl(_Basic_syncbuf_impl&& _Right) { + _Swap(_Right); + } + + void _Swap(_Basic_syncbuf_impl& _Right) { // see LWG-3498 regarding noexcept + _Mysb::swap(_Right); + _STD swap(_Emit_on_sync, _Right._Emit_on_sync); + _STD swap(_Sync_recorded, _Right._Sync_recorded); + } + + bool _Emit_on_sync{false}; + bool _Sync_recorded{false}; +}; + +// CLASS TEMPLATE basic_syncbuf +template +class basic_syncbuf : public _Basic_syncbuf_impl<_Elem, _Traits> { +public: + using int_type = typename _Traits::int_type; + using pos_type = typename _Traits::pos_type; + using off_type = typename _Traits::off_type; + using allocator_type = _Alloc; + using streambuf_type = basic_streambuf<_Elem, _Traits>; + + using _Mybase = _Basic_syncbuf_impl<_Elem, _Traits>; + using _Pointer = typename allocator_traits<_Alloc>::pointer; + using _Size_type = typename allocator_traits<_Alloc>::size_type; + + using _Mybase::set_emit_on_sync; + + basic_syncbuf() = default; + + explicit basic_syncbuf(streambuf_type* _Strbuf) : basic_syncbuf(_Strbuf, _Alloc{}) {} + + basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) + : _Wrapped(_Strbuf), _Mypair{_One_then_variadic_args_t{}, _Al_, nullptr} { + if (_Wrapped) { + auto& _Mutex = _Get_mutex(); + _Mutex = __std_acquire_shared_mutex_for_instance(_Wrapped); + if (!_Mutex) { + _Xbad_alloc(); + } + } + _Init(); + } + + basic_syncbuf(basic_syncbuf&& _Right) : _Mypair{_One_then_variadic_args_t{}, _STD move(_Right._Getal()), nullptr} { + _Swap_except_al(_Right); + } + + ~basic_syncbuf() { + _Emit(); + _Tidy(); + } + + basic_syncbuf& operator=(basic_syncbuf&& _Right) { // see LWG-3498 regarding noexcept + emit(); + if (this != _STD addressof(_Right)) { + _Move_assign(_STD move(_Right), _Choose_pocma<_Alloc>{}); + } + return *this; + } + + void swap(basic_syncbuf& _Right) { // see LWG-3498 regarding noexcept + if (this != _STD addressof(_Right)) { + _Pocs(_Getal(), _Right._Getal()); + _Swap_except_al(_Right); + } + } + + bool emit() { + if (!_Wrapped) { + return false; + } + + bool _Result = true; + const _Size_type _Data_size = _Get_data_size(); + _Elem* const _Begin_seq_ptr = streambuf_type::pbase(); + if (_Data_size > 0 || _Mybase::_Sync_recorded) { + scoped_lock _Guard(*_Get_mutex()); + + if (_Data_size > 0 + && _Data_size + != static_cast<_Size_type>( + _Wrapped->sputn(_Begin_seq_ptr, static_cast(_Data_size)))) { + _Result = false; + } + + if (_Mybase::_Sync_recorded) { + if (_Wrapped->pubsync() == -1) { + _Result = false; + } + } + } + _Mybase::_Sync_recorded = false; + streambuf_type::setp(_Begin_seq_ptr, streambuf_type::epptr()); // reset written data + return _Result; + } + + _NODISCARD streambuf_type* get_wrapped() const noexcept { + return _Wrapped; + } + + _NODISCARD allocator_type get_allocator() const noexcept { + return _Mypair._Get_first(); + } + +protected: + virtual int sync() override { + _Mybase::_Sync_recorded = true; + + if (_Mybase::_Emit_on_sync) { + if (!emit()) { + return -1; + } + } + return 0; + } + + virtual int_type overflow(int_type _Current_elem) override { + if (!_Wrapped) { + return _Traits::eof(); + } + const bool _Chk_eof = _Traits::eq_int_type(_Current_elem, _Traits::eof()); + if (_Chk_eof) { + return _Traits::not_eof(_Current_elem); + } + + auto& _Al = _Getal(); + const _Size_type _Buf_size = _Get_buffer_size(); + const _Size_type _Max_allocation = allocator_traits<_Alloc>::max_size(_Al); + if (_Buf_size == _Max_allocation) { + return _Traits::eof(); + } + + const _Size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation); + const _Elem* const _Old_ptr = streambuf_type::pbase(); + const _Size_type _Old_data_size = _Get_data_size(); + + _Elem* const _New_ptr = _Unfancy(_Al.allocate(_New_capacity)); + _Traits::copy(_New_ptr, _Old_ptr, _Old_data_size); + + streambuf_type::setp(_New_ptr, _New_ptr + _Old_data_size, _New_ptr + _New_capacity); + streambuf_type::sputc(_Traits::to_char_type(_Current_elem)); + + return _Current_elem; + } + +private: + static constexpr _Size_type _Min_size = 32; // constant for minimum buffer size + + void _Init() { + _Elem* const _New_ptr = _Unfancy(_Getal().allocate(_Min_size)); + streambuf_type::setp(_New_ptr, _New_ptr + _Min_size); + } + + void _Tidy() noexcept { + const _Size_type _Buf_size = _Get_buffer_size(); + if (0 < _Buf_size) { + _Getal().deallocate(_Refancy<_Pointer>(streambuf_type::pbase()), _Buf_size); + } + + streambuf_type::setp(nullptr, nullptr, nullptr); + if (_Wrapped) { + __std_release_shared_mutex_for_instance(_Wrapped); + _Wrapped = nullptr; + _Get_mutex() = nullptr; + } + } + + void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) { // see LWG-3498 regarding noexcept + _Tidy(); + _Pocma(_Getal(), _Right._Getal()); + _Swap_except_al(_Right); + } + + void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) { // see LWG-3498 regarding noexcept + _Tidy(); + _Pocma(_Getal(), _Right._Getal()); + _Swap_except_al(_Right); + } + + void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { // see LWG-3498 regarding noexcept + auto& _Al = _Getal(); + if (_Al == _Right._Getal()) { + _Move_assign(_STD move(_Right), _Equal_allocators{}); + } else { + _Tidy(); + + const _Size_type _Right_buf_size = _Right._Get_buffer_size(); + const _Size_type _Right_data_size = _Right._Get_data_size(); + + _Elem* const _New_ptr = _Unfancy(_Al.allocate(_Right_buf_size)); + _Traits::copy(_New_ptr, _Right.pbase(), _Right_data_size); + + streambuf_type::setp(_New_ptr, _New_ptr + _Right_data_size, _New_ptr + _Right_buf_size); + _STD swap(streambuf_type::_Plocale, _Right._Plocale); + + _STD swap(_Mybase::_Emit_on_sync, _Right._Emit_on_sync); + _STD swap(_Mybase::_Sync_recorded, _Right._Sync_recorded); + _STD swap(_Wrapped, _Right._Wrapped); + _STD swap(_Get_mutex(), _Right._Get_mutex()); + + _Right._Tidy(); + } + } + + void _Swap_except_al(basic_syncbuf& _Right) { // see LWG-3498 regarding noexcept + _Mybase::_Swap(_Right); + _STD swap(_Wrapped, _Right._Wrapped); + _STD swap(_Get_mutex(), _Right._Get_mutex()); + } + + virtual bool _Do_emit() override { + return emit(); + } + + bool _Emit() noexcept { + _TRY_BEGIN + return emit(); + _CATCH_ALL + return false; + _CATCH_END + } + + _NODISCARD static constexpr _Size_type _Calculate_growth( + const _Size_type _Oldsize, const _Size_type _Newsize, const _Size_type _Maxsize) { + if (_Oldsize > _Maxsize - _Oldsize / 2) { + return _Maxsize; // geometric growth would overflow + } + + const _Size_type _Geometric = _Oldsize + _Oldsize / 2; + + if (_Geometric < _Newsize) { + return _Newsize; // geometric growth would be insufficient + } + + return _Geometric; // geometric growth is sufficient + } + + _NODISCARD _Size_type _Get_data_size() const noexcept { + return static_cast<_Size_type>(streambuf_type::pptr() - streambuf_type::pbase()); + } + + _NODISCARD _Size_type _Get_buffer_size() const noexcept { + return static_cast<_Size_type>(streambuf_type::epptr() - streambuf_type::pbase()); + } + + _NODISCARD _Alloc& _Getal() noexcept { + return _Mypair._Get_first(); + } + + _NODISCARD shared_mutex*& _Get_mutex() noexcept { + return _Mypair._Myval2; + } + + streambuf_type* _Wrapped{nullptr}; + _Compressed_pair<_Alloc, shared_mutex*> _Mypair{_Zero_then_variadic_args_t{}, nullptr}; +}; + +template +void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left, + basic_syncbuf<_Elem, _Traits, _Alloc>& _Right) { // see LWG-3498 regarding noexcept + _Left.swap(_Right); +} + +// CLASS TEMPLATE basic_osyncstream +template +class basic_osyncstream : public basic_ostream<_Elem, _Traits> { +public: + using char_type = _Elem; + 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 = _Alloc; + using streambuf_type = basic_streambuf<_Elem, _Traits>; + using syncbuf_type = basic_syncbuf<_Elem, _Traits, _Alloc>; + + using _Mybase = basic_ostream<_Elem, _Traits>; + + basic_osyncstream(streambuf_type* _Strbuf, const _Alloc& _Al) + : _Mybase(_STD addressof(_Sync_buf)), _Sync_buf(_Strbuf, _Al) {} + + explicit basic_osyncstream(streambuf_type* _Strbuf) : basic_osyncstream(_Strbuf, _Alloc{}) {} + + basic_osyncstream(basic_ostream<_Elem, _Traits>& _Ostr, const _Alloc& _Al) + : basic_osyncstream(_Ostr.rdbuf(), _Al) {} + + explicit basic_osyncstream(basic_ostream<_Elem, _Traits>& _Ostr) : basic_osyncstream(_Ostr, _Alloc{}) {} + + basic_osyncstream(basic_osyncstream&& _Right) noexcept + : _Mybase(_STD move(_Right)), _Sync_buf(_STD move(_Right._Sync_buf)) { + _Mybase::set_rdbuf(_STD addressof(_Sync_buf)); + } + + ~basic_osyncstream() = default; + + basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default; + + void emit() { + if (!_Sync_buf.emit()) { + _Mybase::setstate(ios::badbit); + } + } + _NODISCARD streambuf_type* get_wrapped() const noexcept { + return _Sync_buf.get_wrapped(); + } + _NODISCARD syncbuf_type* rdbuf() const noexcept { + return const_cast(_STD addressof(_Sync_buf)); + } + +private: + syncbuf_type _Sync_buf; +}; + +_STD_END + +#pragma pop_macro("emit") +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _HAS_CXX20 +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _SYNCSTREAM_ diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 2e8d22b23d7..76d4fee65f3 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -133,6 +133,7 @@ // _HAS_CXX20 directly controls: // P0019R8 atomic_ref // P0020R6 atomic, atomic, atomic +// P0053R7 // P0122R7 // P0202R3 constexpr For And exchange() // P0318R1 unwrap_reference, unwrap_ref_decay @@ -167,6 +168,7 @@ // P0660R10 And jthread // P0674R1 make_shared() For Arrays // P0718R2 atomic>, atomic> +// P0753R2 osyncstream Manipulators // P0758R1 is_nothrow_convertible // P0768R1 Library Support For The Spaceship Comparison Operator <=> // P0769R2 shift_left(), shift_right() @@ -1246,6 +1248,7 @@ #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L #define __cpp_lib_starts_ends_with 201711L +#define __cpp_lib_syncbuf 201803L #ifdef __cpp_lib_concepts // TRANSITION, GH-395 #define __cpp_lib_three_way_comparison 201711L diff --git a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets index e7cc52c398d..05ebeab8862 100644 --- a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets +++ b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets @@ -8,6 +8,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception nativecpp diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index 5fd1b263103..b83c74b1e4f 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -6,16 +6,13 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception --> - - nativecpp diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index d8f2d843b5a..240916023ea 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -6,6 +6,7 @@ LIBRARY LIBRARYNAME EXPORTS + __std_acquire_shared_mutex_for_instance __std_atomic_compare_exchange_128 __std_atomic_get_mutex __std_atomic_has_cmpxchg16b @@ -24,5 +25,6 @@ EXPORTS __std_execution_wait_on_uchar __std_execution_wake_by_address_all __std_parallel_algorithms_hw_threads + __std_release_shared_mutex_for_instance __std_submit_threadpool_work __std_wait_for_threadpool_work_callbacks diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp new file mode 100644 index 00000000000..77e7df373e9 --- /dev/null +++ b/stl/src/syncstream.cpp @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// initialize syncstream mutex map + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma warning(disable : 4074) +#pragma init_seg(compiler) +static std::_Init_locks initlocks; + +namespace { + // OBJECT DECLARATIONS + struct _Mutex_count_pair { + _STD shared_mutex _Mutex; + size_t _Ref_count = 0; + }; + + template + class _Crt_allocator { + public: + using value_type = _Ty; + using propagate_on_container_move_assignment = _STD true_type; + using is_always_equal = _STD true_type; + + constexpr _Crt_allocator() noexcept = default; + + constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; + template + constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} + + _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + const auto _Ptr = _calloc_crt(_Count, sizeof(_Ty)); + if (!_Ptr) { + throw _STD bad_alloc{}; + } + return static_cast<_Ty*>(_Ptr); + } + + void deallocate(_Ty* const _Ptr, size_t) noexcept { + _free_crt(_Ptr); + } + }; + + using _Map_alloc = _Crt_allocator<_STD pair>; + using _Map_type = _STD map, _Map_alloc>; + + _Map_type _Lookup_map; + _STD shared_mutex _Lookup_mutex; +} // unnamed namespace + +_EXTERN_C + +// TRANSITION, ABI: This returns a pointer to a C++ type. +// A flat C interface would return an opaque handle and would provide separate functions for locking and unlocking. +_NODISCARD _STD shared_mutex* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept { + try { + _STD scoped_lock _Guard(_Lookup_mutex); + auto& [_Mutex, _Refs] = _Lookup_map.try_emplace(_Ptr).first->second; + ++_Refs; + return &_Mutex; + } catch (...) { + return nullptr; + } +} + +void __stdcall __std_release_shared_mutex_for_instance(void* _Ptr) noexcept { + _STD scoped_lock _Guard(_Lookup_mutex); + const auto _Instance_mutex_iter = _Lookup_map.find(_Ptr); + _ASSERT_EXPR(_Instance_mutex_iter != _Lookup_map.end(), "No mutex exists for given instance!"); + auto& _Refs = _Instance_mutex_iter->second._Ref_count; + if (--_Refs == 0) { + _Lookup_map.erase(_Instance_mutex_iter); + } +} + +_END_EXTERN_C diff --git a/tests/std/test.lst b/tests/std/test.lst index 7b5e0b22103..734e2bd3e70 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -209,6 +209,7 @@ tests\P0024R2_parallel_algorithms_transform_inclusive_scan tests\P0024R2_parallel_algorithms_transform_reduce tests\P0035R4_over_aligned_allocation tests\P0040R3_extending_memory_management_tools +tests\P0053R7_cpp_synchronized_buffered_ostream tests\P0067R5_charconv tests\P0083R3_splicing_maps_and_sets tests\P0088R3_variant @@ -258,6 +259,7 @@ tests\P0660R10_stop_token tests\P0660R10_stop_token_death tests\P0674R1_make_shared_for_arrays tests\P0718R2_atomic_smart_ptrs +tests\P0753R2_manipulators_for_cpp_synchronized_buffered_ostream tests\P0758R1_is_nothrow_convertible tests\P0768R1_spaceship_cpos tests\P0768R1_spaceship_operator diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp new file mode 100644 index 00000000000..06da2ce3a88 --- /dev/null +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -0,0 +1,385 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "test.hpp" + +using namespace std; + +static_assert(is_default_constructible_v); +static_assert(is_constructible_v); +static_assert(is_constructible_v); +static_assert(is_move_constructible_v); +static_assert(is_move_assignable_v); +static_assert(is_swappable_v); +static_assert(is_destructible_v); + +template +class test_syncbuf : public basic_syncbuf { +public: + using size_type = typename Alloc::size_type; + using value_type = typename Alloc::value_type; + using Mybase = basic_syncbuf; + using streambuf_type = typename Mybase::streambuf_type; + + using Mybase::epptr; + using Mybase::pbase; + using Mybase::pptr; + + test_syncbuf() = default; + + explicit test_syncbuf(streambuf_type* strbuf) : Mybase(strbuf) {} + + test_syncbuf(streambuf_type* strbuf, const Alloc& al) : Mybase(strbuf, al) {} + + test_syncbuf(test_syncbuf&&) = default; + test_syncbuf& operator=(test_syncbuf&&) = default; + ~test_syncbuf() = default; + + auto Test_get_buffer_size() const noexcept { + return static_cast(epptr() - pbase()); + } + auto Test_get_data_size() const noexcept { + return static_cast(pptr() - pbase()); + } + + test_syncbuf(const test_syncbuf&) = delete; + test_syncbuf& operator=(const test_syncbuf&) = delete; +}; + +template +void test_syncbuf_member_functions(string_buffer* buf = nullptr) { + + using value_type = typename Alloc::value_type; + using Syncbuf = test_syncbuf, Alloc>; + using OStream = basic_ostream>; + + Alloc alloc{}; + + Syncbuf aSyncbuf{buf, alloc}; + + // test construction post-conditions + assert(aSyncbuf.get_wrapped() == buf); + assert(aSyncbuf.get_allocator() == alloc); + assert(aSyncbuf.Test_get_data_size() == 0); + assert(aSyncbuf.Test_get_buffer_size() == Min_syncbuf_size); + + // check emit post-conditions with no input + if (buf) { + assert(aSyncbuf.emit() == true); + } else { + assert(aSyncbuf.emit() == false); + } + + OStream os{&aSyncbuf}; + + os << "A small string\n"; + assert(aSyncbuf.Test_get_data_size() == 15); + if (buf) { + assert(aSyncbuf.emit() == true); + assert(buf->str == "A small string\n"); + buf->str.clear(); + } else { + assert(os.rdstate() == ios::goodbit); + assert(aSyncbuf.emit() == false); + } + + os << "A string holds more than 32 characters"; // requires one re-allocation + if (buf) { + assert(aSyncbuf.Test_get_data_size() == 38); + assert(aSyncbuf.emit() == true); + assert(buf->str == "A string holds more than 32 characters"); + buf->str.clear(); + } else { + assert(aSyncbuf.Test_get_data_size() == Min_syncbuf_size); // if _Wrapped is nullptr, re-allocation will not + // occur and will return a _Traits::eof bit + assert(os.rdstate() == ios::badbit); + os.setstate(ios::goodbit); + assert(aSyncbuf.emit() == false); + } + + os << "A string that will definitely overflow the small_size_allocator"; // requires more than one re-allocation + if (buf) { + if constexpr (is_base_of_v) { // fail to allocate enough memory + assert(aSyncbuf.Test_get_data_size() == Min_size_allocation); + assert(os.rdstate() == ios::badbit); + os.setstate(ios::goodbit); + assert(aSyncbuf.emit() == true); + assert(buf->str == "A string that will definitely overflow the small_s"); + } else { + assert(aSyncbuf.Test_get_data_size() == 63); + assert(os.rdstate() == ios::goodbit); + assert(aSyncbuf.emit() == true); + assert(buf->str == "A string that will definitely overflow the small_size_allocator"); + } + buf->str.clear(); + } else { + assert(aSyncbuf.Test_get_data_size() == Min_syncbuf_size); + assert(os.rdstate() == ios::badbit); + os.setstate(ios::goodbit); + assert(aSyncbuf.emit() == false); + } +} + +template +void test_syncbuf_synchronization(string_buffer* buf) { + assert(buf); // meaningless to run with nullptr + + using value_type = typename Alloc::value_type; + using Syncbuf = test_syncbuf, Alloc>; + using OStream = basic_ostream>; + + { + Syncbuf buf1{buf}; + OStream os1{&buf1}; + os1 << "Last element "; + { + Syncbuf buf2{buf}; + buf2.set_emit_on_sync(false); + OStream os2{&buf2}; + os2 << "Second element "; + int syncResult = buf2.pubsync(); // trigger a sync + assert(syncResult == 0); + { + Syncbuf buf3{buf}; + OStream{&buf3} << "First element to be presented!\n"; + } + os2 << "to be presented!\n"; + } + os1 << "to be presented!\n"; + } + assert( + buf->str == "First element to be presented!\nSecond element to be presented!\nLast element to be presented!\n"); + buf->str.clear(); + { + Syncbuf buf1{buf}; + OStream os1{&buf1}; + os1 << "Last element "; + { + Syncbuf buf2{buf}; + buf2.set_emit_on_sync(true); + OStream os2{&buf2}; + os2 << "First element to be emitted by sync!\n"; + int syncResult = buf2.pubsync(); // trigger a sync + if constexpr (ThrowOnSync) { + assert(syncResult == -1); + } else { + assert(syncResult == 0); + } + { + Syncbuf buf3{buf}; + OStream{&buf3} << "Second element to be presented!\n"; + } + os2 << "Third element to be presented!\n"; + } + os1 << "to be presented!\n"; + } + assert(buf->str + == "First element to be emitted by sync!\nSecond element to be presented!\nThird element to be " + "presented!\nLast element to be presented!\n"); + buf->str.clear(); +} + +template +void test_syncbuf_move_swap_operations(string_buffer* buf) { + + using value_type = typename Alloc::value_type; + using Syncbuf = test_syncbuf, Alloc>; + using OStream = basic_ostream>; + + { // test move constructor + Syncbuf buf1{buf}; + OStream(&buf1) << "Some input"; + auto buf1WrappedObject = buf1.get_wrapped(); + auto buf1BufferSize = buf1.Test_get_buffer_size(); + auto buf1DataSize = buf1.Test_get_data_size(); + Syncbuf buf2{move(buf1)}; + assert(buf->str == ""); + + // move constructor post-conditions + assert(buf2.get_wrapped() == buf1WrappedObject); + assert(buf2.Test_get_buffer_size() == buf1BufferSize); + assert(buf2.Test_get_data_size() == buf1DataSize); + assert(buf1.get_wrapped() == nullptr); + assert(buf1.pbase() == buf1.pptr()); + assert(buf1.Test_get_buffer_size() == 0); + assert(buf1.Test_get_data_size() == 0); + } + assert(buf->str == "Some input"); + buf->str.clear(); + { // test move assignment + Syncbuf buf1{buf}; + OStream{&buf1} << "Some input"; + Syncbuf buf2{nullptr}; + auto buf1WrappedObject = buf1.get_wrapped(); + auto buf1BufferSize = buf1.Test_get_buffer_size(); + auto buf1DataSize = buf1.Test_get_data_size(); + buf2 = move(buf1); + + // move assignment post-conditions + assert(buf2.get_wrapped() == buf1WrappedObject); + assert(buf2.Test_get_buffer_size() == buf1BufferSize); + assert(buf2.Test_get_data_size() == buf1DataSize); + assert(buf1.get_wrapped() == nullptr); + assert(buf1.Test_get_buffer_size() == 0); + assert(buf1.Test_get_data_size() == 0); + + if constexpr (allocator_traits::propagate_on_container_move_assignment::value + && allocator_traits::is_always_equal::value) { + assert(buf1.get_allocator() == buf2.get_allocator()); + } else if constexpr (allocator_traits::is_always_equal::value) { + assert(buf1.get_allocator() == buf2.get_allocator()); + } else { + assert(buf1.get_allocator() != buf2.get_allocator()); + } + } + assert(buf->str == "Some input"); + buf->str.clear(); + { // test swap + Syncbuf buf1{buf}; + OStream{&buf1} << "Some input that requires re-allocation"; + Syncbuf buf2{nullptr}; + auto buf1WrappedObject = buf1.get_wrapped(); + auto buf1BufferSize = buf1.Test_get_buffer_size(); + auto buf1DataSize = buf1.Test_get_data_size(); + auto buf2WrappedObject = buf2.get_wrapped(); + auto buf2BufferSize = buf2.Test_get_buffer_size(); + auto buf2DataSize = buf2.Test_get_data_size(); + if constexpr (allocator_traits::propagate_on_container_swap::value + || allocator_traits::is_always_equal::value) { + buf1.swap(buf2); + assert(buf2.get_wrapped() == buf1WrappedObject); + assert(buf2.Test_get_buffer_size() == buf1BufferSize); + assert(buf2.Test_get_data_size() == buf1DataSize); + assert(buf1.get_wrapped() == buf2WrappedObject); + assert(buf1.Test_get_buffer_size() == buf2BufferSize); + assert(buf1.Test_get_data_size() == buf2DataSize); + } + } + assert(buf->str == "Some input that requires re-allocation"); + buf->str.clear(); +} + +template +void test_osyncstream(string_buffer* buf = nullptr) { + using value_type = typename Alloc::value_type; + using OSyncStream = basic_osyncstream, Alloc>; + + { + OSyncStream oss{buf}; + + // test construction post-conditions + assert(oss.get_wrapped() == buf); + assert(oss.rdbuf()->get_wrapped() == buf); + + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + + oss << "A small string\n"; + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + if (buf) { + assert(buf->str == "A small string\n"); + buf->str.clear(); + } + + oss << "A string holds more than 32 characters"; // requires one re-allocation + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + if (buf) { + assert(buf->str == "A string holds more than 32 characters"); + buf->str.clear(); + } + + oss << "A string that will definitely overflow the small_size_allocator"; // requires more than one + // re-allocation + if constexpr (is_base_of_v) { // fail to allocate enough memory + assert(oss.rdstate() == ios::badbit); + oss.clear(); + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + assert(buf ? buf->str == "A string that will definitely overflow the small_s" : true); + } else { + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + assert(buf ? buf->str == "A string that will definitely overflow the small_size_allocator" : true); + } + } + if (buf) { + buf->str.clear(); + } + + { // move construction + OSyncStream oss{buf}; + oss << "Some input"; + auto ossWrapped = oss.get_wrapped(); + OSyncStream oss1{move(oss)}; + + assert(oss1.get_wrapped() == ossWrapped); + assert(oss.get_wrapped() == nullptr); + } + if (buf) { + assert(buf->str == "Some input"); + buf->str.clear(); + } + + { + OSyncStream oss{buf}; + oss << "Some input"; + auto ossWrapped = oss.get_wrapped(); + + OSyncStream oss1{buf}; + oss1 << "An input to emit first\n"; + + oss1 = move(oss); + + assert(oss1.get_wrapped() == ossWrapped); + assert(oss.get_wrapped() == nullptr); + if (buf) { + assert(buf->str == "An input to emit first\n"); + buf->str.clear(); + } + } + if (buf) { + assert(buf->str == "Some input"); + buf->str.clear(); + } + + { // test synchronization + OSyncStream oss(buf); + oss << "Last "; + { OSyncStream(oss.get_wrapped()) << "First Input!\n"; } + oss << "Input!" << '\n'; + } + if (buf) { + assert(buf->str == "First Input!\nLast Input!\n"); + buf->str.clear(); + } +} + +int main() { + string_buffer char_buffer{}; + string_buffer no_sync_char_buffer{}; + + // Testing basic_syncbuf + test_syncbuf_member_functions>(); + test_syncbuf_member_functions>(); + + test_syncbuf_member_functions>(&char_buffer); + test_syncbuf_member_functions>(&char_buffer); + + test_syncbuf_synchronization>(&char_buffer); + test_syncbuf_synchronization>(&no_sync_char_buffer); + + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); + + // Testing basic_osyncstream + test_osyncstream>(); + test_osyncstream>(); + + test_osyncstream>(&char_buffer); + test_osyncstream>(&char_buffer); +} diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp new file mode 100644 index 00000000000..beffa7170de --- /dev/null +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +constexpr size_t Min_size_allocation = 50; +constexpr size_t Min_syncbuf_size = 32; + +template +class string_buffer : public basic_streambuf> { // represents the wrapped object in syncbuf +public: + string_buffer() = default; + ~string_buffer() = default; + + streamsize xsputn(const Ty* ptr, streamsize n) override { + str.append(ptr, static_cast(n)); + return n; + } + + int sync() override { + if constexpr (ThrowOnSync) { + return -1; + } else { + return 0; + } + } + + string str; +}; + +class small_size_allocation { +public: + using size_type = size_t; + + [[nodiscard]] size_type max_size() const noexcept { + return Min_size_allocation; + } +}; + +template +class allocator_base { +public: + using value_type = Ty; + using pointer = Ty*; + + [[nodiscard]] pointer allocate(const size_t n) { + return allocator{}.allocate(n); + } + + void deallocate(const pointer ptr, const size_t n) noexcept { + allocator{}.deallocate(ptr, n); + } +}; + +template +class small_size_allocator : public allocator_base, public small_size_allocation { +public: + using propagate_on_container_move_assignment = true_type; + using propagate_on_container_swap = true_type; + + constexpr small_size_allocator() noexcept = default; + + template + constexpr small_size_allocator(const small_size_allocator&) noexcept {} +}; + +template +[[nodiscard]] bool operator==(const small_size_allocator&, const small_size_allocator&) noexcept { + return true; +} + +class non_move_assignable_non_equal_allocator_id { +public: + non_move_assignable_non_equal_allocator_id() : id(id_gen++) {} + constexpr explicit non_move_assignable_non_equal_allocator_id(size_t _id) : id(_id) {} + ~non_move_assignable_non_equal_allocator_id() = default; + + size_t id; + +private: + inline static size_t id_gen = 0; +}; + +template +class non_move_assignable_non_equal_allocator : public non_move_assignable_non_equal_allocator_id, + public allocator_base { +public: + using size_type = size_t; + using propagate_on_container_move_assignment = false_type; + using propagate_on_container_swap = true_type; + using is_always_equal = false_type; + + non_move_assignable_non_equal_allocator() noexcept = default; + + template + constexpr non_move_assignable_non_equal_allocator( + const non_move_assignable_non_equal_allocator& rhs) noexcept + : non_move_assignable_non_equal_allocator_id{rhs.id} {} +}; + +template +[[nodiscard]] bool operator==(const non_move_assignable_non_equal_allocator& lhs, + const non_move_assignable_non_equal_allocator& rhs) noexcept { + return lhs.id == rhs.id; +} + +template +class non_move_assignable_equal_allocator : public allocator_base { +public: + using size_type = size_t; + using propagate_on_container_move_assignment = false_type; + using propagate_on_container_swap = true_type; + + constexpr non_move_assignable_equal_allocator() noexcept = default; + + template + constexpr non_move_assignable_equal_allocator(const non_move_assignable_equal_allocator&) noexcept {} +}; + +template +[[nodiscard]] bool operator==( + const non_move_assignable_equal_allocator&, const non_move_assignable_equal_allocator&) noexcept { + return true; +} + +template +class non_swappable_equal_allocator : public allocator_base { +public: + using size_type = size_t; + using propagate_on_container_move_assignment = true_type; + using propagate_on_container_swap = false_type; + + constexpr non_swappable_equal_allocator() noexcept = default; + + template + constexpr non_swappable_equal_allocator(const non_swappable_equal_allocator&) noexcept {} +}; + +template +[[nodiscard]] bool operator==( + const non_swappable_equal_allocator&, const non_swappable_equal_allocator&) noexcept { + return true; +} diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp new file mode 100644 index 00000000000..f58fde52393 --- /dev/null +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +class string_buffer : public basic_streambuf> { // represents the wrapped object in syncbuf +public: + string_buffer() = default; + ~string_buffer() = default; + + streamsize xsputn(const Ty* ptr, streamsize n) override { + str.append(ptr, static_cast(n)); + return n; + } + + int sync() override { + if constexpr (ThrowOnSync) { + return -1; + } else { + return 0; + } + } + + string str; +}; + + +template +void test_osyncstream_manipulators(string_buffer* buf = nullptr) { + using char_type = typename Ty::char_type; + using traits_type = typename Ty::traits_type; + + static_assert(is_base_of_v, Ty>); + + Ty os{buf}; + os << "Some input"; + + assert(addressof(emit_on_flush(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + auto* aSyncbuf = static_cast*>(os.rdbuf()); + assert(aSyncbuf->_Stl_internal_check_get_emit_on_sync() == true); + if (buf) { + assert(buf->str == ""); + } + } + + assert(addressof(flush_emit(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + if constexpr (ThrowOnSync) { + assert(os.rdstate() == ios::badbit); + } else { + assert(os.rdstate() == (buf ? ios::goodbit : ios::badbit)); + } + + if (buf) { + assert(buf->str == "Some input"); + buf->str.clear(); + } + os.clear(); + os << "Another input"; + } + + assert(addressof(noemit_on_flush(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + auto* aSyncbuf = static_cast*>(os.rdbuf()); + assert(aSyncbuf->_Stl_internal_check_get_emit_on_sync() == false); + } + + assert(addressof(flush_emit(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + assert(os.rdstate() == ios::goodbit); + if (buf) { + assert(buf->str == "Another input"); + } + os.clear(); + } + + if (buf) { + buf->str.clear(); + } +} + +int main() { + string_buffer char_buffer{}; + string_buffer no_sync_char_buffer{}; + + test_osyncstream_manipulators>(); + test_osyncstream_manipulators, allocator>, allocator>(); + + test_osyncstream_manipulators>(&char_buffer); + test_osyncstream_manipulators, allocator>, allocator>( + &char_buffer); + + test_osyncstream_manipulators>(&no_sync_char_buffer); + test_osyncstream_manipulators, allocator>, allocator>( + &no_sync_char_buffer); +} diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py index 8fe322f324d..ddf75ebbd8d 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py +++ b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py @@ -73,7 +73,7 @@ def getBuildSteps(self, test, litConfig, shared): 'string_view', 'string', 'strstream', - # 'syncstream', + 'syncstream', 'system_error', 'thread', 'tuple', diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl index 214e5463338..2ccde3fc6e8 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl +++ b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl @@ -72,7 +72,7 @@ () "string_view", "string", "strstream", - # "syncstream", + "syncstream", "system_error", "thread", "tuple", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index 7dba262dbb0..8b0e829db32 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -73,7 +73,7 @@ import ; import ; import ; import ; -// import ; +import ; import ; import ; import ; @@ -810,7 +810,13 @@ int main() { { puts("Testing ."); - puts("(TRANSITION, not yet implemented.)"); + syncbuf sync_buf{nullptr}; + assert(sync_buf.get_wrapped() == nullptr); + assert(sync_buf.get_allocator() == allocator{}); + assert(sync_buf.emit() == false); + osyncstream sync_str{cout}; + sync_str << "Testing P1502R1_standard_library_header_units.\n"; + assert(sync_str.rdbuf()->get_wrapped() == cout.rdbuf()); } { diff --git a/tests/std/tests/VSO_0000000_fancy_pointers/test.compile.pass.cpp b/tests/std/tests/VSO_0000000_fancy_pointers/test.compile.pass.cpp index 60f0b53fbc0..2a6a9b4b90e 100644 --- a/tests/std/tests/VSO_0000000_fancy_pointers/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0000000_fancy_pointers/test.compile.pass.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -431,6 +432,11 @@ template class std::unordered_multiset, fancy_all template class std::basic_string, fancy_allocator>; template class std::basic_stringbuf, fancy_allocator>; +#if _HAS_CXX20 +template class std::basic_syncbuf, fancy_allocator>; +template class std::basic_osyncstream, fancy_allocator>; +#endif + void instantiate() { random_iterators_test>>(); fwd_iterators_test>>(); diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index f4ab60d8fcc..f6d8967ade9 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1403,6 +1403,20 @@ STATIC_ASSERT(__cpp_lib_string_view == 201803L); #endif #endif +#if _HAS_CXX20 +#ifndef __cpp_lib_syncbuf +#error __cpp_lib_syncbuf is not defined +#elif __cpp_lib_syncbuf != 201803L +#error __cpp_lib_syncbuf is not 201803L +#else +STATIC_ASSERT(__cpp_lib_syncbuf == 201803L); +#endif +#else +#ifdef __cpp_lib_syncbuf +#error __cpp_lib_syncbuf is defined +#endif +#endif + #if _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_three_way_comparison #error __cpp_lib_three_way_comparison is not defined diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index 6728e9f7124..1a7ff0a6cd4 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -67,6 +67,7 @@ PM_CL="/DMEOW_HEADER=streambuf" PM_CL="/DMEOW_HEADER=string" PM_CL="/DMEOW_HEADER=string_view" PM_CL="/DMEOW_HEADER=strstream /D_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING" +PM_CL="/DMEOW_HEADER=syncstream" PM_CL="/DMEOW_HEADER=system_error" PM_CL="/DMEOW_HEADER=thread" PM_CL="/DMEOW_HEADER=tuple"