diff --git a/stl/inc/memory b/stl/inc/memory index 782a50c2d37..72724242fd6 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -56,6 +56,72 @@ namespace ranges { concept _No_throw_forward_range = _No_throw_input_range<_Rng> && _No_throw_forward_iterator>; // clang-format on + + // ALIAS TEMPLATE uninitialized_copy_result + template + using uninitialized_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::uninitialized_copy + class _Uninitialized_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, _No_throw_forward_iterator _Out, + _No_throw_sentinel_for<_Out> _OSe> + requires constructible_from, iter_reference_t<_It>> + uninitialized_copy_result<_It, _Out> operator()(_It _First1, _Se _Last1, _Out _First2, _OSe _Last2) const { + // clang-format on + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = + _Uninitialized_copy_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2))); + + _Seek_wrapped(_First1, _STD move(_UResult.in)); + _Seek_wrapped(_First2, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2)}; + } + + // clang-format off + template + requires constructible_from, range_reference_t<_Rng1>> + uninitialized_copy_result, borrowed_iterator_t<_Rng2>> operator()( + _Rng1&& _Range1, _Rng2&& _Range2) const { + // clang-format on + auto _First1 = _RANGES begin(_Range1); + auto _UResult = _Uninitialized_copy_unchecked( + _Get_unwrapped(_STD move(_First1)), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2)); + + _Seek_wrapped(_First1, _STD move(_UResult.in)); + return {_STD move(_First1), _Rewrap_iterator(_Range2, _STD move(_UResult.out))}; + } + + private: + template + _NODISCARD static uninitialized_copy_result<_It, _Out> _Uninitialized_copy_unchecked( + _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_Out>); + _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_OSe, _Out>); + _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_reference_t<_It>>); + + if constexpr (is_same_v<_Se, _It> && _Ptr_copy_cat<_It, _Out>::_Really_trivial) { + return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); + } else { + _Uninitialized_backout _Backout{_STD move(_OFirst)}; + + for (; _IFirst != _ILast && _Backout._Last != _OLast; ++_IFirst) { + _Backout._Emplace_back(*_IFirst); + } + + return {_STD move(_IFirst), _Backout._Release()}; + } + } + }; + + inline constexpr _Uninitialized_copy_fn uninitialized_copy{_Not_quite_object::_Construct_tag{}}; } // namespace ranges #endif // __cpp_lib_concepts @@ -65,23 +131,25 @@ template _NoThrowFwdIt uninitialized_copy_n(const _InIt _First, const _Diff _Count_raw, _NoThrowFwdIt _Dest) { // copy [_First, _First + _Count) to [_Dest, ...) _Algorithm_int_t<_Diff> _Count = _Count_raw; - if (0 < _Count) { - auto _UFirst = _Get_unwrapped_n(_First, _Count); - auto _UDest = _Get_unwrapped_n(_Dest, _Count); - if constexpr (_Ptr_copy_cat::_Really_trivial) { - _UDest = _Copy_memmove(_UFirst, _UFirst + _Count, _UDest); - } else { - _Uninitialized_backout _Backout{_UDest}; - for (; 0 < _Count; --_Count, (void) ++_UFirst) { - _Backout._Emplace_back(*_UFirst); - } + if (_Count <= 0) { + return _Dest; + } - _UDest = _Backout._Release(); + auto _UFirst = _Get_unwrapped_n(_First, _Count); + auto _UDest = _Get_unwrapped_n(_Dest, _Count); + if constexpr (_Ptr_copy_cat::_Really_trivial) { + _UDest = _Copy_memmove(_UFirst, _UFirst + _Count, _UDest); + } else { + _Uninitialized_backout _Backout{_UDest}; + + for (; _Count > 0; --_Count, (void) ++_UFirst) { + _Backout._Emplace_back(*_UFirst); } - _Seek_wrapped(_Dest, _UDest); + _UDest = _Backout._Release(); } + _Seek_wrapped(_Dest, _UDest); return _Dest; } #else // ^^^ _HAS_IF_CONSTEXPR / !_HAS_IF_CONSTEXPR vvv @@ -89,7 +157,8 @@ template _NoThrowFwdIt _Uninitialized_copy_n_unchecked2(_InIt _First, _Diff _Count, const _NoThrowFwdIt _Dest, false_type) { // copy [_First, _First + _Count) to [_Dest, ...), no special optimization _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; - for (; 0 < _Count; --_Count, (void) ++_First) { + + for (; _Count > 0; --_Count, (void) ++_First) { _Backout._Emplace_back(*_First); } @@ -106,17 +175,65 @@ template _NoThrowFwdIt uninitialized_copy_n(const _InIt _First, const _Diff _Count_raw, _NoThrowFwdIt _Dest) { // copy [_First, _First + _Count) to [_Dest, ...)] _Algorithm_int_t<_Diff> _Count = _Count_raw; - if (0 < _Count) { - auto _UFirst = _Get_unwrapped_n(_First, _Count); - auto _UDest = _Get_unwrapped_n(_Dest, _Count); - _Seek_wrapped(_Dest, _Uninitialized_copy_n_unchecked2(_UFirst, _Count, _UDest, - bool_constant<_Ptr_copy_cat::_Really_trivial>{})); + if (_Count <= 0) { + return _Dest; } + auto _UFirst = _Get_unwrapped_n(_First, _Count); + auto _UDest = _Get_unwrapped_n(_Dest, _Count); + _Seek_wrapped(_Dest, _Uninitialized_copy_n_unchecked2(_UFirst, _Count, _UDest, + bool_constant<_Ptr_copy_cat::_Really_trivial>{})); return _Dest; } #endif // _HAS_IF_CONSTEXPR +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE uninitialized_copy_n_result + template + using uninitialized_copy_n_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::uninitialized_copy_n + class _Uninitialized_copy_n_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _OSe> + requires constructible_from, iter_reference_t<_It>> + uninitialized_copy_n_result<_It, _Out> operator()( + _It _First1, iter_difference_t<_It> _Count, _Out _First2, _OSe _Last2) const { + // clang-format on + if (_Count <= 0) { + return {_STD move(_First1), _STD move(_First2)}; + } + + _Adl_verify_range(_First2, _Last2); + auto _IFirst = _Get_unwrapped_n(_STD move(_First1), _Count); + 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); + } else { + _Uninitialized_backout _Backout{_STD move(_OFirst)}; + + for (; _Count > 0 && _OFirst != _OLast; --_Count, (void) ++_IFirst) { + _Backout._Emplace_back(*_IFirst); + } + + _OFirst = _Backout._Release(); + } + + _Seek_wrapped(_First1, _IFirst); + _Seek_wrapped(_First2, _OFirst); + return {_STD move(_First1), _STD move(_First2)}; + } + }; + + inline constexpr _Uninitialized_copy_n_fn uninitialized_copy_n{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + #if _HAS_CXX17 // FUNCTION TEMPLATE uninitialized_move template @@ -142,10 +259,10 @@ namespace ranges { using _Not_quite_object::_Not_quite_object; // clang-format off - template _Se1, _No_throw_forward_iterator _It2, - _No_throw_sentinel_for<_It2> _Se2> - requires constructible_from, iter_rvalue_reference_t<_It1>> - uninitialized_move_result<_It1, _It2> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2) const { + template _Se, _No_throw_forward_iterator _Out, + _No_throw_sentinel_for<_Out> _OSe> + requires constructible_from, iter_rvalue_reference_t<_It>> + uninitialized_move_result<_It, _Out> operator()(_It _First1, _Se _Last1, _Out _First2, _OSe _Last2) const { // clang-format on _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); @@ -173,22 +290,26 @@ namespace ranges { } private: - template - _NODISCARD static uninitialized_move_result<_It1, _It2> _Uninitialized_move_unchecked( - _It1 _IFirst, const _Se1 _ILast, _It2 _OFirst, const _Se2 _OLast) { - _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); - _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_It2>); - _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se2, _It2>); - _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_rvalue_reference_t<_It1>>); - - _Uninitialized_backout _Backout{_STD move(_OFirst)}; - - for (; _IFirst != _ILast && _Backout._Last != _OLast; ++_IFirst) { - _Backout._Emplace_back(_RANGES iter_move(_IFirst)); - } + template + _NODISCARD static uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( + _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_Out>); + _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_OSe, _Out>); + _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_rvalue_reference_t<_It>>); + + if constexpr (is_same_v<_Se, _It> && _Ptr_move_cat<_It, _Out>::_Really_trivial) { + return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); + } else { + _Uninitialized_backout _Backout{_STD move(_OFirst)}; - return {_STD move(_IFirst), _Backout._Release()}; + for (; _IFirst != _ILast && _Backout._Last != _OLast; ++_IFirst) { + _Backout._Emplace_back(_RANGES iter_move(_IFirst)); + } + + return {_STD move(_IFirst), _Backout._Release()}; + } } }; @@ -201,52 +322,156 @@ template pair<_InIt, _NoThrowFwdIt> uninitialized_move_n(_InIt _First, const _Diff _Count_raw, _NoThrowFwdIt _Dest) { // move [_First, _First + _Count) to [_Dest, ...) _Algorithm_int_t<_Diff> _Count = _Count_raw; - if (0 < _Count) { - auto _UFirst = _Get_unwrapped_n(_First, _Count); - auto _UDest = _Get_unwrapped_n(_Dest, _Count); - if constexpr (_Ptr_move_cat::_Really_trivial) { - _UDest = _Copy_memmove(_UFirst, _UFirst + _Count, _UDest); - _UFirst += _Count; - } else { - _Uninitialized_backout _Backout{_UDest}; - for (; 0 < _Count; --_Count, (void) ++_UFirst) { - _Backout._Emplace_back(_STD move(*_UFirst)); - } + if (_Count <= 0) { + return {_First, _Dest}; + } - _UDest = _Backout._Release(); + auto _UFirst = _Get_unwrapped_n(_First, _Count); + auto _UDest = _Get_unwrapped_n(_Dest, _Count); + if constexpr (_Ptr_move_cat::_Really_trivial) { + _UDest = _Copy_memmove(_UFirst, _UFirst + _Count, _UDest); + _UFirst += _Count; + } else { + _Uninitialized_backout _Backout{_UDest}; + + for (; _Count > 0; --_Count, (void) ++_UFirst) { + _Backout._Emplace_back(_STD move(*_UFirst)); } - _Seek_wrapped(_Dest, _UDest); - _Seek_wrapped(_First, _UFirst); + _UDest = _Backout._Release(); } + _Seek_wrapped(_Dest, _UDest); + _Seek_wrapped(_First, _UFirst); return {_First, _Dest}; } #endif // _HAS_CXX17 + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE uninitialized_move_n_result + template + using uninitialized_move_n_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::uninitialized_move_n + class _Uninitialized_move_n_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _OSe> + requires constructible_from, iter_rvalue_reference_t<_It>> + uninitialized_move_n_result<_It, _Out> operator()( + _It _First1, iter_difference_t<_It> _Count, _Out _First2, _OSe _Last2) const { + // clang-format on + if (_Count <= 0) { + return {_STD move(_First1), _STD move(_First2)}; + } + + _Adl_verify_range(_First2, _Last2); + auto _IFirst = _Get_unwrapped_n(_STD move(_First1), _Count); + 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); + } else { + _Uninitialized_backout _Backout{_STD move(_OFirst)}; + + for (; _Count > 0 && _Backout._Last != _OLast; --_Count, (void) ++_IFirst) { + _Backout._Emplace_back(_RANGES iter_move(_IFirst)); + } + + _OFirst = _Backout._Release(); + } + + _Seek_wrapped(_First1, _IFirst); + _Seek_wrapped(_First2, _OFirst); + return {_STD move(_First1), _STD move(_First2)}; + } + }; + + inline constexpr _Uninitialized_move_n_fn uninitialized_move_n{_Not_quite_object::_Construct_tag{}}; + + // VARIABLE ranges::uninitialized_fill + class _Uninitialized_fill_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template <_No_throw_forward_iterator _It, _No_throw_sentinel_for<_It> _Se, class _Ty> + requires constructible_from, const _Ty&> + _It operator()(_It _First, _Se _Last, const _Ty& _Val) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UResult = _Uninitialized_fill_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Val); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + // clang-format off + template <_No_throw_forward_range _Rng, class _Ty> + requires constructible_from, const _Ty&> + borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, const _Ty& _Val) const { + // clang-format on + return _Rewrap_iterator(_Range, _Uninitialized_fill_unchecked(_Ubegin(_Range), _Uend(_Range), _Val)); + } + + private: + template + _NODISCARD static _It _Uninitialized_fill_unchecked(_It _OFirst, const _Se _OLast, const _Ty& _Val) { + _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(constructible_from, const _Ty&>); + + if constexpr (_Fill_memset_is_safe<_It, _Ty>) { + const auto _OFinal = _RANGES next(_OFirst, _STD move(_OLast)); + const auto _Diff = static_cast(_OFinal - _OFirst); + _Fill_memset(_OFirst, _Val, _Diff); + return _OFinal; + } else { + _Uninitialized_backout _Backout{_STD move(_OFirst)}; + + while (_Backout._Last != _OLast) { + _Backout._Emplace_back(_Val); + } + + return _Backout._Release(); + } + } + }; + + inline constexpr _Uninitialized_fill_fn uninitialized_fill{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE uninitialized_fill_n #if _HAS_IF_CONSTEXPR template _NoThrowFwdIt uninitialized_fill_n(_NoThrowFwdIt _First, const _Diff _Count_raw, const _Tval& _Val) { // copy _Count copies of _Val to raw _First _Algorithm_int_t<_Diff> _Count = _Count_raw; - if (0 < _Count) { - auto _UFirst = _Get_unwrapped_n(_First, _Count); - if constexpr (_Fill_memset_is_safe<_Unwrapped_n_t, _Tval>) { - _Fill_memset(_UFirst, _Val, _Count); - _UFirst += _Count; - } else { - _Uninitialized_backout<_Unwrapped_n_t> _Backout{_UFirst}; - for (; 0 < _Count; --_Count) { - _Backout._Emplace_back(_Val); - } + if (_Count <= 0) { + return _First; + } + + auto _UFirst = _Get_unwrapped_n(_First, _Count); + if constexpr (_Fill_memset_is_safe<_Unwrapped_n_t, _Tval>) { + _Fill_memset(_UFirst, _Val, static_cast(_Count)); + _UFirst += _Count; + } else { + _Uninitialized_backout<_Unwrapped_n_t> _Backout{_UFirst}; - _UFirst = _Backout._Release(); + for (; _Count > 0; --_Count) { + _Backout._Emplace_back(_Val); } - _Seek_wrapped(_First, _UFirst); + _UFirst = _Backout._Release(); } + _Seek_wrapped(_First, _UFirst); return _First; } #else // ^^^ _HAS_IF_CONSTEXPR // !_HAS_IF_CONSTEXPR vvv @@ -255,7 +480,7 @@ _NoThrowFwdIt _Uninitialized_fill_n_unchecked1( const _NoThrowFwdIt _First, _Diff _Count, const _Tval& _Val, false_type) { // copy _Count copies of _Val to raw _First, no special optimization _Uninitialized_backout<_NoThrowFwdIt> _Backout{_First}; - for (; 0 < _Count; --_Count) { + for (; _Count > 0; --_Count) { _Backout._Emplace_back(_Val); } @@ -274,16 +499,54 @@ template _NoThrowFwdIt uninitialized_fill_n(_NoThrowFwdIt _First, const _Diff _Count_raw, const _Tval& _Val) { // copy _Count copies of _Val to raw _First _Algorithm_int_t<_Diff> _Count = _Count_raw; - if (0 < _Count) { - auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); - _Seek_wrapped(_First, _Uninitialized_fill_n_unchecked1(_STD move(_UFirst), _Count, _Val, - bool_constant<_Fill_memset_is_safe<_Unwrapped_t<_NoThrowFwdIt>, _Tval>>{})); + if (_Count <= 0) { + return _First; } + auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); + _Seek_wrapped(_First, _Uninitialized_fill_n_unchecked1(_STD move(_UFirst), _Count, _Val, + bool_constant<_Fill_memset_is_safe<_Unwrapped_t<_NoThrowFwdIt>, _Tval>>{})); return _First; } #endif // _HAS_IF_CONSTEXPR +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::uninitialized_fill_n + class _Uninitialized_fill_n_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template <_No_throw_forward_iterator _It, class _Ty> + requires constructible_from, const _Ty&> + _It operator()(_It _First, iter_difference_t<_It> _Count, const _Ty& _Val) const { + // clang-format on + if (_Count <= 0) { + return _First; + } + + auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); + if constexpr (_Fill_memset_is_safe<_It, _Ty>) { + _Fill_memset(_UFirst, _Val, static_cast(_Count)); + _Seek_wrapped(_First, _UFirst + _Count); + } else { + _Uninitialized_backout _Backout{_STD move(_UFirst)}; + + for (; _Count > 0; --_Count) { + _Backout._Emplace_back(_Val); + } + + _Seek_wrapped(_First, _Backout._Release()); + } + return _First; + } + }; + + inline constexpr _Uninitialized_fill_n_fn uninitialized_fill_n{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE construct_at #if _HAS_CXX20 template @@ -419,21 +682,20 @@ template _NoThrowFwdIt destroy_n(_NoThrowFwdIt _First, const _Diff _Count_raw) { // destroy all elements in [_First, _First + _Count) _Algorithm_int_t<_Diff> _Count = _Count_raw; - if (0 < _Count) { - auto _UFirst = _Get_unwrapped_n(_First, _Count); - if constexpr (is_trivially_destructible_v<_Iter_value_t<_NoThrowFwdIt>>) { - _STD advance(_UFirst, _Count); - } else { - do { - _Destroy_in_place(*_UFirst); - ++_UFirst; - --_Count; - } while (0 < _Count); - } + if (_Count <= 0) { + return _First; + } - _Seek_wrapped(_First, _UFirst); + auto _UFirst = _Get_unwrapped_n(_First, _Count); + if constexpr (is_trivially_destructible_v<_Iter_value_t<_NoThrowFwdIt>>) { + _STD advance(_UFirst, _Count); + } else { + for (; _Count > 0; --_Count, (void) ++_UFirst) { + _Destroy_in_place(*_UFirst); + } } + _Seek_wrapped(_First, _UFirst); return _First; } @@ -474,44 +736,154 @@ namespace ranges { #endif // __cpp_lib_concepts // FUNCTION TEMPLATE uninitialized_default_construct +template +_NODISCARD void* _Voidify_iter(_Iter _It) noexcept { + return const_cast(static_cast(_STD addressof(*_It))); +} + template void uninitialized_default_construct(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) { // default-initialize all elements in [_First, _Last) - using _Ty = _Iter_value_t<_NoThrowFwdIt>; + using _Ty = remove_reference_t<_Iter_ref_t<_NoThrowFwdIt>>; _Adl_verify_range(_First, _Last); if constexpr (!is_trivially_default_constructible_v<_Ty>) { - const auto _ULast = _Get_unwrapped(_Last); _Uninitialized_backout _Backout{_Get_unwrapped(_First)}; - for (; _Backout._Last != _ULast; ++_Backout._Last) { - ::new (static_cast(_Unfancy(_Backout._Last))) _Ty; + + for (const auto _ULast = _Get_unwrapped(_Last); _Backout._Last != _ULast; ++_Backout._Last) { + ::new (_Voidify_iter(_Backout._Last)) _Ty; } _Backout._Release(); } } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::uninitialized_default_construct + class _Uninitialized_default_construct_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template <_No_throw_forward_iterator _It, _No_throw_sentinel_for<_It> _Se> + requires default_initializable> + _It operator()(_It _First, _Se _Last) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UResult = _Uninitialized_default_construct_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last))); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + // clang-format off + template <_No_throw_forward_range _Rng> + requires default_initializable> + borrowed_iterator_t<_Rng> operator()(_Rng&& _Range) const { + // clang-format on + auto _UResult = _Uninitialized_default_construct_unchecked(_Ubegin(_Range), _Uend(_Range)); + + return _Rewrap_iterator(_Range, _STD move(_UResult)); + } + + private: + template + _NODISCARD static _It _Uninitialized_default_construct_unchecked(_It _OFirst, const _Se _OLast) { + _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(default_initializable>); + + using _Ty = remove_reference_t>; + if constexpr (is_trivially_default_constructible_v<_Ty>) { + _RANGES advance(_OFirst, _OLast); + return _OFirst; + } else { + _Uninitialized_backout _Backout{_STD move(_OFirst)}; + + for (; _Backout._Last != _OLast; ++_Backout._Last) { + ::new (_Voidify_iter(_Backout._Last)) _Ty; + } + + return _Backout._Release(); + } + } + }; + + inline constexpr _Uninitialized_default_construct_fn uninitialized_default_construct{ + _Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE uninitialized_default_construct_n template _NoThrowFwdIt uninitialized_default_construct_n(_NoThrowFwdIt _First, const _Diff _Count_raw) { // default-initialize all elements in [_First, _First + _Count_raw) using _Ty = _Iter_value_t<_NoThrowFwdIt>; _Algorithm_int_t<_Diff> _Count = _Count_raw; - if (0 < _Count) { - if constexpr (is_trivially_default_constructible_v<_Ty>) { - _STD advance(_First, _Count); - } else { - _Uninitialized_backout _Backout{_Get_unwrapped_n(_First, _Count)}; - for (; 0 < _Count; ++_Backout._Last, (void) --_Count) { - ::new (static_cast(_Unfancy(_Backout._Last))) _Ty; - } + if (_Count <= 0) { + return _First; + } + + if constexpr (is_trivially_default_constructible_v<_Ty>) { + _STD advance(_First, _Count); + } else { + _Uninitialized_backout _Backout{_Get_unwrapped_n(_First, _Count)}; - _Seek_wrapped(_First, _Backout._Release()); + for (; _Count > 0; ++_Backout._Last, (void) --_Count) { + ::new (_Voidify_iter(_Backout._Last)) _Ty; } - } + _Seek_wrapped(_First, _Backout._Release()); + } return _First; } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::uninitialized_default_construct_n + class _Uninitialized_default_construct_n_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template <_No_throw_forward_iterator _It> + requires default_initializable> + _It operator()(_It _First, iter_difference_t<_It> _Count) const { + // clang-format on + if (_Count <= 0) { + return _First; + } + + using _Ty = remove_reference_t>; + if constexpr (is_trivially_default_constructible_v<_Ty>) { + _RANGES advance(_First, _Count); + } else if constexpr (is_nothrow_default_constructible_v<_Ty>) { + auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); + + for (; _Count > 0; --_Count, (void) ++_UFirst) { + ::new (_Voidify_iter(_UFirst)) _Ty; + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + } else { + _Uninitialized_backout _Backout{_Get_unwrapped_n(_STD move(_First), _Count)}; + + for (; _Count > 0; --_Count, (void) ++_Backout._Last) { + ::new (_Voidify_iter(_Backout._Last)) _Ty; + } + + _Seek_wrapped(_First, _Backout._Release()); + } + return _First; + } + }; + + inline constexpr _Uninitialized_default_construct_n_fn uninitialized_default_construct_n{ + _Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE uninitialized_value_construct template void uninitialized_value_construct(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) { @@ -523,6 +895,7 @@ void uninitialized_value_construct(const _NoThrowFwdIt _First, const _NoThrowFwd _Zero_range(_UFirst, _ULast); } else { _Uninitialized_backout _Backout{_UFirst}; + while (_Backout._Last != _ULast) { _Backout._Emplace_back(); } @@ -531,18 +904,115 @@ void uninitialized_value_construct(const _NoThrowFwdIt _First, const _NoThrowFwd } } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::uninitialized_value_construct + class _Uninitialized_value_construct_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template <_No_throw_forward_iterator _It, _No_throw_sentinel_for<_It> _Se> + requires default_initializable> + _It operator()(_It _First, _Se _Last) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UResult = _Uninitialized_value_construct_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last))); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + // clang-format off + template <_No_throw_forward_range _Rng> + requires default_initializable> + borrowed_iterator_t<_Rng> operator()(_Rng&& _Range) const { + // clang-format on + auto _UResult = _Uninitialized_value_construct_unchecked(_Ubegin(_Range), _Uend(_Range)); + + return _Rewrap_iterator(_Range, _STD move(_UResult)); + } + + private: + template + _NODISCARD static _It _Uninitialized_value_construct_unchecked(_It _OFirst, const _Se _OLast) { + _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(default_initializable>); + + if constexpr (_Use_memset_value_construct_v<_It>) { + const auto _OFinal = _RANGES next(_OFirst, _STD move(_OLast)); + const auto _Count = static_cast(_OFinal - _OFirst); + _CSTD memset(_OFirst, 0, _Count); + return _OFinal; + } else { + _Uninitialized_backout _Backout{_STD move(_OFirst)}; + + while (_Backout._Last != _OLast) { + _Backout._Emplace_back(); + } + + return _Backout._Release(); + } + } + }; + + inline constexpr _Uninitialized_value_construct_fn uninitialized_value_construct{ + _Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE uninitialized_value_construct_n template _NoThrowFwdIt uninitialized_value_construct_n(_NoThrowFwdIt _First, const _Diff _Count_raw) { // value-initialize all elements in [_First, _First + _Count_raw) _Algorithm_int_t<_Diff> _Count = _Count_raw; - if (0 < _Count) { - _Seek_wrapped(_First, _Uninitialized_value_construct_n_unchecked1(_Get_unwrapped_n(_First, _Count), _Count)); + if (_Count <= 0) { + return _First; } + _Seek_wrapped(_First, _Uninitialized_value_construct_n_unchecked1(_Get_unwrapped_n(_First, _Count), _Count)); return _First; } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::uninitialized_value_construct_n + class _Uninitialized_value_construct_n_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template <_No_throw_forward_iterator _It> + requires default_initializable> + _It operator()(_It _First, iter_difference_t<_It> _Count) const { + // clang-format on + if (_Count <= 0) { + return _First; + } + + auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); + if constexpr (_Use_memset_value_construct_v<_It>) { + _CSTD memset(_UFirst, 0, static_cast(_Count)); + _Seek_wrapped(_First, _UFirst + _Count); + } else { + _Uninitialized_backout _Backout{_STD move(_UFirst)}; + + for (; _Count > 0; --_Count) { + _Backout._Emplace_back(); + } + + _Seek_wrapped(_First, _Backout._Release()); + } + return _First; + } + }; + + inline constexpr _Uninitialized_value_construct_n_fn uninitialized_value_construct_n{ + _Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 @@ -2498,7 +2968,7 @@ _NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared _Alblock _Rebound(_Al); _Alloc_construct_ptr _Constructor{_Rebound}; _Constructor._Allocate(); - ::new (static_cast(_Unfancy(_Constructor._Ptr))) _Refc(_Al); + ::new (_Voidify_iter(_Constructor._Ptr)) _Refc(_Al); shared_ptr<_Ty> _Ret; const auto _Ptr = static_cast*>(_Constructor._Ptr->_Storage._Value); _Ret._Set_ptr_rep_and_enable_shared(_Ptr, _Unfancy(_Constructor._Release())); @@ -2514,7 +2984,7 @@ _NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared _Alblock _Rebound(_Al); _Alloc_construct_ptr _Constructor{_Rebound}; _Constructor._Allocate(); - ::new (static_cast(_Unfancy(_Constructor._Ptr))) _Refc(_Al, _Val); + ::new (_Voidify_iter(_Constructor._Ptr)) _Refc(_Al, _Val); shared_ptr<_Ty> _Ret; const auto _Ptr = static_cast*>(_Constructor._Ptr->_Storage._Value); _Ret._Set_ptr_rep_and_enable_shared(_Ptr, _Unfancy(_Constructor._Release())); diff --git a/stl/inc/xutility b/stl/inc/xutility index 9658711f8ac..0a402f4d210 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4378,6 +4378,17 @@ _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); +} + #if _HAS_IF_CONSTEXPR // VARIABLE TEMPLATE _Is_vb_iterator template diff --git a/tests/std/test.lst b/tests/std/test.lst index 2585db8b27f..58427f3711b 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -311,7 +311,16 @@ tests\P0896R4_ranges_alg_sort tests\P0896R4_ranges_alg_swap_ranges tests\P0896R4_ranges_alg_transform_binary tests\P0896R4_ranges_alg_transform_unary +tests\P0896R4_ranges_alg_uninitialized_copy +tests\P0896R4_ranges_alg_uninitialized_copy_n +tests\P0896R4_ranges_alg_uninitialized_default_construct +tests\P0896R4_ranges_alg_uninitialized_default_construct_n +tests\P0896R4_ranges_alg_uninitialized_fill +tests\P0896R4_ranges_alg_uninitialized_fill_n tests\P0896R4_ranges_alg_uninitialized_move +tests\P0896R4_ranges_alg_uninitialized_move_n +tests\P0896R4_ranges_alg_uninitialized_value_construct +tests\P0896R4_ranges_alg_uninitialized_value_construct_n tests\P0896R4_ranges_alg_unique tests\P0896R4_ranges_alg_unique_copy tests\P0896R4_ranges_algorithm_machinery diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp new file mode 100644 index 00000000000..f2c607162fe --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp @@ -0,0 +1,177 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +// Validate that uninitialized_copy_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::uninitialized_copy_result>); +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::uninitialized_copy_result>); +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::uninitialized_copy_result>); +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::uninitialized_copy_result>); + +struct int_wrapper { + inline static int constructions = 0; + inline static int destructions = 0; + + static void clear_counts() { + constructions = 0; + destructions = 0; + } + + static constexpr int magic_throwing_val = 29; + int val = 10; + + int_wrapper() { + ++constructions; + } + int_wrapper(int x) : val{x} { + ++constructions; + } + + int_wrapper(const int_wrapper& that) { + if (that.val == magic_throwing_val) { + throw magic_throwing_val; + } + + val = that.val; + ++constructions; + } + + ~int_wrapper() { + ++destructions; + } + + int_wrapper& operator=(const int_wrapper&) { + abort(); + } + + auto operator<=>(const int_wrapper&) const = default; +}; + +template +struct holder { + STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); + alignas(T) unsigned char space[N * sizeof(T)]; + + auto as_span() { + return span{reinterpret_cast(space + 0), N}; + } +}; + +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}; + + template + static void call() { + using 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}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + const 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, equal_to{}, &int_wrapper::val)); + not_ranges_destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + + { // Validate iterator overload + int_wrapper input[3] = {13, 55, 12345}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as, iterator_t>> auto result = uninitialized_copy( + wrapped_input.begin(), wrapped_input.end(), 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); + } + } +}; + +struct throwing_test { + static constexpr int expected_input[] = {13, 55, int_wrapper::magic_throwing_val, 12345}; + + template + static void call() { + // Validate only range overload (one is plenty since they both use the same backend) + int_wrapper input[] = {13, 55, int_wrapper::magic_throwing_val, 12345}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + try { + (void) ranges::uninitialized_copy(wrapped_input, wrapped_output); + assert(false); + } catch (int i) { + assert(i == int_wrapper::magic_throwing_val); + } catch (...) { + assert(false); + } + assert(int_wrapper::constructions == 2); + assert(int_wrapper::destructions == 2); + assert(ranges::equal(input, expected_input, ranges::equal_to{}, &int_wrapper::val)); + } +}; + +template +using test_input = test::range; +using test_output = test::range; + +int main() { + // The algorithm is oblivious to non-required category, size, difference, and "proxyness" of the input range. It + // _is_ sensitive to proxyness in that it requires non-proxy references for the output range. + + instantiator::call, test_output>(); + instantiator::call, test_output>(); + throwing_test::call, test_output>(); + throwing_test::call, test_output>(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst 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 new file mode 100644 index 00000000000..9e86c952d8d --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +// Validate that uninitialized_copy_n_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +struct int_wrapper { + inline static int constructions = 0; + inline static int destructions = 0; + + static void clear_counts() { + constructions = 0; + destructions = 0; + } + + static constexpr int magic_throwing_val = 29; + int val = 10; + + int_wrapper() { + ++constructions; + } + int_wrapper(int x) : val{x} { + ++constructions; + } + + int_wrapper(const int_wrapper& that) { + if (that.val == magic_throwing_val) { + throw magic_throwing_val; + } + + val = that.val; + ++constructions; + } + + ~int_wrapper() { + ++destructions; + } + + int_wrapper& operator=(const int_wrapper&) { + abort(); + } + + auto operator<=>(const int_wrapper&) const = default; +}; + +template +struct holder { + STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); + alignas(T) unsigned char space[N * sizeof(T)]; + + auto as_span() { + return span{reinterpret_cast(space + 0), N}; + } +}; + +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}; + + template + static void call() { + using ranges::uninitialized_copy_n, ranges::uninitialized_copy_n_result, ranges::equal, ranges::equal_to, + ranges::iterator_t; + + 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)); + not_ranges_destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } +}; + +struct throwing_test { + static constexpr int expected_input[] = {13, 55, int_wrapper::magic_throwing_val, 12345}; + + template + static void call() { + int_wrapper input[] = {13, 55, int_wrapper::magic_throwing_val, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + try { + (void) ranges::uninitialized_copy_n(wrapped_input.begin(), 4, wrapped_output.begin(), wrapped_output.end()); + assert(false); + } catch (int i) { + assert(i == int_wrapper::magic_throwing_val); + } catch (...) { + assert(false); + } + assert(int_wrapper::constructions == 2); + assert(int_wrapper::destructions == 2); + } +}; + +template +using test_input = test::range; +using test_output = test::range; + +int main() { + // The algorithm is oblivious to non-required category, size, difference, and "proxyness" of the input range. It + // _is_ sensitive to proxyness in that it requires non-proxy references for the output range. + + instantiator::call, test_output>(); + instantiator::call, test_output>(); + throwing_test::call, test_output>(); + throwing_test::call, test_output>(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct/test.cpp new file mode 100644 index 00000000000..2f99b0aef75 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct/test.cpp @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +// Validate dangling story +STATIC_ASSERT(same_as{})), int*>); +STATIC_ASSERT(same_as{})), ranges::dangling>); + +struct int_wrapper { + inline static int constructions = 0; + inline static int destructions = 0; + + static void clear_counts() { + constructions = 0; + destructions = 0; + } + + static constexpr int magic_throwing_val = 4; + int val; + + int_wrapper() { + if (++constructions == magic_throwing_val) { + throw magic_throwing_val; + } + } + + ~int_wrapper() { + ++destructions; + } + + auto operator<=>(const int_wrapper&) const = default; +}; +STATIC_ASSERT(default_initializable); + +template +struct holder { + STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); + alignas(T) unsigned char space[N * sizeof(T)]; + + auto as_span() { + return span{reinterpret_cast(space + 0), N}; + } +}; + +template +void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy + for (auto& e : r) { + destroy_at(&e); + } +} + +struct instantiator { + template + static void call() { + using ranges::uninitialized_default_construct, ranges::equal, ranges::equal_to, ranges::iterator_t; + + { // Validate range overload + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = uninitialized_default_construct(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + + { // Validate iterator overload + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = + uninitialized_default_construct(wrapped_input.begin(), wrapped_input.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + } +}; + +struct throwing_test { + template + static void call() { + // Validate only range overload (one is plenty since they both use the same backend) + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + try { + (void) ranges::uninitialized_default_construct(wrapped_input); + assert(false); + } catch (int i) { + assert(i == int_wrapper::magic_throwing_val); + } catch (...) { + assert(false); + } + assert(int_wrapper::constructions == int_wrapper::magic_throwing_val); + assert(int_wrapper::destructions == int_wrapper::magic_throwing_val - 1); + } +}; + +using test_range = test::range; + +int main() { + // The algorithm is oblivious to non-required category, size, difference. It _is_ sensitive to proxyness in that it + // requires non-proxy references for the input range. + + instantiator::call(); + throwing_test::call(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct_n/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct_n/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct_n/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct_n/test.cpp new file mode 100644 index 00000000000..d28eefede3c --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct_n/test.cpp @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +struct int_wrapper { + inline static int constructions = 0; + inline static int destructions = 0; + + static void clear_counts() { + constructions = 0; + destructions = 0; + } + + static constexpr int magic_throwing_val = 4; + int val; + + int_wrapper() { + if (++constructions == magic_throwing_val) { + throw magic_throwing_val; + } + } + + ~int_wrapper() { + ++destructions; + } + + auto operator<=>(const int_wrapper&) const = default; +}; +STATIC_ASSERT(default_initializable); + +template +struct holder { + STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); + alignas(T) unsigned char space[N * sizeof(T)]; + + auto as_span() { + return span{reinterpret_cast(space + 0), N}; + } +}; + +template +void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy + for (auto& e : r) { + destroy_at(&e); + } +} + +struct instantiator { + template + static void call() { + using ranges::uninitialized_default_construct_n, ranges::equal, ranges::equal_to, ranges::iterator_t; + + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = uninitialized_default_construct_n(wrapped_input.begin(), 3); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } +}; + +struct throwing_test { + template + static void call() { + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + try { + (void) ranges::uninitialized_default_construct_n(wrapped_input.begin(), int_wrapper::magic_throwing_val); + assert(false); + } catch (int i) { + assert(i == int_wrapper::magic_throwing_val); + } catch (...) { + assert(false); + } + assert(int_wrapper::constructions == int_wrapper::magic_throwing_val); + assert(int_wrapper::destructions == int_wrapper::magic_throwing_val - 1); + } +}; + +using test_range = test::range; + +int main() { + // The algorithm is oblivious to non-required category, size, difference. It _is_ sensitive to proxyness in that it + // requires non-proxy references for the input range. + + instantiator::call(); + throwing_test::call(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp new file mode 100644 index 00000000000..1defdd193d2 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +// Validate dangling story +STATIC_ASSERT(same_as{}, 42)), int*>); +STATIC_ASSERT(same_as{}, 42)), ranges::dangling>); + +struct int_wrapper { + inline static int constructions = 0; + inline static int destructions = 0; + + static void clear_counts() { + constructions = 0; + destructions = 0; + } + + static constexpr int magic_throwing_val = 4; + int val; + + int_wrapper() = default; + + int_wrapper(const int v) { + if (++constructions == magic_throwing_val) { + throw magic_throwing_val; + } + val = v; + } + + ~int_wrapper() { + ++destructions; + } + + auto operator<=>(const int_wrapper&) const = default; +}; +STATIC_ASSERT(default_initializable); + +template +struct holder { + STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); + alignas(T) unsigned char space[N * sizeof(T)]; + + auto as_span() { + return span{reinterpret_cast(space + 0), N}; + } +}; + +template +void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy + for (auto& e : r) { + destroy_at(&e); + } +} + +struct instantiator { + static constexpr int expected[3] = {42, 42, 42}; + + template + static void call() { + using ranges::uninitialized_fill, ranges::equal, ranges::equal_to, ranges::iterator_t; + + { // Validate range overload + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = uninitialized_fill(wrapped_input, 42); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + assert(equal(wrapped_input, expected, equal_to{}, &int_wrapper::val)); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + + { // Validate iterator overload + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = + uninitialized_fill(wrapped_input.begin(), wrapped_input.end(), 42); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + assert(equal(wrapped_input, expected, equal_to{}, &int_wrapper::val)); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + } +}; + +struct throwing_test { + template + static void call() { + // Validate only range overload (one is plenty since they both use the same backend) + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + try { + (void) ranges::uninitialized_fill(wrapped_input, 42); + assert(false); + } catch (int i) { + assert(i == int_wrapper::magic_throwing_val); + } catch (...) { + assert(false); + } + assert(int_wrapper::constructions == int_wrapper::magic_throwing_val); + assert(int_wrapper::destructions == int_wrapper::magic_throwing_val - 1); + } +}; + +using test_range = test::range; + +int main() { + // The algorithm is oblivious to non-required category, size, difference. It _is_ sensitive to proxyness in that it + // requires non-proxy references for the input range. + + instantiator::call(); + throwing_test::call(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp new file mode 100644 index 00000000000..97da0b88fd1 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +struct int_wrapper { + inline static int constructions = 0; + inline static int destructions = 0; + + static void clear_counts() { + constructions = 0; + destructions = 0; + } + + static constexpr int magic_throwing_val = 4; + int val; + + int_wrapper() = default; + + int_wrapper(const int v) { + if (++constructions == magic_throwing_val) { + throw magic_throwing_val; + } + val = v; + } + + ~int_wrapper() { + ++destructions; + } + + auto operator<=>(const int_wrapper&) const = default; +}; +STATIC_ASSERT(default_initializable); + +template +struct holder { + STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); + alignas(T) unsigned char space[N * sizeof(T)]; + + auto as_span() { + return span{reinterpret_cast(space + 0), N}; + } +}; + +template +void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy + for (auto& e : r) { + destroy_at(&e); + } +} + +struct instantiator { + static constexpr int expected[3] = {42, 42, 42}; + + template + static void call() { + using ranges::uninitialized_fill_n, ranges::equal, ranges::equal_to, ranges::iterator_t; + + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = uninitialized_fill_n(wrapped_input.begin(), 3, 42); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + assert(equal(wrapped_input, expected, equal_to{}, &int_wrapper::val)); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } +}; + +struct throwing_test { + template + static void call() { + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + try { + (void) ranges::uninitialized_fill_n(wrapped_input.begin(), int_wrapper::magic_throwing_val, 42); + assert(false); + } catch (int i) { + assert(i == int_wrapper::magic_throwing_val); + } catch (...) { + assert(false); + } + assert(int_wrapper::constructions == int_wrapper::magic_throwing_val); + assert(int_wrapper::destructions == int_wrapper::magic_throwing_val - 1); + } +}; + +using test_range = test::range; + +int main() { + // The algorithm is oblivious to non-required category, size, difference. It _is_ sensitive to proxyness in that it + // requires non-proxy references for the input range. + + instantiator::call(); + throwing_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 10bd044bbbf..8be0b1e8de3 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -58,12 +59,8 @@ struct int_wrapper { ++destructions; } - int_wrapper& operator=(int_wrapper&& that) { - if (that.val == magic_throwing_val) { - throw magic_throwing_val; - } - val = exchange(that.val, -1); - return *this; + int_wrapper& operator=(int_wrapper&&) { + abort(); } auto operator<=>(const int_wrapper&) const = default; diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst 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 new file mode 100644 index 00000000000..eef82b952c4 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +// Validate that uninitialized_move_n_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +struct int_wrapper { + inline static int constructions = 0; + inline static int destructions = 0; + + static void clear_counts() { + constructions = 0; + destructions = 0; + } + + static constexpr int magic_throwing_val = 29; + int val = 10; + + int_wrapper() { + ++constructions; + } + int_wrapper(int x) : val{x} { + ++constructions; + } + + int_wrapper(int_wrapper&& that) { + if (that.val == magic_throwing_val) { + throw magic_throwing_val; + } + + val = exchange(that.val, -1); + ++constructions; + } + + ~int_wrapper() { + ++destructions; + } + + int_wrapper& operator=(int_wrapper&&) { + abort(); + } + + auto operator<=>(const int_wrapper&) const = default; +}; +STATIC_ASSERT(movable && !copyable); + +template +struct holder { + STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); + alignas(T) unsigned char space[N * sizeof(T)]; + + auto as_span() { + return span{reinterpret_cast(space + 0), N}; + } +}; + +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}; + + template + static void call() { + using ranges::uninitialized_move_n, ranges::uninitialized_move_n_result, ranges::equal, ranges::equal_to, + ranges::iterator_t; + + 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)); + not_ranges_destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } +}; + +struct throwing_test { + static constexpr int expected_input[] = {-1, -1, int_wrapper::magic_throwing_val, 12345}; + + template + static void call() { + int_wrapper input[] = {13, 55, int_wrapper::magic_throwing_val, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + try { + (void) ranges::uninitialized_move_n(wrapped_input.begin(), 4, wrapped_output.begin(), wrapped_output.end()); + assert(false); + } catch (int i) { + assert(i == int_wrapper::magic_throwing_val); + } catch (...) { + assert(false); + } + assert(int_wrapper::constructions == 2); + assert(int_wrapper::destructions == 2); + assert(ranges::equal(input, expected_input, ranges::equal_to{}, &int_wrapper::val)); + } +}; + +template +using test_input = test::range; +using test_output = test::range; + +int main() { + // The algorithm is oblivious to non-required category, size, difference, and "proxyness" of the input range. It + // _is_ sensitive to proxyness in that it requires non-proxy references for the output range. + + instantiator::call, test_output>(); + instantiator::call, test_output>(); + throwing_test::call, test_output>(); + throwing_test::call, test_output>(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp new file mode 100644 index 00000000000..a17ede367c5 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +// Validate dangling story +STATIC_ASSERT(same_as{})), int*>); +STATIC_ASSERT(same_as{})), ranges::dangling>); + +struct int_wrapper { + inline static int constructions = 0; + inline static int destructions = 0; + + static void clear_counts() { + constructions = 0; + destructions = 0; + } + + static constexpr int magic_throwing_val = 4; + int val = 10; + + int_wrapper() { + if (++constructions == magic_throwing_val) { + throw magic_throwing_val; + } + } + + ~int_wrapper() { + ++destructions; + } + + auto operator<=>(const int_wrapper&) const = default; +}; +STATIC_ASSERT(default_initializable); + +template +struct holder { + STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); + alignas(T) unsigned char space[N * sizeof(T)]; + + auto as_span() { + return span{reinterpret_cast(space + 0), N}; + } +}; + +template +void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy + for (auto& e : r) { + destroy_at(&e); + } +} + +struct instantiator { + static constexpr int expected[3] = {10, 10, 10}; + + template + static void call() { + using ranges::uninitialized_value_construct, ranges::equal, ranges::equal_to, ranges::iterator_t; + + { // Validate range overload + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = uninitialized_value_construct(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + assert(equal(wrapped_input, expected, equal_to{}, &int_wrapper::val)); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + + { // Validate iterator overload + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = + uninitialized_value_construct(wrapped_input.begin(), wrapped_input.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + assert(equal(wrapped_input, expected, equal_to{}, &int_wrapper::val)); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + } +}; + +struct throwing_test { + template + static void call() { + // Validate only range overload (one is plenty since they both use the same backend) + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + try { + (void) ranges::uninitialized_value_construct(wrapped_input); + assert(false); + } catch (int i) { + assert(i == int_wrapper::magic_throwing_val); + } catch (...) { + assert(false); + } + assert(int_wrapper::constructions == int_wrapper::magic_throwing_val); + assert(int_wrapper::destructions == int_wrapper::magic_throwing_val - 1); + } +}; + +using test_range = test::range; + +int main() { + // The algorithm is oblivious to non-required category, size, difference. It _is_ sensitive to proxyness in that it + // requires non-proxy references for the input range. + + instantiator::call(); + throwing_test::call(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp new file mode 100644 index 00000000000..6a9f08449d8 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +struct int_wrapper { + inline static int constructions = 0; + inline static int destructions = 0; + + static void clear_counts() { + constructions = 0; + destructions = 0; + } + + static constexpr int magic_throwing_val = 4; + int val = 10; + + int_wrapper() { + if (++constructions == magic_throwing_val) { + throw magic_throwing_val; + } + } + + ~int_wrapper() { + ++destructions; + } + + auto operator<=>(const int_wrapper&) const = default; +}; +STATIC_ASSERT(default_initializable); + +template +struct holder { + STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); + alignas(T) unsigned char space[N * sizeof(T)]; + + auto as_span() { + return span{reinterpret_cast(space + 0), N}; + } +}; + +template +void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy + for (auto& e : r) { + destroy_at(&e); + } +} + +struct instantiator { + static constexpr int expected[3] = {10, 10, 10}; + + template + static void call() { + using ranges::uninitialized_value_construct_n, ranges::equal, ranges::equal_to, ranges::iterator_t; + + { // Validate iterator overload + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = uninitialized_value_construct_n(wrapped_input.begin(), 3); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + assert(equal(wrapped_input, expected, equal_to{}, &int_wrapper::val)); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + } +}; + +struct throwing_test { + template + static void call() { + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + try { + (void) ranges::uninitialized_value_construct_n(wrapped_input.begin(), int_wrapper::magic_throwing_val); + assert(false); + } catch (int i) { + assert(i == int_wrapper::magic_throwing_val); + } catch (...) { + assert(false); + } + assert(int_wrapper::constructions == int_wrapper::magic_throwing_val); + assert(int_wrapper::destructions == int_wrapper::magic_throwing_val - 1); + } +}; + +using test_range = test::range; + +int main() { + // The algorithm is oblivious to non-required category, size, difference. It _is_ sensitive to proxyness in that it + // requires non-proxy references for the input range. + + instantiator::call(); + throwing_test::call(); +}