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/chrono b/stl/inc/chrono index 25d3b2ac196..c664419e172 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -17,6 +17,9 @@ #if _HAS_CXX20 #include +#ifdef __cpp_lib_concepts +#include +#endif // defined(__cpp_lib_concepts) #endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) @@ -370,12 +373,14 @@ namespace chrono { return _CT(_Left).count() == _CT(_Right).count(); } +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const duration<_Rep1, _Period1>& _Left, const duration<_Rep2, _Period2>& _Right) noexcept( is_arithmetic_v<_Rep1>&& is_arithmetic_v<_Rep2>) /* strengthened */ { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool @@ -406,6 +411,19 @@ namespace chrono { return !(_Left < _Right); } +#ifdef __cpp_lib_concepts + // clang-format off + template + requires three_way_comparable, duration<_Rep2, _Period2>>::rep> + _NODISCARD constexpr auto + operator<=>(const duration<_Rep1, _Period1>& _Left, const duration<_Rep2, _Period2>& _Right) noexcept( + is_arithmetic_v<_Rep1>&& is_arithmetic_v<_Rep2>) /* strengthened */ { + // clang-format on + using _CT = common_type_t, duration<_Rep2, _Period2>>; + return _CT(_Left).count() <=> _CT(_Right).count(); + } +#endif // defined(__cpp_lib_concepts) + // FUNCTION TEMPLATE duration_cast template , int> _Enabled> _NODISCARD constexpr _To duration_cast(const duration<_Rep, _Period>& _Dur) noexcept( @@ -550,12 +568,14 @@ namespace chrono { return _Left.time_since_epoch() == _Right.time_since_epoch(); } +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const time_point<_Clock, _Duration1>& _Left, const time_point<_Clock, _Duration2>& _Right) noexcept( is_arithmetic_v&& is_arithmetic_v) /* strengthened */ { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool @@ -585,6 +605,15 @@ namespace chrono { return !(_Left < _Right); } +#ifdef __cpp_lib_concepts + template _Duration2> + _NODISCARD constexpr auto + operator<=>(const time_point<_Clock, _Duration1>& _Left, const time_point<_Clock, _Duration2>& _Right) noexcept( + is_arithmetic_v&& is_arithmetic_v) /* strengthened */ { + return _Left.time_since_epoch() <=> _Right.time_since_epoch(); + } +#endif // defined(__cpp_lib_concepts) + // FUNCTION TEMPLATE time_point_cast template , int> = 0> _NODISCARD constexpr time_point<_Clock, _To> time_point_cast(const time_point<_Clock, _Duration>& _Time) noexcept( 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/memory b/stl/inc/memory index 279effbcc6b..ae479ece7bb 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -149,7 +149,9 @@ namespace ranges { auto _OFirst = _Get_unwrapped(_STD move(_First2)); const auto _OLast = _Get_unwrapped(_STD move(_Last2)); if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial) { - _OFirst = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + auto _UResult = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + _IFirst = _UResult.in; + _OFirst = _UResult.out; } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -283,7 +285,9 @@ namespace ranges { auto _OFirst = _Get_unwrapped(_STD move(_First2)); const auto _OLast = _Get_unwrapped(_STD move(_Last2)); if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial) { - _OFirst = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + auto _UResult = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + _IFirst = _UResult.in; + _OFirst = _UResult.out; } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; 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/variant b/stl/inc/variant index 7389fbee200..036e1d602aa 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -967,10 +967,25 @@ using _Variant_destroy_layer = conditional_t + +#if _HAS_CXX20 +// build Ti x[] = {std::forward(t)}; +template +auto _Construct_array(_TargetType(&&)[1]) -> _Meta_list, _TargetType>; + +template +using _Variant_type_resolver = decltype(_Construct_array<_Idx, _TargetType>({_STD declval<_InitializerType>()})); +#endif // _HAS_CXX20 + +template struct _Variant_init_single_overload { - using _FTy = _Meta_list, _Ty> (*)(_Ty); - operator _FTy(); +#if _HAS_CXX20 + template + auto operator()(_TargetType, _InitializerType&&) -> _Variant_type_resolver<_Idx, _TargetType, _InitializerType>; +#else // _HAS_CXX20 + template + auto operator()(_TargetType, _InitializerType&&) -> _Meta_list, _TargetType>; +#endif // _HAS_CXX20 }; template @@ -978,7 +993,9 @@ struct _Variant_init_overload_set_; template struct _Variant_init_overload_set_, _Types...> - : _Variant_init_single_overload<_Indices, _Types>... {}; + : _Variant_init_single_overload<_Indices, _Types>... { + using _Variant_init_single_overload<_Indices, _Types>::operator()...; +}; template using _Variant_init_overload_set = _Variant_init_overload_set_, _Types...>; @@ -987,11 +1004,12 @@ template struct _Variant_init_helper {}; // failure case (has no member "type") template -struct _Variant_init_helper{}(_STD declval<_Ty>()))>, _Ty, +struct _Variant_init_helper< + void_t{}(_STD declval<_Ty>(), _STD declval<_Ty>()))>, _Ty, _Types...> { // perform overload resolution to determine the unique alternative that should be initialized in // variant<_Types...> from an argument expression with type and value category _Ty - using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>())); + using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>(), _STD declval<_Ty>())); }; template // extract the type from _Variant_init_helper diff --git a/stl/inc/xmemory b/stl/inc/xmemory index e83b30e271b..ed52ae8b548 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1553,6 +1553,18 @@ namespace ranges { && _No_throw_forward_iterator>; // clang-format on + template + in_out_result<_InIt, _OutIt> _Copy_memcpy_common( + _InIt _IFirst, _InIt _ILast, _OutIt _OFirst, _OutIt _OLast) noexcept { + const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirst)); + const auto _ILast_ch = const_cast(reinterpret_cast(_ILast)); + const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirst)); + const auto _OLast_ch = const_cast(reinterpret_cast(_OLast)); + const auto _Count = static_cast((_STD min)(_ILast_ch - _IFirst_ch, _OLast_ch - _OFirst_ch)); + _CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count); + return {reinterpret_cast<_InIt>(_IFirst_ch + _Count), reinterpret_cast<_OutIt>(_OFirst_ch + _Count)}; + } + // ALIAS TEMPLATE uninitialized_move_result template using uninitialized_move_result = in_out_result<_In, _Out>; diff --git a/stl/inc/xutility b/stl/inc/xutility index eb2a04cadf3..66e6cbd227b 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4102,17 +4102,6 @@ _OutIt _Copy_memmove(move_iterator<_InIt> _First, move_iterator<_InIt> _Last, _O return _Copy_memmove(_First.base(), _Last.base(), _Dest); } -template -_OutIt _Copy_memcpy_common(_InIt _IFirst, _InIt _ILast, _OutIt _OFirst, _OutIt _OLast) noexcept { - const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirst)); - const auto _ILast_ch = const_cast(reinterpret_cast(_ILast)); - const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirst)); - const auto _OLast_ch = const_cast(reinterpret_cast(_OLast)); - const auto _Count = static_cast((_STD min)(_ILast_ch - _IFirst_ch, _OLast_ch - _OFirst_ch)); - _CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count); - return reinterpret_cast<_OutIt>(_OFirst_ch + _Count); -} - // VARIABLE TEMPLATE _Is_vb_iterator template _INLINE_VAR constexpr bool _Is_vb_iterator = false; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 2e8d22b23d7..988fb6bcfdd 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 @@ -159,6 +160,7 @@ // P0556R3 Integral Power-Of-2 Operations (renamed by P1956R1) // P0586R2 Integer Comparison Functions // P0595R2 is_constant_evaluated() +// P0608R3 Improving variant's Converting Constructor/Assignment // P0616R0 Using move() In // P0631R8 Math Constants // P0646R1 list/forward_list remove()/remove_if()/unique() Return size_type @@ -167,6 +169,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 +1249,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 6f890034c25..3ac4d56e19c 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 @@ -251,6 +252,7 @@ tests\P0556R3_bit_integral_power_of_two_operations tests\P0586R2_integer_comparison tests\P0595R2_is_constant_evaluated tests\P0607R0_inline_variables +tests\P0608R3_improved_variant_converting_constructor tests\P0616R0_using_move_in_numeric tests\P0631R8_numbers_math_constants tests\P0660R10_jthread_and_cv_any @@ -258,6 +260,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/P0088R3_variant/env.lst b/tests/std/tests/P0088R3_variant/env.lst index 48e3b76c748..474ef952dd3 100644 --- a/tests/std/tests/P0088R3_variant/env.lst +++ b/tests/std/tests/P0088R3_variant/env.lst @@ -10,7 +10,7 @@ RUNALL_CROSSLIST PM_CL="/w14640 /Zc:threadSafeInit-" RUNALL_CROSSLIST PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:noexceptTypes-" -PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /DCONSTEXPR_NOTHROW" +PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:wchar_t-" @@ -22,15 +22,15 @@ PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /analyze: PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /fp:strict" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" -PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /DCONSTEXPR_NOTHROW" +PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /analyze:only /analyze:autolog-" PM_CL="/Za /EHsc /MD /std:c++latest /permissive-" PM_CL="/Za /EHsc /MDd /std:c++latest /permissive-" -PM_CL="/clr /MD /std:c++17 /DCONSTEXPR_NOTHROW" -PM_CL="/clr /MDd /std:c++17 /DCONSTEXPR_NOTHROW" +PM_CL="/clr /MD /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" +PM_CL="/clr /MDd /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive-" PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive-" PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++latest /permissive-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MDd /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MDd /std:c++17 /DTEST_PERMISSIVE" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MTd /std:c++latest /permissive- /fp:strict" diff --git a/tests/std/tests/P0088R3_variant/test.cpp b/tests/std/tests/P0088R3_variant/test.cpp index 7a66a9f06e0..87e482ffec0 100644 --- a/tests/std/tests/P0088R3_variant/test.cpp +++ b/tests/std/tests/P0088R3_variant/test.cpp @@ -1768,7 +1768,8 @@ int run_test() { static_assert(!std::is_assignable, int>::value, ""); static_assert(!std::is_assignable, int>::value, ""); -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 static_assert(std::is_assignable, int>::value == VariantAllowsNarrowingConversions, ""); static_assert(std::is_assignable, int>::value @@ -1778,13 +1779,16 @@ int run_test() static_assert(!std::is_assignable, int>::value, ""); static_assert(!std::is_assignable, decltype("meow")>::value, ""); +#endif // !__EDG__ static_assert(!std::is_assignable, decltype("meow")>::value, ""); static_assert(!std::is_assignable, decltype("meow")>::value, ""); - static_assert(!std::is_assignable, std::true_type>::value, ""); + static_assert(std::is_assignable, std::true_type>::value, ""); static_assert(!std::is_assignable, std::unique_ptr >::value, ""); +#ifndef TEST_PERMISSIVE static_assert(!std::is_assignable, decltype(nullptr)>::value, ""); -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 return 0; } @@ -3048,7 +3052,8 @@ void test_T_assignment_sfinae() { using V = std::variant; static_assert(!std::is_assignable::value, "no matching operator="); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 { using V = std::variant; static_assert(std::is_assignable::value == VariantAllowsNarrowingConversions, @@ -3063,10 +3068,11 @@ void test_T_assignment_sfinae() { }; static_assert(!std::is_assignable::value, "no boolean conversion in operator="); - static_assert(!std::is_assignable::value, + static_assert(std::is_assignable::value, "no converted to bool in operator="); } -#endif // TRANSITION, P0608 +#endif // !__EDG__ +#endif // _HAS_CXX20 { struct X {}; struct Y { @@ -3104,7 +3110,8 @@ void test_T_assignment_basic() { assert(v.index() == 1); assert(std::get<1>(v) == 43); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS { std::variant v; @@ -3116,19 +3123,22 @@ void test_T_assignment_basic() { assert(std::get<0>(v) == 43); } #endif +#endif // !__EDG__ { std::variant v = true; v = "bar"; assert(v.index() == 0); assert(std::get<0>(v) == "bar"); } +#ifndef TEST_PERMISSIVE { std::variant> v; v = nullptr; assert(v.index() == 1); assert(std::get<1>(v) == nullptr); } -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 { std::variant v = 42; v = false; @@ -3266,7 +3276,8 @@ int run_test() { static_assert(!std::is_constructible, int>::value, ""); static_assert(!std::is_constructible, int>::value, ""); -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 static_assert(std::is_constructible, int>::value == VariantAllowsNarrowingConversions, ""); static_assert(std::is_constructible, int>::value @@ -3278,11 +3289,13 @@ int run_test() static_assert(!std::is_constructible, decltype("meow")>::value, ""); static_assert(!std::is_constructible, decltype("meow")>::value, ""); static_assert(!std::is_constructible, decltype("meow")>::value, ""); - - static_assert(!std::is_constructible, std::true_type>::value, ""); +#endif // !__EDG__ + static_assert(std::is_constructible, std::true_type>::value, ""); static_assert(!std::is_constructible, std::unique_ptr >::value, ""); +#ifndef TEST_PERMISSIVE static_assert(!std::is_constructible, decltype(nullptr)>::value, ""); -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 return 0; } @@ -4567,7 +4580,8 @@ void test_T_ctor_sfinae() { static_assert(!std::is_constructible::value, "no matching constructor"); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 { using V = std::variant; static_assert(std::is_constructible::value == VariantAllowsNarrowingConversions, @@ -4582,10 +4596,11 @@ void test_T_ctor_sfinae() { }; static_assert(!std::is_constructible::value, "no boolean conversion in constructor"); - static_assert(!std::is_constructible::value, + static_assert(std::is_constructible::value, "no converted to bool in constructor"); } -#endif // TRANSITION, P0608 +#endif // !__EDG__ +#endif // _HAS_CXX20 { struct X {}; struct Y { @@ -4629,7 +4644,8 @@ void test_T_ctor_basic() { static_assert(v.index() == 1, ""); static_assert(std::get<1>(v) == 42, ""); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS { constexpr std::variant v(42); @@ -4637,17 +4653,20 @@ void test_T_ctor_basic() { static_assert(std::get<1>(v) == 42, ""); } #endif +#endif // !__EDG__ { std::variant v = "meow"; assert(v.index() == 0); assert(std::get<0>(v) == "meow"); } +#ifndef TEST_PERMISSIVE { std::variant> v = nullptr; assert(v.index() == 1); assert(std::get<1>(v) == nullptr); } -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 { std::variant v = true; assert(v.index() == 0); @@ -4681,6 +4700,7 @@ void test_T_ctor_basic() { #endif } +#if !_HAS_CXX20 // Narrowing check occurs with P0608R3 struct BoomOnAnything { template constexpr BoomOnAnything(T) { static_assert(!std::is_same::value, ""); } @@ -4692,6 +4712,7 @@ void test_no_narrowing_check_for_class_types() { assert(v.index() == 0); assert(std::get<0>(v) == 42); } +#endif // Narrowing check occurs with P0608R3 struct Bar {}; struct Baz {}; @@ -4708,7 +4729,9 @@ int run_test() { test_T_ctor_basic(); test_T_ctor_noexcept(); test_T_ctor_sfinae(); +#if !_HAS_CXX20 // Narrowing check occurs with P0608R3 test_no_narrowing_check_for_class_types(); +#endif // Narrowing check occurs with P0608R3 test_construction_with_repeated_types(); return 0; } diff --git a/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst b/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0608R3_improved_variant_converting_constructor/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/P0608R3_improved_variant_converting_constructor/test.cpp b/tests/std/tests/P0608R3_improved_variant_converting_constructor/test.cpp new file mode 100644 index 00000000000..2d407f7d1e5 --- /dev/null +++ b/tests/std/tests/P0608R3_improved_variant_converting_constructor/test.cpp @@ -0,0 +1,263 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Also tests for P1957R2: Converting from T* to bool should be considered narrowing + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct double_double { + double_double(double x) : x_(x) {} + + double x_; +}; +struct convertible_bool { + convertible_bool(bool x) : x_(x) {} + ~convertible_bool() = default; + + operator bool() const noexcept { + return x_; + } + + bool x_; +}; +struct default_struct {}; + +void assert_P0608R3() { + // P0608R3 examples + static_assert(is_constructible_v, const char*>); + static_assert(is_constructible_v, string>); + static_assert(is_constructible_v>, char16_t>); + static_assert(is_constructible_v>, double&>); + static_assert(is_constructible_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(is_constructible_v, int>); + static_assert(is_constructible_v, int>); + static_assert(is_constructible_v, int>); + static_assert(is_constructible_v, long long>, int>); +#endif // !__EDG__ + static_assert(is_constructible_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(!is_constructible_v, int>); + static_assert(!is_constructible_v>, int>); +#endif // !__EDG__ + static_assert(!is_constructible_v, int>); + + static_assert(is_assignable_v, const char*>); + static_assert(is_assignable_v, string>); + static_assert(is_assignable_v>, char16_t>); + static_assert(is_assignable_v>, double&>); + static_assert(is_assignable_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(is_assignable_v, int>); + static_assert(is_assignable_v, int>); + static_assert(is_assignable_v, int>); + static_assert(is_assignable_v, long long>, int>); +#endif // !__EDG__ + static_assert(is_assignable_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(!is_assignable_v, int>); + static_assert(!is_assignable_v>, int>); +#endif // !__EDG__ + static_assert(!is_assignable_v, int>); +} + +void assert_P1957R2() { + // P1957R2 examples + static_assert(is_constructible_v, bool>); + static_assert(is_constructible_v, bitset<4>::reference>); + static_assert(is_constructible_v, bitset<4>::reference>); + + static_assert(is_assignable_v, bool>); + static_assert(is_assignable_v, bitset<4>::reference>); + static_assert(is_assignable_v, bitset<4>::reference>); +} + +void assert_more_examples() { + // More examples + static_assert(is_constructible_v, double>); + static_assert(is_constructible_v>, optional, int>, int>); + static_assert(is_constructible_v>, optional>, int>); + static_assert(is_constructible_v, optional, float>, int>); + static_assert(is_constructible_v, convertible_bool>); + static_assert(is_constructible_v, convertible_bool>); + static_assert(is_constructible_v, bool>); + static_assert(is_constructible_v, convertible_bool>); + static_assert(is_constructible_v, bool>); + static_assert(is_constructible_v, bool>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 +#ifdef __clang__ // TRANSITION, DevCom-1338628 + static_assert(is_constructible_v, int>); +#endif // __clang__ + static_assert(!is_constructible_v, unsigned int>); + static_assert(!is_constructible_v, int>); +#endif // !__EDG__ + static_assert(!is_constructible_v, int>); + + static_assert(is_assignable_v, double>); + static_assert(is_assignable_v>, optional, int>, int>); + static_assert(is_assignable_v>, optional>, int>); + static_assert(is_assignable_v, optional, float>, int>); + static_assert(is_assignable_v, convertible_bool>); + static_assert(is_assignable_v, convertible_bool>); + static_assert(is_assignable_v, bool>); + static_assert(is_assignable_v, convertible_bool>); + static_assert(is_assignable_v, bool>); + static_assert(is_assignable_v, bool>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 +#ifdef __clang__ // TRANSITION, DevCom-1338628 + static_assert(is_assignable_v, int>); +#endif // __clang__ + static_assert(!is_assignable_v, unsigned int>); + static_assert(!is_assignable_v, int>); +#endif // !__EDG__ + static_assert(!is_assignable_v, int>); +} + +void test_variant_constructor_P0608R3() { + // P0608R3 runtime checks + variant a = "abc"; // string + assert(a.index() == 0); + assert(get<0>(a) == "abc"); + + variant> b = u'\u2043'; // optional + assert(b.index() == 1); + assert(get>(b) == u'\u2043'); + + double c_data = 3.14; + variant> c = c_data; // reference_wrapper + assert(c.index() == 1); + assert(get<1>(c) == c_data); + + variant d; + assert(d.index() == 0); + d = 0; // int + assert(d.index() == 1); + + variant e; + assert(e.index() == 0); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + e = 0; // long + assert(e.index() == 1); +#endif // !__EDG__ + + variant f = 'a'; // int + assert(f.index() == 1); + assert(get(f) == 97); + +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + variant g = 0; // long + assert(g.index() == 1); + + variant h = 0; // long + assert(h.index() == 1); + + variant, long long> i = 0; // long long + assert(i.index() == 2); +#endif // !__EDG__ + + variant j = 'a'; // int + assert(j.index() == 1); + assert(get(j) == 97); +} + +void test_variant_constructor_P1957R2() { + bitset<4> a_bitset("0101"); + bool a_data = a_bitset[2]; + variant a = a_data; // bool + assert(a.index() == 0); + assert(get<0>(a)); + + bitset<4> b_bitset("0101"); + variant b = b_bitset[2]; // bool + variant b2 = b_bitset[1]; // bool + assert(b.index() == 0); + assert(get<0>(b)); + assert(b2.index() == 0); + assert(!get<0>(b2)); +} + +void test_variant_constructor_more_examples() { + variant> a = true; // bool + assert(a.index() == 3); + + variant b = convertible_bool{true}; // bool + assert(b.index() == 0); + assert(get<0>(b)); + + variant c = false; // bool + assert(c.index() == 2); + + variant d = convertible_bool{true}; // convertible_bool + assert(d.index() == 2); + + variant e = bool{}; // bool + assert(e.index() == 1); + assert(!get<1>(e)); + + variant f = convertible_bool{false}; // bool + assert(f.index() == 1); + assert(!get<1>(f)); + + variant g = true_type{}; // bool + assert(g.index() == 0); + assert(get<0>(g)); +} + +void test_assignment_operator() { + variant a; // string + assert(a.index() == 0); + assert(get(a) == ""); + a = 3; // int + assert(a.index() == 2); + assert(get(a) == 3); + a = true; // bool + assert(a.index() == 1); + assert(get(a) == true); + +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + bool b_data = true; + variant b = b_data; // bool + assert(b.index() == 0); + assert(get<0>(b) == b_data); + b = 12; // int + assert(b.index() == 1); + assert(get<1>(b) == 12); + b = 12.5; // double_double + assert(b.index() == 2); + assert(get<2>(b).x_ == 12.5); +#endif // !__EDG__ + +#ifdef __clang__ // TRANSITION, DevCom-1338628 + variant c; + assert(c.index() == 0); + c = false; // bool + assert(c.index() == 1); + assert(get<1>(c) == false); + c = 5.12; // double_double + assert(c.index() == 2); + assert(get<2>(c).x_ == 5.12); + double_double c_data{1.2}; + c = static_cast(&c_data); // void* + assert(c.index() == 0); + assert(static_cast(get<0>(c))->x_ == 1.2); +#endif // __clang__ +} + +int main() { + assert_P0608R3(); + assert_P1957R2(); + assert_more_examples(); + test_variant_constructor_P0608R3(); + test_variant_constructor_P1957R2(); + test_variant_constructor_more_examples(); + test_assignment_operator(); +} 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/P0896R4_ranges_alg_uninitialized_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp index f2c607162fe..38c0d687d54 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp @@ -76,21 +76,16 @@ struct holder { } }; -template -void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy - for (auto& e : r) { - destroy_at(&e); - } -} - struct instantiator { - static constexpr int expected_output[] = {13, 55, 12345}; - static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; template static void call() { - using ranges::uninitialized_copy, ranges::uninitialized_copy_result, ranges::equal, ranges::equal_to, - ranges::iterator_t; + using ranges::destroy, ranges::uninitialized_copy, ranges::uninitialized_copy_result, ranges::equal, + ranges::equal_to, ranges::iterator_t; { // Validate range overload int_wrapper input[3] = {13, 55, 12345}; @@ -107,7 +102,7 @@ struct instantiator { assert(result.out == wrapped_output.end()); assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); + destroy(wrapped_output); assert(int_wrapper::constructions == 3); assert(int_wrapper::destructions == 3); } @@ -127,10 +122,51 @@ struct instantiator { assert(result.out == wrapped_output.end()); assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); + destroy(wrapped_output); assert(int_wrapper::constructions == 3); assert(int_wrapper::destructions == 3); } + + { // Validate range overload shorter output + int_wrapper input[4] = {13, 55, 12345, 42}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_copy(wrapped_input, wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(++result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input_long, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + + { // Validate range overload shorter input + int_wrapper input[3] = {13, 55, 12345}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_copy(wrapped_input, wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + construct_at(addressof(*result.out), -1); // Need to construct non written element for comparison + assert(++result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output_long, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 4); + assert(int_wrapper::destructions == 4); + } } }; @@ -160,6 +196,48 @@ struct throwing_test { } }; +struct memcpy_test { + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; + + static void call() { + { // Validate only range overload + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = ranges::uninitialized_copy(input, output); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate input shorter + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1, -1}; + + auto result = ranges::uninitialized_copy(input, output); + assert(result.in == end(input)); + assert(++result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output_long)); + } + + { // Validate output shorter + int input[] = {13, 55, 12345, 42}; + int output[] = {-1, -1, -1}; + + auto result = ranges::uninitialized_copy(input, output); + assert(++result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input_long)); + assert(ranges::equal(output, expected_output)); + } + } +}; + template using test_input = test::range; @@ -174,4 +252,5 @@ int main() { instantiator::call, test_output>(); throwing_test::call, test_output>(); throwing_test::call, test_output>(); + memcpy_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp index 9e86c952d8d..3096d91b16a 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -66,39 +67,77 @@ struct holder { } }; -template -void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy - for (auto& e : r) { - destroy_at(&e); - } -} - struct instantiator { - static constexpr int expected_output[] = {13, 55, 12345}; - static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; template static void call() { - using ranges::uninitialized_copy_n, ranges::uninitialized_copy_n_result, ranges::equal, ranges::equal_to, - ranges::iterator_t; + using ranges::destroy, ranges::uninitialized_copy_n, ranges::uninitialized_copy_n_result, ranges::equal, + ranges::equal_to, ranges::iterator_t; + + { // Validate equal ranges + int_wrapper input[3] = {13, 55, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as, iterator_t>> auto result = + uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } - int_wrapper input[3] = {13, 55, 12345}; - Read wrapped_input{input}; - holder mem; - Write wrapped_output{mem.as_span()}; + { // Validate shorter output + int_wrapper input[4] = {13, 55, 12345, 42}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(++result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input_long, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } - int_wrapper::clear_counts(); - const same_as, iterator_t>> auto result = - uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 0); - assert(result.in == wrapped_input.end()); - assert(result.out == wrapped_output.end()); - assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); - assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 3); + { // Validate shorter input + int_wrapper input[3] = {13, 55, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + construct_at(addressof(*result.out), -1); // Need to construct non written element for comparison + assert(++result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output_long, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 4); + assert(int_wrapper::destructions == 4); + } } }; @@ -126,6 +165,53 @@ struct throwing_test { } }; +struct memcpy_test { + static constexpr int expected_output[] = {13, 55, 12345, -1}; + static constexpr int expected_output_long[] = {13, 55, -1, -1}; + static constexpr int expected_input[] = {13, 55, 12345, 42}; + static constexpr int expected_input_short[] = {13, 55}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; + + static void call() { + using ranges::uninitialized_copy_n, ranges::uninitialized_copy_n_result, ranges::equal, ranges::iterator_t; + { // Validate range overload + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_copy_n(input.begin(), 3, output.begin(), output.end()); + assert(next(result.in) == input.end()); + assert(next(result.out) == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_output)); + } + + { // Validate shorter input + vector input = {13, 55}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_copy_n(input.begin(), 2, output.begin(), output.end()); + assert(result.in == input.end()); + assert(next(result.out, 2) == output.end()); + assert(equal(input, expected_input_short)); + assert(equal(output, expected_output_long)); + } + + { // Validate shorter output + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_copy_n(input.begin(), 2, output.begin(), output.end()); + assert(next(result.in, 2) == input.end()); + assert(result.out == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_input_short)); + } + } +}; + template using test_input = test::range; @@ -140,4 +226,5 @@ int main() { instantiator::call, test_output>(); throwing_test::call, test_output>(); throwing_test::call, test_output>(); + memcpy_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp index 8be0b1e8de3..865dcffcccf 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp @@ -78,8 +78,10 @@ struct holder { }; struct instantiator { - static constexpr int expected_output[] = {13, 55, 12345}; - static constexpr int expected_input[] = {-1, -1, -1}; + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {-1, -1, -1}; + static constexpr int expected_input_long[] = {-1, -1, -1, 42}; template static void call() { @@ -125,6 +127,47 @@ struct instantiator { assert(int_wrapper::constructions == 3); assert(int_wrapper::destructions == 3); } + + { // Validate range overload shorter output + int_wrapper input[4] = {13, 55, 12345, 42}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_move(wrapped_input, wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(++result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input_long, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + + { // Validate range overload shorter input + int_wrapper input[3] = {13, 55, 12345}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_move(wrapped_input, wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + construct_at(addressof(*result.out), -1); // Need to construct non written element for comparison + assert(++result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output_long, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 4); + assert(int_wrapper::destructions == 4); + } } }; @@ -154,6 +197,46 @@ struct throwing_test { } }; +struct memcpy_test { + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; + + static void call() { + { // Validate range + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + ranges::uninitialized_move(input, output); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate input shorter + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1, -1}; + + auto result = ranges::uninitialized_move(input, output); + assert(result.in == end(input)); + assert(++result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output_long)); + } + + { // Validate output shorter + int input[] = {13, 55, 12345, 42}; + int output[] = {-1, -1, -1}; + + auto result = ranges::uninitialized_move(input, output); + assert(++result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input_long)); + assert(ranges::equal(output, expected_output)); + } + } +}; + template using test_input = test::range; @@ -168,4 +251,5 @@ int main() { instantiator::call, test_output>(); throwing_test::call, test_output>(); throwing_test::call, test_output>(); + memcpy_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp index eef82b952c4..60d6a79c732 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -67,39 +68,76 @@ struct holder { } }; -template -void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy - for (auto& e : r) { - destroy_at(&e); - } -} - struct instantiator { - static constexpr int expected_output[] = {13, 55, 12345}; - static constexpr int expected_input[] = {-1, -1, -1}; + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {-1, -1, -1}; + static constexpr int expected_input_long[] = {-1, -1, -1, 42}; template static void call() { - using ranges::uninitialized_move_n, ranges::uninitialized_move_n_result, ranges::equal, ranges::equal_to, - ranges::iterator_t; + using ranges::destroy, ranges::uninitialized_move_n, ranges::uninitialized_move_n_result, ranges::equal, + ranges::equal_to, ranges::iterator_t; + { // Validate matching ranges + int_wrapper input[3] = {13, 55, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as, iterator_t>> auto result = + uninitialized_move_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } - int_wrapper input[3] = {13, 55, 12345}; - Read wrapped_input{input}; - holder mem; - Write wrapped_output{mem.as_span()}; + { // Validate shorter output + int_wrapper input[4] = {13, 55, 12345, 42}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_move_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(++result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input_long, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } - int_wrapper::clear_counts(); - const same_as, iterator_t>> auto result = - uninitialized_move_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 0); - assert(result.in == wrapped_input.end()); - assert(result.out == wrapped_output.end()); - assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); - assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 3); + { // Validate shorter input + int_wrapper input[3] = {13, 55, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_move_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + construct_at(addressof(*result.out), -1); // Need to construct non written element for comparison + assert(++result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output_long, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 4); + assert(int_wrapper::destructions == 4); + } } }; @@ -128,6 +166,53 @@ struct throwing_test { } }; +struct memcpy_test { + static constexpr int expected_output[] = {13, 55, 12345, -1}; + static constexpr int expected_output_long[] = {13, 55, -1, -1}; + static constexpr int expected_input[] = {13, 55, 12345, 42}; + static constexpr int expected_input_short[] = {13, 55}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; + + static void call() { + using ranges::uninitialized_move_n, ranges::uninitialized_move_n_result, ranges::equal, ranges::iterator_t; + { // Validate range overload + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_move_n(input.begin(), 3, output.begin(), output.end()); + assert(next(result.in) == input.end()); + assert(next(result.out) == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_output)); + } + + { // Validate shorter input + vector input = {13, 55}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_move_n(input.begin(), 2, output.begin(), output.end()); + assert(result.in == input.end()); + assert(next(result.out, 2) == output.end()); + assert(equal(input, expected_input_short)); + assert(equal(output, expected_output_long)); + } + + { // Validate shorter output + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_move_n(input.begin(), 2, output.begin(), output.end()); + assert(next(result.in, 2) == input.end()); + assert(result.out == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_input_short)); + } + } +}; + template using test_input = test::range; @@ -142,4 +227,5 @@ int main() { instantiator::call, test_output>(); throwing_test::call, test_output>(); throwing_test::call, test_output>(); + memcpy_test::call(); } 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/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index dd19714b162..ed9ceceed4c 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +43,11 @@ template using SpaceshipType = decltype(std::declval() <=> std::declval()); +template +concept HasSpaceshipWith = requires { + std::declval() <=> std::declval(); +}; + using PartiallyOrdered = double; struct WeaklyOrdered { @@ -875,6 +883,45 @@ void ordering_test_cases() { assert(a1 != a4); assert(a1 != a5); } + { // chrono::duration + using std::chrono::hours; + using std::chrono::minutes; + using std::chrono::seconds; + + spaceship_test(seconds{1}, seconds{1}, seconds{2}); + spaceship_test(seconds{3600}, hours{1}, minutes{61}); + + using double_seconds = std::chrono::duration; + using float_milliseconds = std::chrono::duration; + using ntsc_fields = std::chrono::duration>; + + spaceship_test(double_seconds{1}, float_milliseconds{1000}, ntsc_fields{60}); + + constexpr double_seconds nan_s{std::numeric_limits::quiet_NaN()}; +#ifdef __clang__ // TRANSITION, DevCom-445462 + static_assert(nan_s <=> nan_s == std::partial_ordering::unordered); +#endif // defined(__clang__) + assert(nan_s <=> nan_s == std::partial_ordering::unordered); + } + { // chrono::time_point + using std::chrono::milliseconds; + using double_seconds = std::chrono::duration; + using sys_tp = std::chrono::system_clock::time_point; + using sys_ms = std::chrono::time_point; + using sys_double_s = std::chrono::time_point; + + spaceship_test(sys_tp{}, sys_ms{}, sys_ms{milliseconds{1}}); + spaceship_test(sys_tp{}, sys_double_s{}, sys_double_s{double_seconds{1}}); + + constexpr sys_double_s nan_tp{double_seconds{std::numeric_limits::quiet_NaN()}}; +#ifdef __clang__ // TRANSITION, DevCom-445462 + static_assert(nan_tp <=> nan_tp == std::partial_ordering::unordered); +#endif // defined(__clang__) + assert(nan_tp <=> nan_tp == std::partial_ordering::unordered); + + using steady_tp = std::chrono::steady_clock::time_point; + static_assert(!HasSpaceshipWith); + } { // filesystem::space_info constexpr std::filesystem::space_info si1{4'000'000'000'000, 2'000'000'000'000, 1'000'000'000'000}; constexpr std::filesystem::space_info si2{4'000'000'000'000, 2'000'000'000'000, 1'000'000'000'000}; 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" diff --git a/tools/inc/stljobs.h b/tools/inc/stljobs.h index a538a9e8456..9167cc60860 100644 --- a/tools/inc/stljobs.h +++ b/tools/inc/stljobs.h @@ -123,6 +123,16 @@ inline handle create_file(LPCWSTR lpFileName, DWORD return result; } +inline handle create_event( + LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName) { + handle result{CreateEventW(lpEventAttributes, bManualReset, bInitialState, lpName)}; + if (!result) { + api_failure("CreateEventW"); + } + + return result; +} + inline handle create_named_pipe(LPCWSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes) { @@ -305,6 +315,9 @@ struct output_collecting_pipe { writeHandle = create_file(pipeNameBuffer, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, &inheritSa, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, HANDLE{}); + readEvent = create_event(nullptr, TRUE, FALSE, nullptr); + overlapped.hEvent = readEvent.get(); + readIo = tp_io{std::move(readHandle), callback, this, nullptr}; start(); @@ -312,7 +325,7 @@ struct output_collecting_pipe { ~output_collecting_pipe() noexcept { if (readIo) { - if (running.load()) { + if (running.exchange(false)) { // prevent callback() from calling read_some() if (!CancelIoEx(readIo.get_file(), &overlapped)) { api_failure("CancelIoEx"); // slams into noexcept } @@ -413,6 +426,7 @@ struct output_collecting_pipe { std::string targetBuffer; // if running, owned by a threadpool thread, otherwise owned by the calling thread size_t validTill{}; handle writeHandle; + handle readEvent; tp_io readIo; OVERLAPPED overlapped{}; };