diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 52e23b979..beaf4b589 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -72,6 +72,160 @@ struct _Optimistic_temporary_buffer { // temporary storage with _alloca-like att aligned_union_t<0, _Ty> _Stack_space[_Optimistic_count]; }; +#ifdef __cpp_lib_concepts +namespace ranges { + // CONCEPT _Convertible_from + template + concept _Convertible_from = convertible_to<_From, _To>; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif // __clang__ + + // STRUCT TEMPLATE in_fun_result + template + struct in_fun_result { + [[no_unique_address]] _In in; + [[no_unique_address]] _Fn fun; + + // clang-format off + template <_Convertible_from _IIn, _Convertible_from _FFn> + constexpr operator in_fun_result<_IIn, _FFn>() const & { + return {in, fun}; + } + + template <_Convertible_from<_In> _IIn, _Convertible_from<_Fn> _FFn> + constexpr operator in_fun_result<_IIn, _FFn>() && { + return {_STD move(in), _STD move(fun)}; + } + // clang-format on + }; + + // STRUCT TEMPLATE in_in_result + template + struct in_in_result { + [[no_unique_address]] _In1 in1; + [[no_unique_address]] _In2 in2; + + // clang-format off + template <_Convertible_from _IIn1, _Convertible_from _IIn2> + constexpr operator in_in_result<_IIn1, _IIn2>() const & { + return {in1, in2}; + } + + template <_Convertible_from<_In1> _IIn1, _Convertible_from<_In2> _IIn2> + constexpr operator in_in_result<_IIn1, _IIn2>() && { + return {_STD move(in1), _STD move(in2)}; + } + // clang-format on + }; + + // STRUCT TEMPLATE in_out_result + template + struct in_out_result { + [[no_unique_address]] _In in; + [[no_unique_address]] _Out out; + + // clang-format off + template <_Convertible_from _IIn, _Convertible_from _OOut> + constexpr operator in_out_result<_IIn, _OOut>() const & { + return {in, out}; + } + + template <_Convertible_from<_In> _IIn, _Convertible_from<_Out> _OOut> + constexpr operator in_out_result<_IIn, _OOut>() && { + return {_STD move(in), _STD move(out)}; + } + // clang-format on + }; + + // STRUCT TEMPLATE in_in_out_result + template + struct in_in_out_result { + [[no_unique_address]] _In1 in1; + [[no_unique_address]] _In2 in2; + [[no_unique_address]] _Out out; + + // clang-format off + template <_Convertible_from _IIn1, _Convertible_from _IIn2, + _Convertible_from _OOut> + constexpr operator in_in_out_result<_IIn1, _IIn2, _OOut>() const & { + return {in1, in2, out}; + } + + template <_Convertible_from<_In1> _IIn1, _Convertible_from<_In2> _IIn2, _Convertible_from<_Out> _OOut> + constexpr operator in_in_out_result<_IIn1, _IIn2, _OOut>() && { + return {_STD move(in1), _STD move(in2), _STD move(out)}; + } + // clang-format on + }; + + // STRUCT TEMPLATE in_out_out_result + template + struct in_out_out_result { + [[no_unique_address]] _In in; + [[no_unique_address]] _Out1 out1; + [[no_unique_address]] _Out2 out2; + + // clang-format off + template <_Convertible_from _IIn, _Convertible_from _OOut1, + _Convertible_from _OOut2> + constexpr operator in_out_out_result<_IIn, _OOut1, _OOut2>() const & { + return {in, out1, out2}; + } + + template <_Convertible_from<_In> _IIn, _Convertible_from<_Out1> _OOut1, _Convertible_from<_Out2> _OOut2> + constexpr operator in_out_out_result<_IIn, _OOut1, _OOut2>() && { + return {_STD move(in), _STD move(out1), _STD move(out2)}; + } + // clang-format on + }; + + // STRUCT TEMPLATE min_max_result + template + struct min_max_result { + [[no_unique_address]] _Ty min; + [[no_unique_address]] _Ty max; + + // clang-format off + template <_Convertible_from _Ty2> + constexpr operator min_max_result<_Ty2>() const & { + return {min, max}; + } + + template <_Convertible_from<_Ty> _Ty2> + constexpr operator min_max_result<_Ty2>() && { + return {_STD move(min), _STD move(max)}; + } + // clang-format on + }; + + // STRUCT TEMPLATE in_found_result + template + struct in_found_result { + [[no_unique_address]] _In in; + bool found; + + // clang-format off + template <_Convertible_from _IIn> + constexpr operator in_found_result<_IIn>() const & { + return {in, found}; + } + + template <_Convertible_from<_In> _IIn> + constexpr operator in_found_result<_IIn>() && { + return {_STD move(in), found}; + } + // clang-format on + }; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE for_each template _CONSTEXPR20 _Fn for_each(_InIt _First, _InIt _Last, _Fn _Func) { // perform function for each element [_First, _Last) @@ -132,12 +286,148 @@ _SourceTy* for_each_n( #endif // _ITERATOR_DEBUG_ARRAY_OVERLOADS #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE for_each_result + template + using for_each_result = in_fun_result<_It, _Fn>; + + // VARIABLE ranges::for_each + class _For_each_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirectly_unary_invocable> _Fn> + constexpr for_each_result<_It, _Fn> operator()(_It _First, _Se _Last, _Fn _Func, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + for (; _UFirst != _ULast; ++_UFirst) { + _C_invoke(_Func, _C_invoke(_Proj, *_UFirst)); + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return {_STD move(_First), _STD move(_Func)}; + } + + template , _Pj>> _Fn> + constexpr for_each_result, _Fn> operator()( + _Rng&& _Range, _Fn _Func, _Pj _Proj = {}) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _STD move(_Func), _Pass_fn(_Proj)); + } + }; + + inline constexpr _For_each_fn for_each{_Not_quite_object::_Construct_tag{}}; + + // ALIAS TEMPLATE for_each_n_result + template + using for_each_n_result = in_fun_result<_It, _Fn>; + + // VARIABLE ranges::for_each_n + class _For_each_n_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template > _Fn> + constexpr for_each_n_result<_It, _Fn> operator()( + _It _First, iter_difference_t<_It> _Count, _Fn _Func, _Pj _Proj = {}) const { + if (0 < _Count) { + auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); + do { + _C_invoke(_Func, _C_invoke(_Proj, *_UFirst)); + --_Count; + ++_UFirst; + } while (0 < _Count); + + _Seek_wrapped(_First, _STD move(_UFirst)); + } + + return {_STD move(_First), _STD move(_Func)}; + } + }; + + inline constexpr _For_each_n_fn for_each_n{_Not_quite_object::_Construct_tag{}}; + + // VARIABLE ranges::find + class _Find_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, const _Ty*> + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + // Performance note: investigate using _Find_unchecked when same_as<_Pj, identity> + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + for (; _UFirst != _ULast; ++_UFirst) { + if (_C_invoke(_Proj, *_UFirst) == _Val) { + break; + } + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return _First; + } + + template + requires indirect_binary_predicate, _Pj>, const _Ty*> + _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()( + _Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Val, _Pass_fn(_Proj)); + } + // clang-format on + }; + + inline constexpr _Find_fn find{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + #if _HAS_CXX17 // PARALLEL FUNCTION TEMPLATE find_if template = 0> _NODISCARD _FwdIt find_if(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, _Pr _Pred) noexcept; // terminates #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::find_if + class _Find_if_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + for (; _UFirst != _ULast; ++_UFirst) { + if (_C_invoke(_Pred, _C_invoke(_Proj, *_UFirst))) { + break; + } + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return _First; + } + + template , _Pj>> _Pr> + _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + // clang-format on + }; + + inline constexpr _Find_if_fn find_if{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE find_if_not template _NODISCARD _CONSTEXPR20 _InIt find_if_not(_InIt _First, const _InIt _Last, _Pr _Pred) { @@ -160,6 +450,42 @@ template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + for (; _UFirst != _ULast; ++_UFirst) { + if (!_C_invoke(_Pred, _C_invoke(_Proj, *_UFirst))) { + break; + } + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return _First; + } + + template , _Pj>> _Pr> + _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + // clang-format on + }; + + inline constexpr _Find_if_not_fn find_if_not{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE adjacent_find template _NODISCARD _CONSTEXPR20 _FwdIt adjacent_find(const _FwdIt _First, _FwdIt _Last, _Pr _Pred) { @@ -196,6 +522,45 @@ _NODISCARD _FwdIt adjacent_find(_ExPo&& _Exec, const _FwdIt _First, const _FwdIt } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::count + class _Count_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, const _Ty*> + _NODISCARD constexpr iter_difference_t<_It> operator()( + _It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + iter_difference_t<_It> _Count = 0; + + for (; _UFirst != _ULast; ++_UFirst) { + if (_C_invoke(_Proj, *_UFirst) == _Val) { + ++_Count; + } + } + + return _Count; + } + + template + requires indirect_binary_predicate, _Pj>, const _Ty*> + _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Val, _Pass_fn(_Proj)); + } + // clang-format on + }; + + inline constexpr _Count_fn count{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE count_if template _NODISCARD _CONSTEXPR20 _Iter_diff_t<_InIt> count_if(_InIt _First, _InIt _Last, _Pr _Pred) { @@ -219,6 +584,42 @@ _NODISCARD _Iter_diff_t<_FwdIt> count_if( _ExPo&& _Exec, const _FwdIt _First, const _FwdIt _Last, _Pr _Pred) noexcept; // terminates #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::count_if + class _Count_if_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + iter_difference_t<_It> _Count = 0; + for (; _UFirst != _ULast; ++_UFirst) { + if (_C_invoke(_Pred, _C_invoke(_Proj, *_UFirst))) { + ++_Count; + } + } + + return _Count; + } + + template , _Pj>> _Pr> + _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + // clang-format on + }; + + inline constexpr _Count_if_fn count_if{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE mismatch template _NODISCARD _CONSTEXPR20 pair<_InIt1, _InIt2> mismatch(_InIt1 _First1, const _InIt1 _Last1, _InIt2 _First2, _Pr _Pred) { @@ -394,6 +795,194 @@ _NODISCARD pair<_FwdIt1, _FwdIt2> mismatch( } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE mismatch_result + template + using mismatch_result = in_in_result<_In1, _In2>; + + // VARIABLE ranges::mismatch + class _Mismatch_fn : private _Not_quite_object { + private: + template + _NODISCARD static constexpr mismatch_result<_It1, _It2> _Mismatch_n( + _It1 _First1, _It2 _First2, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); + + for (; _Count != 0; ++_UFirst1, (void) ++_UFirst2, --_Count) { + if (!_C_invoke(_Pred, _C_invoke(_Proj1, *_UFirst1), _C_invoke(_Proj2, *_UFirst2))) { + break; + } + } + + _Seek_wrapped(_First1, _STD move(_UFirst1)); + _Seek_wrapped(_First2, _STD move(_UFirst2)); + return {_STD move(_First1), _STD move(_First2)}; + } + + template + _NODISCARD static constexpr mismatch_result<_It1, _It2> _Mismatch_4( + _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + const auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); + auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); + const auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); + + for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) { + if (!_C_invoke(_Pred, _C_invoke(_Proj1, *_UFirst1), _C_invoke(_Proj2, *_UFirst2))) { + break; + } + } + + _Seek_wrapped(_First1, _STD move(_UFirst1)); + _Seek_wrapped(_First2, _STD move(_UFirst2)); + return {_STD move(_First1), _STD move(_First2)}; + } + + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + class _Pr = ranges::equal_to, class _Pj1 = identity, class _Pj2 = identity> + requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr mismatch_result<_It1, _It2> operator()(_It1 _First1, _Se1 _Last1, + _It2 _First2, _Se2 _Last2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + + if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + iter_difference_t<_It1> _Count1 = _Last1 - _First1; + const iter_difference_t<_It2> _Count2 = _Last2 - _First2; + if (_Count1 > _Count2) { + _Count1 = static_cast(_Count2); + } + + return _Mismatch_n(_STD move(_First1), _STD move(_First2), _Count1, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } else { + return _Mismatch_4(_STD move(_First1), _STD move(_Last1), _STD move(_First2), _STD move(_Last2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + } + + template + requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr mismatch_result, borrowed_iterator_t<_Rng2>> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { + range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); + const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); + if (_Count1 > _Count2) { + _Count1 = static_cast>(_Count2); + } + + return _Mismatch_n(_RANGES begin(_Range1), _RANGES begin(_Range2), _Count1, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } else { + return _Mismatch_4(_RANGES begin(_Range1), _RANGES end(_Range1), + _RANGES begin(_Range2), _RANGES end(_Range2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + } + // clang-format on + }; + + inline constexpr _Mismatch_fn mismatch{_Not_quite_object::_Construct_tag{}}; + + // VARIABLE ranges::equal + class _Equal_fn : private _Not_quite_object { + private: + template + _NODISCARD static constexpr bool _Equal_count( + _It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + for (; _Count != 0; ++_First1, (void) ++_First2, --_Count) { + if (!_C_invoke(_Pred, _C_invoke(_Proj1, *_First1), _C_invoke(_Proj2, *_First2))) { + return false; + } + } + + return true; + } + + template + _NODISCARD static constexpr bool _Equal_4( + _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + for (;;) { + if (_First1 == _Last1) { + return _First2 == _Last2; + } else if (_First2 == _Last2) { + return false; + } + + if (!_C_invoke(_Pred, _C_invoke(_Proj1, *_First1), _C_invoke(_Proj2, *_First2))) { + return false; + } + + ++_First1; + ++_First2; + } + } + + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + class _Pr = ranges::equal_to, class _Pj1 = identity, class _Pj2 = identity> + requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr bool operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred = {}, + _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); + auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); + auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); + + if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + const auto _Count = _ULast1 - _UFirst1; + if (_Count != _ULast2 - _UFirst2) { + return false; + } + + return _Equal_count(_STD move(_UFirst1), _STD move(_UFirst2), _Count, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } else { + return _Equal_4(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), _STD move(_ULast2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + } + + template + requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr bool operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { + using _Size1 = _Make_unsigned_like_t>; + const auto _Count = static_cast<_Size1>(_RANGES size(_Range1)); + using _Size2 = _Make_unsigned_like_t>; + if (_Count != static_cast<_Size2>(_RANGES size(_Range2))) { + return false; + } + return _Equal_count(_Get_unwrapped(_RANGES begin(_Range1)), _Get_unwrapped(_RANGES begin(_Range2)), + _Count, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } else { + return _Equal_4(_Get_unwrapped(_RANGES begin(_Range1)), _Get_unwrapped(_RANGES end(_Range1)), + _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + } + // clang-format on + }; + + inline constexpr _Equal_fn equal{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE all_of template _NODISCARD _CONSTEXPR20 bool all_of(_InIt _First, _InIt _Last, _Pr _Pred) { // test if all elements satisfy _Pred @@ -414,6 +1003,39 @@ template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr bool operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + for (; _UFirst != _ULast; ++_UFirst) { + if (!_C_invoke(_Pred, _C_invoke(_Proj, *_UFirst))) { + return false; + } + } + + return true; + } + + template , _Pj>> _Pr> + _NODISCARD constexpr bool operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + }; + + inline constexpr _All_of_fn all_of{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE any_of template _NODISCARD _CONSTEXPR20 bool any_of(const _InIt _First, const _InIt _Last, _Pr _Pred) { @@ -435,6 +1057,39 @@ template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr bool operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + for (; _UFirst != _ULast; ++_UFirst) { + if (_C_invoke(_Pred, _C_invoke(_Proj, *_UFirst))) { + return true; + } + } + + return false; + } + + template , _Pj>> _Pr> + _NODISCARD constexpr bool operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + }; + + inline constexpr _Any_of_fn any_of{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE none_of template _NODISCARD _CONSTEXPR20 bool none_of(const _InIt _First, const _InIt _Last, _Pr _Pred) { @@ -456,6 +1111,105 @@ template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr bool operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + for (; _UFirst != _ULast; ++_UFirst) { + if (_C_invoke(_Pred, _C_invoke(_Proj, *_UFirst))) { + return false; + } + } + + return true; + } + + template , _Pj>> _Pr> + _NODISCARD constexpr bool operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + }; + + inline constexpr _None_of_fn none_of{_Not_quite_object::_Construct_tag{}}; + + // ALIAS TEMPLATE copy_result + template + using copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::copy + class _Copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out> + requires indirectly_copyable<_It, _Out> + constexpr copy_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + for (; _UFirst != _ULast; ++_UFirst, (void) ++_Result) { + *_Result = *_UFirst; + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return {_STD move(_First), _STD move(_Result)}; + } + + template + requires indirectly_copyable, _Out> + constexpr copy_result, _Out> operator()(_Rng&& _Range, _Out _Result) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _STD move(_Result)); + } + // clang-format on + }; + + inline constexpr _Copy_fn copy{_Not_quite_object::_Construct_tag{}}; + + // ALIAS TEMPLATE copy_n_result + template + using copy_n_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::copy_n + class _Copy_n_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template + requires indirectly_copyable<_It, _Out> + constexpr copy_n_result<_It, _Out> operator()(_It _First, iter_difference_t<_It> _Count, _Out _Result) const { + auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); + for (; _Count > 0; ++_UFirst, (void) ++_Result, --_Count) { + *_Result = *_UFirst; + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return {_STD move(_First), _STD move(_Result)}; + } + + template + requires indirectly_copyable, _Out> + constexpr copy_n_result, _Out> operator()(_Rng&& _Range, _Out _Result) const { + return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _STD move(_Result)); + } + // clang-format on + }; + + inline constexpr _Copy_n_fn copy_n{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE copy_if template _CONSTEXPR20 _OutIt copy_if(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred) { // copy each satisfying _Pred @@ -505,6 +1259,52 @@ _DestTy* copy_if( #endif // _ITERATOR_DEBUG_ARRAY_OVERLOADS #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE copy_if_result + template + using copy_if_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::copy_if + class _Copy_if_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out, class _Pj = identity, + indirect_unary_predicate> _Pr> + requires indirectly_copyable<_It, _Out> + constexpr copy_if_result<_It, _Out> operator()( + _It _First, _Se _Last, _Out _Result, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + const auto _ULast = _Get_unwrapped(_STD move(_Last)); + for (; _UFirst != _ULast; ++_UFirst) { + if (_C_invoke(_Pred, _C_invoke(_Proj, *_UFirst))) { + *_Result = *_UFirst; + ++_Result; + } + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return {_STD move(_First), _STD move(_Result)}; + } + + template , _Pj>> _Pr> + requires indirectly_copyable, _Out> + constexpr copy_if_result, _Out> operator()( + _Rng&& _Range, _Out _Result, _Pr _Pred, _Pj _Proj = {}) const { + return (*this)( + _RANGES begin(_Range), _RANGES end(_Range), _STD move(_Result), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + // clang-format on + }; + + inline constexpr _Copy_if_fn copy_if{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE partition_copy template _CONSTEXPR20 pair<_OutIt1, _OutIt2> partition_copy( diff --git a/stl/inc/concepts b/stl/inc/concepts index 597057621..4938950c2 100644 --- a/stl/inc/concepts +++ b/stl/inc/concepts @@ -48,11 +48,9 @@ concept derived_from = __is_base_of(_Base, _Derived) // CONCEPT convertible_to template concept convertible_to = __is_convertible_to(_From, _To) -#if 1 // Implement the PR of LWG-3151 - && requires { static_cast<_To>(_STD declval<_From>()); }; -#else // ^^^ LWG-3151 / N4810 vvv - && requires(_From (&_Fn)()) { static_cast<_To>(_Fn()); }; -#endif // select LWG-3151 vs. N4810 + && requires(add_rvalue_reference_t<_From> (&_Fn)()) { + static_cast<_To>(_Fn()); + }; // CONCEPT common_reference_with template @@ -181,12 +179,7 @@ concept swappable = requires(_Ty& __x, _Ty& __y) { // CONCEPT swappable_with template -concept swappable_with = -#if 1 // Implement the PR of LWG-3175 - common_reference_with<_Ty1, _Ty2> -#else // ^^^ LWG-3175 / N4810 vvv - common_reference_with&, const remove_reference_t<_Ty2>&> -#endif // select LWG-3175 vs. N4810 +concept swappable_with = common_reference_with<_Ty1, _Ty2> && requires(_Ty1&& __t, _Ty2&& __u) { _RANGES swap(static_cast<_Ty1&&>(__t), static_cast<_Ty1&&>(__t)); _RANGES swap(static_cast<_Ty2&&>(__u), static_cast<_Ty2&&>(__u)); @@ -201,41 +194,22 @@ concept copy_constructible = move_constructible<_Ty> && constructible_from<_Ty, const _Ty&> && convertible_to && constructible_from<_Ty, const _Ty> && convertible_to; -// CONCEPT movable +// CONCEPT _Boolean_testable template -concept movable = is_object_v<_Ty> && move_constructible<_Ty> && assignable_from<_Ty&, _Ty> && swappable<_Ty>; +concept _Boolean_testable_impl = convertible_to<_Ty, bool>; -// CONCEPT boolean -#if _HAS_STD_BOOLEAN -#define _STL_BOOLEAN_CONCEPT boolean -#else -#define _STL_BOOLEAN_CONCEPT _Boolean -#endif template -concept _STL_BOOLEAN_CONCEPT = movable> - && requires(const remove_reference_t<_Ty>& __x, const remove_reference_t<_Ty>& __y, const bool __b) { - { __x } -> convertible_to; - { !__x } -> convertible_to; - { __x && __y } -> same_as; - { __x && __b } -> same_as; - { __b && __y } -> same_as; - { __x || __y } -> same_as; - { __x || __b } -> same_as; - { __b || __y } -> same_as; - { __x == __y } -> convertible_to; - { __x == __b } -> convertible_to; - { __b == __y } -> convertible_to; - { __x != __y } -> convertible_to; - { __x != __b } -> convertible_to; - { __b != __y } -> convertible_to; +concept _Boolean_testable = _Boolean_testable_impl<_Ty> + && requires (_Ty&& __t) { + { !static_cast<_Ty&&>(__t) } -> _Boolean_testable_impl; }; // CONCEPT _Weakly_equality_comparable_with template concept _Half_equality_comparable = requires(const remove_reference_t<_Ty1>& __x, const remove_reference_t<_Ty2>& __y) { - { __x == __y } -> _STL_BOOLEAN_CONCEPT; - { __x != __y } -> _STL_BOOLEAN_CONCEPT; + { __x == __y } -> _Boolean_testable; + { __x != __y } -> _Boolean_testable; }; template @@ -257,10 +231,10 @@ concept equality_comparable_with = equality_comparable<_Ty1> && equality_compara // CONCEPT _Partially_ordered_with template concept _Half_ordered = requires(const remove_reference_t<_Ty1>& __t, const remove_reference_t<_Ty2>& __u) { - { __t < __u } -> _STL_BOOLEAN_CONCEPT; - { __t > __u } -> _STL_BOOLEAN_CONCEPT; - { __t <= __u } -> _STL_BOOLEAN_CONCEPT; - { __t >= __u } -> _STL_BOOLEAN_CONCEPT; + { __t < __u } -> _Boolean_testable; + { __t > __u } -> _Boolean_testable; + { __t <= __u } -> _Boolean_testable; + { __t >= __u } -> _Boolean_testable; }; template @@ -277,9 +251,20 @@ concept totally_ordered_with = totally_ordered<_Ty1> && totally_ordered<_Ty2> && totally_ordered&, const remove_reference_t<_Ty2>&>> && _Partially_ordered_with<_Ty1, _Ty2>; +// CONCEPT movable +template +concept movable = is_object_v<_Ty> + && move_constructible<_Ty> + && assignable_from<_Ty&, _Ty> + && swappable<_Ty>; + // CONCEPT copyable template -concept copyable = copy_constructible<_Ty> && movable<_Ty> && assignable_from<_Ty&, const _Ty&>; +concept copyable = copy_constructible<_Ty> + && movable<_Ty> + && assignable_from<_Ty&, _Ty&> + && assignable_from<_Ty&, const _Ty&> + && assignable_from<_Ty&, const _Ty>; // CONCEPT semiregular template @@ -301,13 +286,21 @@ concept regular_invocable = invocable<_FTy, _ArgTys...>; // CONCEPT predicate template -concept predicate = regular_invocable<_FTy, _ArgTys...> && _STL_BOOLEAN_CONCEPT>; +concept predicate = regular_invocable<_FTy, _ArgTys...> + && _Boolean_testable>; // CONCEPT relation template -concept relation = predicate<_FTy, _Ty1, _Ty1> && predicate<_FTy, _Ty2, _Ty2> && predicate<_FTy, _Ty1, _Ty2> +concept relation = + predicate<_FTy, _Ty1, _Ty1> + && predicate<_FTy, _Ty2, _Ty2> + && predicate<_FTy, _Ty1, _Ty2> && predicate<_FTy, _Ty2, _Ty1>; +// CONCEPT equivalence_relation +template +concept equivalence_relation = relation<_FTy, _Ty1, _Ty2>; + // CONCEPT strict_weak_order template concept strict_weak_order = relation<_FTy, _Ty1, _Ty2>; diff --git a/stl/inc/functional b/stl/inc/functional index 3849bde4e..3b1820dc3 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -658,18 +658,6 @@ _NODISCARD _Mem_fn<_Rx _Ty::*> mem_fn(_Rx _Ty::*_Pm) noexcept { return _Mem_fn<_Rx _Ty::*>(_Pm); } -#if _HAS_CXX20 -// STRUCT identity -struct identity { - template - _NODISCARD constexpr _Ty&& operator()(_Ty&& _Left) const noexcept { - return _STD forward<_Ty>(_Left); - } - - using is_transparent = int; -}; -#endif // _HAS_CXX20 - #if _HAS_CXX17 // FUNCTION TEMPLATE not_fn struct _Not_fn_tag { diff --git a/stl/inc/ranges b/stl/inc/ranges index ff76b56de..302433337 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -25,15 +25,12 @@ namespace ranges { // Much machinery defined in // clang-format off - // CONCEPT ranges::common_range - template - concept common_range = range<_Rng> && same_as, sentinel_t<_Rng>>; - // CONCEPT ranges::viewable_range template - concept viewable_range = range<_Rng> && (safe_range<_Rng> || view>); - // clang-format on + concept viewable_range = range<_Rng> + && (borrowed_range<_Rng> || view>); } // namespace ranges + _STD_END #pragma pop_macro("new") diff --git a/stl/inc/span b/stl/inc/span index 82434eaf0..223fdb070 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -260,7 +260,7 @@ class span; #ifdef __cpp_lib_concepts namespace ranges { template - inline constexpr bool enable_safe_range> = true; + inline constexpr bool enable_borrowed_range> = true; } // namespace ranges // VARIABLE TEMPLATE _Is_span_v @@ -293,7 +293,7 @@ concept _Is_span_compatible_range = && !_Is_std_array_v> && _RANGES contiguous_range<_Rng> && _RANGES sized_range<_Rng> - && (_RANGES safe_range<_Rng> || is_const_v<_Ty>) + && (_RANGES borrowed_range<_Rng> || is_const_v<_Ty>) && is_convertible_v>(*)[], _Ty(*)[]>; // clang-format on #else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv diff --git a/stl/inc/xstring b/stl/inc/xstring index ebd456a25..4f5fec773 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -1576,7 +1576,7 @@ private: #ifdef __cpp_lib_concepts namespace ranges { template - inline constexpr bool enable_safe_range> = true; + inline constexpr bool enable_borrowed_range> = true; } // namespace ranges #endif // __cpp_lib_concepts diff --git a/stl/inc/xutility b/stl/inc/xutility index dcefa1483..561f0ebae 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -155,6 +155,16 @@ _NODISCARD constexpr auto to_address(const _Ptr& _Val) noexcept { return _STD to_address(_Val.operator->()); // plain pointer overload must come first } } + +// STRUCT identity +struct identity { + template + _NODISCARD constexpr _Ty&& operator()(_Ty&& _Left) const noexcept { + return _STD forward<_Ty>(_Left); + } + + using is_transparent = int; +}; #endif // _HAS_CXX20 // FUNCTION TEMPLATE _Pass_fn @@ -312,10 +322,10 @@ template struct iterator_traits; template -using iter_difference_t = typename conditional_t<_Is_from_primary>, incrementable_traits<_Ty>, - iterator_traits<_Ty>>::difference_type; +using iter_difference_t = typename conditional_t<_Is_from_primary>>, + incrementable_traits>, iterator_traits>>::difference_type; -// STRUCT TEMPLATE readable_traits +// STRUCT TEMPLATE indirectly_readable_traits template struct _Cond_value_type {}; @@ -328,32 +338,32 @@ struct _Cond_value_type<_Ty> { // clang-format on template -struct readable_traits {}; +struct indirectly_readable_traits {}; template -struct readable_traits<_Ty*> : _Cond_value_type<_Ty> {}; +struct indirectly_readable_traits<_Ty*> : _Cond_value_type<_Ty> {}; // clang-format off template requires is_array_v<_Ty> -struct readable_traits<_Ty> { +struct indirectly_readable_traits<_Ty> { using value_type = remove_cv_t>; }; // clang-format on template -struct readable_traits : readable_traits<_Ty> {}; +struct indirectly_readable_traits : indirectly_readable_traits<_Ty> {}; template <_Has_member_value_type _Ty> -struct readable_traits<_Ty> : _Cond_value_type {}; +struct indirectly_readable_traits<_Ty> : _Cond_value_type {}; template <_Has_member_element_type _Ty> -struct readable_traits<_Ty> : _Cond_value_type {}; +struct indirectly_readable_traits<_Ty> : _Cond_value_type {}; // ALIAS TEMPLATE iter_value_t template -using iter_value_t = typename conditional_t<_Is_from_primary>, readable_traits<_Ty>, - iterator_traits<_Ty>>::value_type; +using iter_value_t = typename conditional_t<_Is_from_primary>>, + indirectly_readable_traits>, iterator_traits>>::value_type; // ALIAS TEMPLATE iter_reference_t template <_Dereferenceable _Ty> @@ -411,11 +421,13 @@ concept _Cpp17_iterator = copyable<_It> && requires(_It __i) { }; template -concept _Cpp17_input_iterator = _Cpp17_iterator<_It> && equality_comparable<_It> - && _Has_member_difference_type> && _Has_member_value_type> +concept _Cpp17_input_iterator = _Cpp17_iterator<_It> + && equality_comparable<_It> + && _Has_member_difference_type> + && _Has_member_value_type> && requires(_It __i) { - typename common_reference_t&&, typename readable_traits<_It>::value_type&>; - typename common_reference_t::value_type&>; + typename common_reference_t&&, typename indirectly_readable_traits<_It>::value_type&>; + typename common_reference_t::value_type&>; requires signed_integral::difference_type>; }; @@ -544,7 +556,7 @@ struct _Iter_traits_category2 { // clang-format off template concept _Cpp17_forward_delta = constructible_from<_It> && is_lvalue_reference_v> - && same_as>, typename readable_traits<_It>::value_type> + && same_as>, typename indirectly_readable_traits<_It>::value_type> && requires(_It __i) { { __i++ } -> convertible_to; requires same_as>; @@ -569,7 +581,7 @@ template struct _Iterator_traits_base<_It> { using iterator_category = typename _Iter_traits_category<_Has_member_iterator_category<_It>>::template _Apply<_It>; using difference_type = typename incrementable_traits<_It>::difference_type; - using value_type = typename readable_traits<_It>::value_type; + using value_type = typename indirectly_readable_traits<_It>::value_type; using pointer = typename _Iter_traits_pointer<( _Has_member_pointer<_It> ? _Itraits_pointer_strategy::_Use_member : _Has_op_arrow<_It&> ? _Itraits_pointer_strategy::_Use_decltype @@ -607,12 +619,13 @@ namespace ranges { }; namespace _Iter_move { - void iter_move(); // Block unqualified lookup per LWG-3247 + void iter_move(); // Block unqualified name lookup // clang-format off template - concept _Has_ADL = _Has_class_or_enum_type<_Ty> // Per LWG-3299 - && requires(_Ty&& __t) { iter_move(static_cast<_Ty&&>(__t)); }; + concept _Has_ADL = _Has_class_or_enum_type<_Ty> && requires(_Ty&& __t) { + iter_move(static_cast<_Ty&&>(__t)); + }; template concept _Can_deref = requires(_Ty&& __t) { @@ -673,24 +686,29 @@ template } using iter_rvalue_reference_t = decltype(_RANGES iter_move(_STD declval<_Ty&>())); -// CONCEPT readable +// CONCEPT indirectly_readable template -concept readable = requires { +concept _Indirectly_readable_impl = requires(const _It __i) { typename iter_value_t<_It>; typename iter_reference_t<_It>; typename iter_rvalue_reference_t<_It>; + { *__i } -> same_as>; + { _RANGES iter_move(__i) } -> same_as>; } && common_reference_with&&, iter_value_t<_It>&> && common_reference_with&&, iter_rvalue_reference_t<_It>&&> && common_reference_with&&, const iter_value_t<_It>&>; + +template +concept indirectly_readable = _Indirectly_readable_impl>; // clang-format on // ALIAS TEMPLATE iter_common_reference_t -template +template using iter_common_reference_t = common_reference_t, iter_value_t<_Ty>&>; -// CONCEPT writable +// CONCEPT indirectly_writable template -concept writable = requires(_It&& __i, _Ty&& __t) { +concept indirectly_writable = requires(_It&& __i, _Ty&& __t) { *__i = static_cast<_Ty&&>(__t); *static_cast<_It&&>(__i) = static_cast<_Ty&&>(__t); const_cast&&>(*__i) = static_cast<_Ty&&>(__t); @@ -707,6 +725,10 @@ concept weakly_incrementable = default_initializable<_Ty> && movable<_Ty> && req __i++; }; +// ALIAS TEMPLATE _Make_unsigned_like_t +template +using _Make_unsigned_like_t = make_unsigned_t<_Ty>; + // CONCEPT incrementable template concept incrementable = regular<_Ty> && weakly_incrementable<_Ty> && requires(_Ty __t) { @@ -782,14 +804,16 @@ using _Iter_concept = typename _Iter_concept_impl<_It>::type; // clang-format off // CONCEPT input_iterator template -concept input_iterator = input_or_output_iterator<_It> && readable<_It> && requires { typename _Iter_concept<_It>; } +concept input_iterator = input_or_output_iterator<_It> && indirectly_readable<_It> + && requires { typename _Iter_concept<_It>; } && derived_from<_Iter_concept<_It>, input_iterator_tag>; // CONCEPT output_iterator template -concept output_iterator = input_or_output_iterator<_It> && writable<_It, _Ty> && requires(_It __i, _Ty&& __t) { - *__i++ = static_cast<_Ty&&>(__t); -}; +concept output_iterator = input_or_output_iterator<_It> && indirectly_writable<_It, _Ty> + && requires(_It __i, _Ty&& __t) { + *__i++ = static_cast<_Ty&&>(__t); + }; // CONCEPT forward_iterator template @@ -827,15 +851,94 @@ concept contiguous_iterator = random_access_iterator<_It> { _STD to_address(__i) } -> same_as>>; }; +// CONCEPT indirectly_unary_invocable +template +concept indirectly_unary_invocable = indirectly_readable<_It> + && copy_constructible<_Fn> + && invocable<_Fn&, iter_value_t<_It>&> + && invocable<_Fn&, iter_reference_t<_It>> + && invocable<_Fn&, iter_common_reference_t<_It>> + && common_reference_with< + invoke_result_t<_Fn&, iter_value_t<_It>&>, + invoke_result_t<_Fn&, iter_reference_t<_It>>>; + +// CONCEPT indirectly_regular_unary_invocable +template +concept indirectly_regular_unary_invocable = indirectly_readable<_It> + && copy_constructible<_Fn> + && regular_invocable<_Fn&, iter_value_t<_It>&> + && regular_invocable<_Fn&, iter_reference_t<_It>> + && regular_invocable<_Fn&, iter_common_reference_t<_It>> + && common_reference_with< + invoke_result_t<_Fn&, iter_value_t<_It>&>, + invoke_result_t<_Fn&, iter_reference_t<_It>>>; + +// CONCEPT indirect_unary_predicate +template +concept indirect_unary_predicate = indirectly_readable<_It> + && copy_constructible<_Fn> + && predicate<_Fn&, iter_value_t<_It>&> + && predicate<_Fn&, iter_reference_t<_It>> + && predicate<_Fn&, iter_common_reference_t<_It>>; + +// CONCEPT indirect_binary_predicate +template +concept indirect_binary_predicate = indirectly_readable<_It1> + && indirectly_readable<_It2> + && copy_constructible<_Fn> + && predicate<_Fn&, iter_value_t<_It1>&, iter_value_t<_It2>&> + && predicate<_Fn&, iter_value_t<_It1>&, iter_reference_t<_It2>> + && predicate<_Fn&, iter_reference_t<_It1>, iter_value_t<_It2>&> + && predicate<_Fn&, iter_reference_t<_It1>, iter_reference_t<_It2>> + && predicate<_Fn&, iter_common_reference_t<_It1>, iter_common_reference_t<_It2>>; + +// ALIAS TEMPLATE indirect_result_t +template + requires (indirectly_readable<_Its> && ...) + && invocable<_Fn, iter_reference_t<_Its>...> +using indirect_result_t = invoke_result_t<_Fn, iter_reference_t<_Its>...>; +// clang-format on + +// STRUCT TEMPLATE projected +#pragma warning(push) +#pragma warning(disable : 5046) // '%s': Symbol involving type with internal linkage not defined +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundefined-internal" // function '%s' has internal linkage but is not defined +#endif // __clang__ + +template _Proj> +struct projected { + using value_type = remove_cvref_t>; + indirect_result_t<_Proj&, _It> operator*() const; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ +#pragma warning(pop) + +template +struct incrementable_traits> { + using difference_type = iter_difference_t<_It>; +}; + +// clang-format off // CONCEPT indirectly_movable template -concept indirectly_movable = readable<_In> && writable<_Out, iter_rvalue_reference_t<_In>>; +concept indirectly_movable = indirectly_readable<_In> && indirectly_writable<_Out, iter_rvalue_reference_t<_In>>; // CONCEPT indirectly_movable_storable template -concept indirectly_movable_storable = indirectly_movable<_In, _Out> && writable<_Out, iter_value_t<_In>> - && movable> && constructible_from, iter_rvalue_reference_t<_In>> +concept indirectly_movable_storable = indirectly_movable<_In, _Out> + && indirectly_writable<_Out, iter_value_t<_In>> + && movable> + && constructible_from, iter_rvalue_reference_t<_In>> && assignable_from&, iter_rvalue_reference_t<_In>>; + +// CONCEPT indirectly_copyable +template +concept indirectly_copyable = indirectly_readable<_In> && indirectly_writable<_Out, iter_reference_t<_In>>; // clang-format on // CUSTOMIZATION POINT OBJECT iter_swap @@ -846,13 +949,14 @@ namespace ranges { // clang-format off template - concept _Has_ADL = (_Has_class_or_enum_type<_Ty1> || _Has_class_or_enum_type<_Ty2>) // Per LWG-3299 + concept _Has_ADL = (_Has_class_or_enum_type<_Ty1> || _Has_class_or_enum_type<_Ty2>) && requires(_Ty1&& __t1, _Ty2&& __t2) { iter_swap(static_cast<_Ty1&&>(__t1), static_cast<_Ty2&&>(__t2)); }; template - concept _Can_swap_references = readable> && readable> + concept _Can_swap_references = indirectly_readable> + && indirectly_readable> && swappable_with, iter_reference_t<_Ty2>>; // clang-format on @@ -912,6 +1016,25 @@ namespace ranges { } } // namespace ranges +// clang-format off +// CONCEPT indirectly_swappable +template +concept indirectly_swappable = indirectly_readable<_It1> && indirectly_readable<_It2> + && requires(const _It1 __i1, const _It2 __i2) { + _RANGES iter_swap(__i1, __i1); + _RANGES iter_swap(__i2, __i2); + _RANGES iter_swap(__i1, __i2); + _RANGES iter_swap(__i2, __i1); + }; + +// CONCEPT indirectly_comparable +template +concept indirectly_comparable = + indirect_binary_predicate<_Rel, + projected<_It1, _Proj1>, + projected<_It2, _Proj2>>; +// clang-format on + // ALIAS TEMPLATE _Iter_ref_t template using _Iter_ref_t = iter_reference_t<_Iter>; @@ -2018,14 +2141,14 @@ namespace ranges { inline constexpr bool _Has_complete_elements<_Ty> = true; // clang-format on - // VARIABLE TEMPLATE ranges::enable_safe_range + // VARIABLE TEMPLATE ranges::enable_borrowed_range template - inline constexpr bool enable_safe_range = false; + inline constexpr bool enable_borrowed_range = false; template - concept _Should_range_access = is_lvalue_reference_v<_Rng> || enable_safe_range>; + concept _Should_range_access = is_lvalue_reference_v<_Rng> || enable_borrowed_range>; - // CUSTOMIZATION POINT OBJECT ranges::begin (Implements D2091R0) + // CUSTOMIZATION POINT OBJECT ranges::begin namespace _Begin { template void begin(_Ty&) = delete; @@ -2054,8 +2177,8 @@ namespace ranges { if constexpr (is_array_v>) { static_assert(_Has_complete_elements<_Ty>, "The range access customization point objects " - "std::ranges::begin, std::ranges::end, std::ranges::rbegin, and std::ranges::rend " - "do not accept arrays with incomplete element types."); + "std::ranges::begin, std::ranges::end, std::ranges::rbegin, std::ranges::rend, " + "and std::ranges::data do not accept arrays with incomplete element types."); return {_St::_Array, true}; } else if constexpr (_Has_member<_Ty>) { return {_St::_Member, noexcept(_Fake_decay_copy(_STD declval<_Ty>().begin()))}; @@ -2092,11 +2215,11 @@ namespace ranges { inline constexpr _Begin::_Cpo begin; } - // ALIAS TEMPLATE ranges::iterator_t (Implements D2091R0) + // ALIAS TEMPLATE ranges::iterator_t template using iterator_t = decltype(_RANGES begin(_STD declval<_Ty&>())); - // CUSTOMIZATION POINT OBJECT ranges::end (Implements D2091R0) + // CUSTOMIZATION POINT OBJECT ranges::end namespace _End { template void end(_Ty&) = delete; @@ -2127,8 +2250,8 @@ namespace ranges { if constexpr (is_array_v<_UnRef>) { static_assert(_Has_complete_elements<_UnRef>, "The range access customization point objects " - "std::ranges::begin, std::ranges::end, std::ranges::rbegin, and std::ranges::rend " - "do not accept arrays with incomplete element types."); + "std::ranges::begin, std::ranges::end, std::ranges::rbegin, std::ranges::rend, " + "and std::ranges::data do not accept arrays with incomplete element types."); if constexpr (extent_v<_UnRef> != 0) { return {_St::_Array, true}; } else { @@ -2177,10 +2300,10 @@ namespace ranges { _RANGES end(__r); }; - // CONCEPT ranges::safe_range + // CONCEPT ranges::borrowed_range // clang-format off template - concept safe_range = range<_Rng> && _Should_range_access<_Rng>; + concept borrowed_range = range<_Rng> && _Should_range_access<_Rng>; // clang-format on // ALIAS TEMPLATE ranges::sentinel_t @@ -2235,7 +2358,7 @@ namespace ranges { inline constexpr _Cend_fn cend; } - // CUSTOMIZATION POINT OBJECT ranges::rbegin (Implements D2091R0) + // CUSTOMIZATION POINT OBJECT ranges::rbegin namespace _Rbegin { template void rbegin(_Ty&) = delete; @@ -2304,7 +2427,7 @@ namespace ranges { inline constexpr _Rbegin::_Cpo rbegin; } - // CUSTOMIZATION POINT OBJECT ranges::rend (Implements D2091R0) + // CUSTOMIZATION POINT OBJECT ranges::rend namespace _Rend { template void rend(_Ty&) = delete; @@ -2410,7 +2533,7 @@ namespace ranges { template inline constexpr bool disable_sized_range = false; - // CUSTOMIZATION POINT OBJECT ranges::size (Implements D2091R0) + // CUSTOMIZATION POINT OBJECT ranges::size namespace _Size { template void size(_Ty&) = delete; @@ -2479,7 +2602,7 @@ namespace ranges { return size(_Val); } else if constexpr (_Choice<_Ty&>._Strategy == _St::_Subtract) { const auto _Delta = _RANGES end(_Val) - _RANGES begin(_Val); - return static_cast>>(_Delta); + return static_cast<_Make_unsigned_like_t>>(_Delta); } else { static_assert(_Always_false<_Ty>, "should be unreachable"); } @@ -2492,7 +2615,7 @@ namespace ranges { inline constexpr _Size::_Cpo size; } - // CUSTOMIZATION POINT OBJECT ranges::empty (Implements D2091R0) + // CUSTOMIZATION POINT OBJECT ranges::empty namespace _Empty { // clang-format off template @@ -2514,13 +2637,13 @@ namespace ranges { class _Cpo { private: - enum class _St { _None, _Array, _Member, _Size, _Compare }; + enum class _St { _None, _Member, _Size, _Compare }; template _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { _STL_INTERNAL_STATIC_ASSERT(is_lvalue_reference_v<_Ty>); - if constexpr (is_array_v>) { - return {_St::_Array, true}; + if constexpr (is_unbounded_array_v>) { + return {_St::_None}; } else if constexpr (_Has_member<_Ty>) { return {_St::_Member, noexcept(static_cast(_STD declval<_Ty>().empty()))}; } else if constexpr (_Has_size<_Ty>) { @@ -2542,9 +2665,7 @@ namespace ranges { template requires (_Choice<_Ty&>._Strategy != _St::_None) _NODISCARD constexpr bool operator()([[maybe_unused]] _Ty&& _Val) const noexcept(_Choice<_Ty&>._No_throw) { - if constexpr (_Choice<_Ty&>._Strategy == _St::_Array) { - return false; - } else if constexpr (_Choice<_Ty&>._Strategy == _St::_Member) { + if constexpr (_Choice<_Ty&>._Strategy == _St::_Member) { return static_cast(_Val.empty()); } else if constexpr (_Choice<_Ty&>._Strategy == _St::_Size) { return _RANGES size(_Val) == 0; @@ -2562,7 +2683,7 @@ namespace ranges { inline constexpr _Empty::_Cpo empty; } - // CUSTOMIZATION POINT OBJECT ranges::data (Implements D2091R0) + // CUSTOMIZATION POINT OBJECT ranges::data namespace _Data { // clang-format off template @@ -2940,6 +3061,28 @@ namespace ranges { using is_transparent = int; }; + + // CONCEPT _Not_same_as + template + concept _Not_same_as = !same_as, remove_cvref_t<_Ty2>>; + + // CONCEPT ranges::common_range + // clang-format off + template + concept common_range = range<_Rng> + && same_as, sentinel_t<_Rng>>; + // clang-format on + + // STRUCT ranges::dangling + struct dangling { + constexpr dangling() noexcept = default; + template + constexpr dangling(_Args&&...) noexcept {} + }; + + // ALIAS TEMPLATE ranges::borrowed_iterator_t + template + using borrowed_iterator_t = conditional_t, iterator_t<_Rng>, dangling>; } // namespace ranges #endif // __cpp_lib_concepts diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 1e167b67a..2ace22740 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -169,21 +169,32 @@ // P1024R3 Enhancing span Usability // P1085R2 Removing span Comparisons // P1115R3 erase()/erase_if() Return size_type +// P1207R4 Movability Of Single-Pass Iterators +// (partially implemented) // P1209R0 erase_if(), erase() // P1227R2 Signed std::ssize(), Unsigned span::size() +// P1243R4 Rangify New Algorithms +// (partially implemented) +// P1248R1 Fixing Relations // P1357R1 is_bounded_array, is_unbounded_array // P1394R4 Range Constructor For span // P1423R3 char8_t Backward Compatibility Remediation // P1456R1 Move-Only Views +// P1474R1 Helpful Pointers For contiguous_iterator // P1612R1 Relocating endian To // P1645R1 constexpr For Algorithms // P1651R0 bind_front() Should Not Unwrap reference_wrapper // P1690R1 Refining Heterogeneous Lookup For Unordered Containers +// P1716R3 Range Comparison Algorithms Are Over-Constrained // P1754R1 Rename Concepts To standard_case -// P1870R1 safe_range +// P1870R1 Rename forwarding-range To borrowed_range (Was safe_range before LWG-3379) // P1872R0 span Should Have size_type, Not index_type +// P1878R1 Constraining Readable Types // P1956R1 has_single_bit(), bit_ceil(), bit_floor(), bit_width() // P1959R0 Removing weak_equality And strong_equality +// P1964R2 Replacing boolean With boolean-testable +// P2091R0 Fixing Issues With Range Access CPOs +// P2102R0 Making "Implicit Expression Variations" More Explicit // P????R? directory_entry::clear_cache() // _HAS_CXX20 indirectly controls: @@ -1082,11 +1093,6 @@ #if defined(__cpp_concepts) && __cpp_concepts > 201507L #define __cpp_lib_concepts 201907L - -// P0898R3 (as modified by P1754R1) std::boolean -#ifndef _HAS_STD_BOOLEAN -#define _HAS_STD_BOOLEAN 1 -#endif // _HAS_STD_BOOLEAN #endif // defined(__cpp_concepts) && __cpp_concepts > 201507L #define __cpp_lib_constexpr_algorithms 201806L diff --git a/tests/std/include/instantiate_algorithms.hpp b/tests/std/include/instantiate_algorithms.hpp index 701611865..0f9784366 100644 --- a/tests/std/include/instantiate_algorithms.hpp +++ b/tests/std/include/instantiate_algorithms.hpp @@ -50,6 +50,12 @@ namespace std_testing { // expensive, and even for relatively cheap to copy function objects we care (somewhat) about debug // mode perf. + struct Immobile { + Immobile() = default; + Immobile(const Immobile&) = delete; + Immobile& operator=(const Immobile&) = delete; + }; + struct MoveOnly { MoveOnly() = default; MoveOnly(const MoveOnly&) = delete; @@ -98,14 +104,14 @@ namespace std_testing { void operator()(const T&) {} }; - struct Rng : MoveOnly { + struct Rng : Immobile { template Integral operator()(Integral) { return 0; } }; - struct Urng : MoveOnly { + struct Urng : Immobile { typedef unsigned int result_type; unsigned int operator()() { return 4; // chosen by fair dice roll; guaranteed to be random diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp new file mode 100644 index 000000000..32ba5f475 --- /dev/null +++ b/tests/std/include/range_algorithm_support.hpp @@ -0,0 +1,532 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +namespace ranges = std::ranges; + +template +inline constexpr bool always_false = false; + +namespace detail { + static constexpr bool permissive() { + return false; + } + + template + struct DependentBase { + static constexpr bool permissive() { + return true; + } + }; + + template + struct Derived : DependentBase { + static constexpr bool test() { + return permissive(); + } + }; +} +constexpr bool is_permissive = detail::Derived::test(); + +template +struct borrowed { // borrowed is a borrowed_range; borrowed is not + int* begin() const; + int* end() const; +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range> = true; + +inline constexpr auto get_first = [](auto&& x) -> auto&& { + return static_cast(x).first; +}; + +inline constexpr auto get_second = [](auto&& x) -> auto&& { + return static_cast(x).second; +}; + +template +class move_only_range : public ranges::view_base { + // Adapts a contiguous range into a move-only view with move-only iterators +private: + using U = std::span; + U elements; + mutable bool begin_called = false; + + class iterator; + class sentinel; + +public: + constexpr explicit move_only_range(U x) : elements{x} {} + + constexpr move_only_range(move_only_range&& that) + : elements{std::exchange(that.elements, {})}, begin_called{that.begin_called} {} + + constexpr move_only_range& operator=(move_only_range&& that) { + elements = std::exchange(that.elements, {}); + begin_called = that.begin_called; + return *this; + } + + constexpr iterator begin() const { + assert(!std::exchange(begin_called, true)); + return iterator{elements.begin()}; + } + + constexpr sentinel end() const { + return sentinel{elements.end()}; + } +}; + +template +move_only_range(R&)->move_only_range>>; + +template +class move_only_range::iterator { +private: + friend sentinel; + ranges::iterator_t> pos; + +public: + using iterator_concept = std::input_iterator_tag; + using iterator_category = std::output_iterator_tag; + using value_type = std::remove_cv_t; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = T&; + + iterator() = default; + constexpr explicit iterator(ranges::iterator_t p) : pos{p} {} + constexpr iterator(iterator&& that) : pos{std::exchange(that.pos, {})} {} + + constexpr iterator& operator=(iterator&& that) { + pos = std::exchange(that.pos, {}); + return *this; + } + + constexpr ranges::iterator_t base() const { + return pos; + } + + constexpr T& operator*() const { + return *pos; + } + constexpr iterator& operator++() { + ++pos; + return *this; + } + constexpr void operator++(int) { + ++pos; + } +}; + +template +class move_only_range::sentinel { +private: + ranges::iterator_t pos; + +public: + sentinel() = default; + constexpr explicit sentinel(ranges::iterator_t p) : pos{p} {} + + constexpr ranges::iterator_t base() const { + return pos; + } + + constexpr bool operator==(iterator const& that) const { + return pos == that.pos; + } +}; + +template +inline constexpr bool ranges::enable_borrowed_range<::move_only_range> = true; + +struct boolish { + operator bool() const { + return true; + } + + boolish operator!() const { + return *this; + } +}; + +template +struct test_iterator { + template + static constexpr bool exactly = std::is_same_v; + template + static constexpr bool at_least = std::derived_from; + + struct reference { + operator ValueType() const requires at_least { + return {}; + } + void operator=(ValueType const&) const {} + }; + + // output iterator operations + test_iterator() = default; + test_iterator(test_iterator&&) = default; + test_iterator& operator=(test_iterator&&) = default; + + reference operator*() const { + return {}; + } + ValueType& operator*() const requires at_least { + static ValueType value{}; + return value; + } + + friend boolish operator==(test_iterator const&, std::default_sentinel_t const&) { + return {}; + } + friend boolish operator==(std::default_sentinel_t const&, test_iterator const&) { + return {}; + } + friend boolish operator!=(test_iterator const&, std::default_sentinel_t const&) { + return {}; + } + friend boolish operator!=(std::default_sentinel_t const&, test_iterator const&) { + return {}; + } + + test_iterator& operator++() & { + return *this; + } + test_iterator operator++(int) & { + return {}; + } + + auto operator--() & { + STATIC_ASSERT(always_false); + } + auto operator--(int) & { + STATIC_ASSERT(always_false); + } + + friend void iter_move(test_iterator const&) { + STATIC_ASSERT(always_false); + } + friend void iter_swap(test_iterator const&, test_iterator const&) { + STATIC_ASSERT(always_false); + } + void operator<(test_iterator const&) const { + STATIC_ASSERT(always_false); + } + void operator>(test_iterator const&) const { + STATIC_ASSERT(always_false); + } + void operator<=(test_iterator const&) const { + STATIC_ASSERT(always_false); + } + void operator>=(test_iterator const&) const { + STATIC_ASSERT(always_false); + } + + void operator&() const { + STATIC_ASSERT(always_false); + } + template + friend void operator,(test_iterator const&, T&&) { + STATIC_ASSERT(always_false); + } + + // input iterator operations: + void operator++(int) & requires exactly {} + friend ValueType iter_move(test_iterator const&) requires at_least { + return {}; + } + friend void iter_swap(test_iterator const&, test_iterator const&) requires at_least {} + + // forward iterator operations: + test_iterator(test_iterator const&) requires at_least = default; + test_iterator& operator=(test_iterator const&) requires at_least = default; + test_iterator operator++(int) & requires at_least {} + boolish operator==(test_iterator const&) const requires at_least { + return {}; + } + boolish operator!=(test_iterator const&) const requires at_least { + return {}; + } + + // bidirectional iterator operations: + test_iterator& operator--() & requires at_least { + return *this; + } + test_iterator operator--(int) & requires at_least {} + + // random-access iterator operations: + boolish operator<(test_iterator const&) const requires at_least { + return {}; + } + boolish operator>(test_iterator const&) const requires at_least { + return {}; + } + boolish operator<=(test_iterator const&) const requires at_least { + return {}; + } + boolish operator>=(test_iterator const&) const requires at_least { + return {}; + } + decltype(auto) operator[](std::ptrdiff_t) const& requires at_least { + return **this; + } + test_iterator& operator+=(std::ptrdiff_t) & requires at_least { + return *this; + } + test_iterator& operator-=(std::ptrdiff_t) & requires at_least { + return *this; + } + test_iterator operator+(std::ptrdiff_t) const requires at_least { + return *this; + } + friend test_iterator operator+( + std::ptrdiff_t, test_iterator const& i) requires at_least { + return i; + } + test_iterator operator-(std::ptrdiff_t) const requires at_least { + return *this; + } + + // sized_sentinel_for operations: + std::ptrdiff_t operator-(test_iterator const&) const requires Sized || at_least { + return 42; + } + friend std::ptrdiff_t operator-(std::default_sentinel_t, test_iterator const&) requires Sized { + return 42; + } + friend std::ptrdiff_t operator-(test_iterator const&, std::default_sentinel_t) requires Sized { + return -42; + } +}; + +template +struct std::iterator_traits<::test_iterator> { + using iterator_concept = Category; + using iterator_category = Category; // TRANSITION, LWG-3289 + using value_type = ValueType; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = iter_reference_t<::test_iterator>; +}; + +template +struct std::pointer_traits<::test_iterator> { + using pointer = ::test_iterator; + using element_type = ValueType; + using difference_type = ptrdiff_t; + + [[nodiscard]] static constexpr element_type* to_address(pointer) noexcept { + return nullptr; + } +}; + +STATIC_ASSERT(std::output_iterator, int>); +STATIC_ASSERT(std::input_iterator>); +STATIC_ASSERT(std::forward_iterator>); +STATIC_ASSERT(std::bidirectional_iterator>); +STATIC_ASSERT(std::random_access_iterator>); +STATIC_ASSERT(std::contiguous_iterator>); + +STATIC_ASSERT(std::output_iterator, int>); +STATIC_ASSERT(std::input_iterator>); +STATIC_ASSERT(std::forward_iterator>); +STATIC_ASSERT(std::bidirectional_iterator>); +STATIC_ASSERT(std::random_access_iterator>); +STATIC_ASSERT(std::contiguous_iterator>); + +STATIC_ASSERT(std::sized_sentinel_for>); +STATIC_ASSERT(std::sized_sentinel_for>); +STATIC_ASSERT(std::sized_sentinel_for>); +STATIC_ASSERT( + std::sized_sentinel_for>); +STATIC_ASSERT( + std::sized_sentinel_for>); +STATIC_ASSERT(std::sized_sentinel_for>); + +STATIC_ASSERT(std::sized_sentinel_for, + test_iterator>); +STATIC_ASSERT(std::sized_sentinel_for, + test_iterator>); +STATIC_ASSERT(std::sized_sentinel_for, + test_iterator>); +STATIC_ASSERT(std::sized_sentinel_for, + test_iterator>); + +template +struct test_range { + using I = test_iterator; + using S = std::conditional_t, I, + std::default_sentinel_t>; + + I begin() const { + return {}; + } + + S end() const { + return {}; + } + + std::ptrdiff_t size() const requires Sized { + return 42; + } + + ValueType* data() const requires std::derived_from { + return nullptr; + } + + void operator&() const { + STATIC_ASSERT(always_false); + } + template + friend void operator,(test_range const&, T&&) { + STATIC_ASSERT(always_false); + } +}; + +STATIC_ASSERT(ranges::output_range, int>); +STATIC_ASSERT(ranges::input_range>); +STATIC_ASSERT(ranges::forward_range>); +STATIC_ASSERT(ranges::bidirectional_range>); +STATIC_ASSERT(ranges::random_access_range>); +STATIC_ASSERT(ranges::contiguous_range>); + +STATIC_ASSERT(ranges::output_range, int>); +STATIC_ASSERT(ranges::input_range>); +STATIC_ASSERT(ranges::forward_range>); +STATIC_ASSERT(ranges::bidirectional_range>); +STATIC_ASSERT(ranges::random_access_range>); +STATIC_ASSERT(ranges::contiguous_range>); +STATIC_ASSERT(ranges::sized_range>); +STATIC_ASSERT(ranges::sized_range>); +STATIC_ASSERT(ranges::sized_range>); +STATIC_ASSERT(ranges::sized_range>); +STATIC_ASSERT(ranges::sized_range>); +STATIC_ASSERT(ranges::sized_range>); + +STATIC_ASSERT(ranges::forward_range>); +STATIC_ASSERT(ranges::bidirectional_range>); +STATIC_ASSERT(ranges::random_access_range>); +STATIC_ASSERT(ranges::contiguous_range>); +STATIC_ASSERT(ranges::sized_range>); +STATIC_ASSERT(ranges::sized_range>); +STATIC_ASSERT(ranges::sized_range>); +STATIC_ASSERT(ranges::sized_range>); + +template +struct unique_tag {}; + +template +using ProjectionFor = unique_tag (*)(std::iter_common_reference_t); + +template +using UnaryPredicateFor = boolish (*)(std::iter_common_reference_t); + +template +using ProjectedUnaryPredicate = boolish (*)(unique_tag); +template +using HalfProjectedBinaryPredicateFor = boolish (*)(unique_tag, std::iter_common_reference_t); +template +using ProjectedBinaryPredicate = boolish (*)(unique_tag, unique_tag); + +template +using BinaryPredicateFor = boolish (*)(std::iter_common_reference_t, std::iter_common_reference_t); + +template +struct with_output_iterators { + template + static void call() { + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + } +}; + +template +struct with_input_ranges { + template + static void call() { + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + } +}; + +template +struct with_input_iterators { + template + static void call() { + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + } +}; + +template +struct with_difference { + template + static void call() { + Continuation::template call>(); + } +}; + +template +void test_out() { + with_output_iterators::call(); +} + +template +void test_in() { + with_input_ranges::call(); +} + +template +void test_in_in() { + with_input_ranges>::call(); +} + +template +void test_in_out() { + with_input_ranges>::call(); +} + +template +void test_counted_out() { + with_input_iterators>>::call(); +} diff --git a/tests/std/test.lst b/tests/std/test.lst index 721b32266..fc678ca34 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -225,6 +225,22 @@ tests\P0769R2_shift_left_shift_right tests\P0784R7_library_support_for_more_constexpr_containers tests\P0811R3_midpoint_lerp tests\P0896R4_P1614R2_comparisons +tests\P0896R4_ranges_algorithm_machinery +tests\P0896R4_ranges_alg_all_of +tests\P0896R4_ranges_alg_any_of +tests\P0896R4_ranges_alg_copy +tests\P0896R4_ranges_alg_copy_if +tests\P0896R4_ranges_alg_copy_n +tests\P0896R4_ranges_alg_count +tests\P0896R4_ranges_alg_count_if +tests\P0896R4_ranges_alg_equal +tests\P0896R4_ranges_alg_find +tests\P0896R4_ranges_alg_find_if +tests\P0896R4_ranges_alg_find_if_not +tests\P0896R4_ranges_alg_for_each +tests\P0896R4_ranges_alg_for_each_n +tests\P0896R4_ranges_alg_mismatch +tests\P0896R4_ranges_alg_none_of tests\P0896R4_ranges_iterator_machinery tests\P0896R4_ranges_range_machinery tests\P0896R4_ranges_to_address diff --git a/tests/std/tests/P0122R7_span/test.cpp b/tests/std/tests/P0122R7_span/test.cpp index 6743d84cb..7d7b8cb9a 100644 --- a/tests/std/tests/P0122R7_span/test.cpp +++ b/tests/std/tests/P0122R7_span/test.cpp @@ -75,8 +75,8 @@ static_assert(is_same_v::iterator>::pointer, static_assert(is_same_v::reverse_iterator, reverse_iterator::iterator>>); #ifdef __cpp_lib_concepts -static_assert(ranges::enable_safe_range>); -static_assert(ranges::enable_safe_range>); +static_assert(ranges::enable_borrowed_range>); +static_assert(ranges::enable_borrowed_range>); #endif // __cpp_lib_concepts static_assert(is_base_of_v, tuple_size>>); @@ -106,7 +106,7 @@ static_assert(!is_convertible_v); struct NonRange {}; -template +template struct BasicRange { T elements[3]{}; @@ -141,15 +141,15 @@ struct BasicRange { #ifdef __cpp_lib_concepts namespace std::ranges { - template - inline constexpr bool enable_safe_range> = Safe; + template + inline constexpr bool enable_borrowed_range> = Borrowed; } #endif // __cpp_lib_concepts using ContiguousSizedRange = BasicRange; -// Not truly a model of safe_range; this is a convenient fiction for testing purposes. -using SafeContiguousSizedRange = BasicRange; +// Not truly a model of borrowed_range; this is a convenient fiction for testing purposes. +using BorrowedContiguousSizedRange = BasicRange; template constexpr void FunctionTakingSpan(type_identity_t>) {} @@ -591,24 +591,24 @@ constexpr bool test() { #ifdef __cpp_lib_concepts static_assert(!is_constructible_v, ContiguousSizedRange>); - static_assert(is_constructible_v, SafeContiguousSizedRange>); + static_assert(is_constructible_v, BorrowedContiguousSizedRange>); static_assert(is_constructible_v, ContiguousSizedRange>); - static_assert(is_convertible_v>); + static_assert(is_convertible_v>); static_assert(is_convertible_v>); - SafeContiguousSizedRange safe_user_range; + BorrowedContiguousSizedRange borrowed_user_range; - span sp_4(move(safe_user_range)); + span sp_4(move(borrowed_user_range)); span sp_5(move(user_range)); - assert(sp_4.data() == safe_user_range.data()); + assert(sp_4.data() == borrowed_user_range.data()); assert(sp_5.data() == user_range.data()); assert(sp_4.size() == 3); assert(sp_5.size() == 3); - FunctionTakingSpan(move(safe_user_range)); + FunctionTakingSpan(move(borrowed_user_range)); FunctionTakingSpan(move(user_range)); static_assert(is_same_v>); diff --git a/tests/std/tests/P0896R4_ranges_alg_all_of/env.lst b/tests/std/tests/P0896R4_ranges_alg_all_of/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_all_of/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_all_of/test.cpp b/tests/std/tests/P0896R4_ranges_alg_all_of/test.cpp new file mode 100644 index 000000000..2eb87d544 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_all_of/test.cpp @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +// +#include + +inline constexpr auto is_even = [](auto const& x) { return x % 2 == 0; }; +inline constexpr auto is_odd = [](auto const& x) { return x % 2 != 0; }; + +using R = std::array, 3>; + +constexpr void smoke_test() { + using ranges::all_of; + constexpr R pairs = {{{0, 13}, {2, 13}, {4, 13}}}; + + assert(all_of(move_only_range{pairs}, is_even, get_first)); + assert(!all_of(move_only_range{pairs}, is_even, get_second)); + assert(!all_of(move_only_range{pairs}, is_odd, get_first)); + assert(all_of(move_only_range{pairs}, is_odd, get_second)); + { + move_only_range wrapped_pairs{pairs}; + assert(all_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_even, get_first)); + } + { + move_only_range wrapped_pairs{pairs}; + assert(!all_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_even, get_second)); + } + { + move_only_range wrapped_pairs{pairs}; + assert(!all_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_odd, get_first)); + } + { + move_only_range wrapped_pairs{pairs}; + assert(all_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_odd, get_second)); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}) { + (void) ranges::all_of(in, UnaryPredicateFor>{}); + (void) ranges::all_of(in, ProjectedUnaryPredicate<>{}, ProjectionFor>{}); + } +}; + +template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_any_of/env.lst b/tests/std/tests/P0896R4_ranges_alg_any_of/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_any_of/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_any_of/test.cpp b/tests/std/tests/P0896R4_ranges_alg_any_of/test.cpp new file mode 100644 index 000000000..d4b4a4b1c --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_any_of/test.cpp @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +// +#include + +constexpr auto is_even = [](auto const& x) { return x % 2 == 0; }; +constexpr auto is_odd = [](auto const& x) { return x % 2 != 0; }; + +constexpr void smoke_test() { + using ranges::any_of; + constexpr std::array, 3> pairs = {{{0, 13}, {7, 13}, {4, 13}}}; + + assert(any_of(move_only_range{pairs}, is_even, get_first)); + assert(!any_of(move_only_range{pairs}, is_even, get_second)); + assert(any_of(move_only_range{pairs}, is_odd, get_first)); + assert(any_of(move_only_range{pairs}, is_odd, get_second)); + { + move_only_range wrapped_pairs{pairs}; + assert(any_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_even, get_first)); + } + { + move_only_range wrapped_pairs{pairs}; + assert(!any_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_even, get_second)); + } + { + move_only_range wrapped_pairs{pairs}; + assert(any_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_odd, get_first)); + } + { + move_only_range wrapped_pairs{pairs}; + assert(any_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_odd, get_second)); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}) { + (void) ranges::any_of(in, UnaryPredicateFor>{}); + (void) ranges::any_of(in, ProjectedUnaryPredicate<>{}, ProjectionFor>{}); + } +}; + +template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_copy/env.lst b/tests/std/tests/P0896R4_ranges_alg_copy/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_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_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_copy/test.cpp new file mode 100644 index 000000000..567e7c90a --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_copy/test.cpp @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::copy, ranges::copy_result, ranges::iterator_t; + using std::same_as; + + // Validate that copy_result aliases in_out_result + STATIC_ASSERT(same_as, ranges::in_out_result>); + + // Validate dangling story + STATIC_ASSERT( + same_as{}, static_cast(nullptr))), copy_result>); + STATIC_ASSERT(same_as{}, static_cast(nullptr))), copy_result>); + + int const input[] = {13, 42, 1729}; + { // Validate range overload + int output[] = {-1, -1, -1}; + auto result = copy(move_only_range{input}, move_only_range{output}.begin()); + STATIC_ASSERT(same_as>, iterator_t>>>); + assert(result.in == move_only_range{input}.end()); + assert(result.out == move_only_range{output}.end()); + assert(ranges::equal(output, input)); + } + { // Validate iterator + sentinel overload + int output[] = {-1, -1, -1}; + move_only_range wrapped_input{input}; + auto result = copy(wrapped_input.begin(), wrapped_input.end(), move_only_range{output}.begin()); + STATIC_ASSERT(same_as>, iterator_t>>>); + assert(result.in == wrapped_input.end()); + assert(result.out == move_only_range{output}.end()); + assert(ranges::equal(output, input)); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}, Out out = {}) { + (void) ranges::copy(in, std::move(out)); + (void) ranges::copy(ranges::begin(in), ranges::end(in), std::move(out)); + } +}; + +template void test_in_out(); diff --git a/tests/std/tests/P0896R4_ranges_alg_copy_if/env.lst b/tests/std/tests/P0896R4_ranges_alg_copy_if/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_copy_if/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_copy_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_copy_if/test.cpp new file mode 100644 index 000000000..032cfafcc --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_copy_if/test.cpp @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +// +#include + +template +struct not_pair { + T first; + U second; + + auto operator<=>(const not_pair&) const = default; +}; + +constexpr void smoke_test() { + using ranges::copy_if, ranges::copy_if_result, ranges::iterator_t; + using std::same_as; + using P = not_pair; + + constexpr auto is_odd = [](int x) { return x % 2 != 0; }; + + // Validate that copy_if_result aliases in_out_result + STATIC_ASSERT(same_as, ranges::in_out_result>); + + // Validate dangling story + STATIC_ASSERT(same_as{}, static_cast(nullptr), is_odd)), + copy_if_result>); + STATIC_ASSERT( + same_as{}, static_cast(nullptr), is_odd)), copy_if_result>); + + std::array const input = {{{1, 99}, {4, 98}, {5, 97}}}; + std::array const expected = {{{1, 99}, {5, 97}}}; + using I = iterator_t>; + using O = iterator_t>; + { // Validate range overload + std::array output = {}; + auto result = copy_if(move_only_range{input}, move_only_range{output}.begin(), is_odd, get_first); + STATIC_ASSERT(same_as>); + assert(result.in == move_only_range{input}.end()); + assert(result.out == move_only_range{output}.end()); + assert(ranges::equal(output, expected)); + } + { // Validate iterator + sentinel overload + std::array output = {}; + move_only_range wrapped_input{input}; + auto result = + copy_if(wrapped_input.begin(), wrapped_input.end(), move_only_range{output}.begin(), is_odd, get_first); + STATIC_ASSERT(same_as>); + assert(result.in == wrapped_input.end()); + assert(result.out == move_only_range{output}.end()); + assert(ranges::equal(output, expected)); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}, Out out = {}) { + using ranges::begin, ranges::copy_if, ranges::end, ranges::iterator_t; + + constexpr UnaryPredicateFor> pred{}; + constexpr ProjectionFor> proj{}; + + copy_if(in, std::move(out), pred); + copy_if(begin(in), end(in), std::move(out), pred); + copy_if(in, std::move(out), ProjectedUnaryPredicate<>{}, proj); + copy_if(begin(in), end(in), std::move(out), ProjectedUnaryPredicate<>{}, proj); + } +}; + +template void test_in_out(); diff --git a/tests/std/tests/P0896R4_ranges_alg_copy_n/env.lst b/tests/std/tests/P0896R4_ranges_alg_copy_n/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_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_copy_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_copy_n/test.cpp new file mode 100644 index 000000000..1be54b7b0 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_copy_n/test.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::copy_n, ranges::copy_n_result, ranges::iterator_t; + using std::same_as; + + // Validate that copy_n_result aliases in_out_result + STATIC_ASSERT(same_as, ranges::in_out_result>); + + int const input[] = {13, 42, 1729}; + int output[] = {-1, -1, -1}; + move_only_range wrapped_input{input}; + auto result = copy_n(wrapped_input.begin(), ranges::distance(input), move_only_range{output}.begin()); + STATIC_ASSERT(same_as>, iterator_t>>>); + assert(result.in == wrapped_input.end()); + assert(result.out == move_only_range{output}.end()); + assert(ranges::equal(output, input)); +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In in = {}, std::iter_difference_t const count = 42, Out out = {}) { + (void) ranges::copy_n(std::move(in), count, std::move(out)); + } +}; + +template void test_counted_out(); diff --git a/tests/std/tests/P0896R4_ranges_alg_count/env.lst b/tests/std/tests/P0896R4_ranges_alg_count/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_count/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_count/test.cpp b/tests/std/tests/P0896R4_ranges_alg_count/test.cpp new file mode 100644 index 000000000..2eb1f0709 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_count/test.cpp @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::count; + using P = std::pair; + std::array const x = {{{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}}; + using D = ranges::range_difference_t>; + + { + // Validate range overload + auto result = count(move_only_range{x}, 99, get_second); + STATIC_ASSERT(std::same_as); + assert(result == 3); + } + { + // Validate iterator + sentinel overload + move_only_range wrapped_x{x}; + auto result = count(wrapped_x.begin(), wrapped_x.end(), 47, get_second); + STATIC_ASSERT(std::same_as); + assert(result == 2); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}) { + ranges::range_value_t const value{}; + (void) ranges::count(in, value); + + struct type { + bool operator==(type const&) const = default; + }; + using Projection = type (*)(std::iter_common_reference_t>); + (void) ranges::count(in, type{}, Projection{}); + } +}; + +template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_count_if/env.lst b/tests/std/tests/P0896R4_ranges_alg_count_if/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_count_if/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_count_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_count_if/test.cpp new file mode 100644 index 000000000..404dfc4b4 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_count_if/test.cpp @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +// +#include + +constexpr auto is_even = [](auto const& x) { return x % 2 == 0; }; +constexpr auto is_odd = [](auto const& x) { return x % 2 != 0; }; + +constexpr void smoke_test() { + using ranges::count_if; + using P = std::pair; + std::array const x = {{{0, 47}, {1, 99}, {2, 99}, {3, 47}, {4, 99}}}; + using D = ranges::range_difference_t>; + + { + // Validate range overload + auto result = count_if(move_only_range{x}, is_even, get_first); + STATIC_ASSERT(std::same_as); + assert(result == 3); + } + { + // Validate iterator + sentinel overload + move_only_range wrapped_x{x}; + auto result = count_if(wrapped_x.begin(), wrapped_x.end(), is_odd, get_first); + STATIC_ASSERT(std::same_as); + assert(result == 2); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}) { + using ranges::iterator_t; + using I = iterator_t; + + (void) ranges::count_if(in, UnaryPredicateFor{}); + (void) ranges::count_if(in, ProjectedUnaryPredicate<>{}, ProjectionFor{}); + } +}; + +template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_equal/env.lst b/tests/std/tests/P0896R4_ranges_alg_equal/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_equal/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_equal/test.cpp b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp new file mode 100644 index 000000000..b641e2fc5 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::equal, ranges::equal_to; + using std::abort, std::array, std::pair, std::same_as; + + array, 3> const x = {{{0, 42}, {2, 42}, {4, 42}}}; + array, 3> const y = {{{13, -1}, {13, 1}, {13, 3}}}; + + constexpr auto cmp = [](auto&& x, auto&& y) { return x == y + 1; }; + + { + // Validate sized ranges + auto result = equal(x, y, cmp, get_first, get_second); + STATIC_ASSERT(same_as); + assert(result); + assert(!equal(x, y, cmp, get_first, get_first)); + } + { + // Validate non-sized ranges + auto result = equal(move_only_range{x}, move_only_range{y}, cmp, get_first, get_second); + STATIC_ASSERT(same_as); + assert(result); + assert(!equal(move_only_range{x}, move_only_range{y}, cmp, get_first, get_first)); + } + { + // Validate sized iterator + sentinel pairs + auto result = equal(x.begin(), x.end(), y.begin(), y.end(), cmp, get_first, get_second); + STATIC_ASSERT(same_as); + assert(result); + assert(!equal(x.begin(), x.end(), y.begin(), y.end(), cmp, get_first, get_first)); + } + { + // Validate non-sized iterator + sentinel pairs + move_only_range wrapped_x{x}; + move_only_range wrapped_y{y}; + auto result = + equal(wrapped_x.begin(), wrapped_x.end(), wrapped_y.begin(), wrapped_y.end(), cmp, get_first, get_second); + STATIC_ASSERT(same_as); + assert(result); + wrapped_x = move_only_range{x}; + wrapped_y = move_only_range{y}; + assert( + !equal(wrapped_x.begin(), wrapped_x.end(), wrapped_y.begin(), wrapped_y.end(), cmp, get_first, get_first)); + } + { + // calls with sized ranges of differing size perform no comparisons nor projections + constexpr auto proj = [](auto &&) -> int { abort(); }; + constexpr auto comp = [](auto&&, auto &&) -> bool { abort(); }; + int const one_int[] = {0}; + int const two_ints[] = {0, 1}; + assert(!equal(one_int, two_ints, comp, proj, proj)); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In1&& in1 = {}, In2&& in2 = {}) { + using ranges::begin, ranges::end, ranges::equal, ranges::iterator_t; + + if constexpr (!is_permissive) { + (void) equal(in1, in2); + (void) equal(begin(in1), end(in1), begin(in2), end(in2)); + } + + BinaryPredicateFor, iterator_t> pred{}; + (void) equal(in1, in2, pred); + (void) equal(begin(in1), end(in1), begin(in2), end(in2), pred); + + HalfProjectedBinaryPredicateFor> halfpred{}; + ProjectionFor> halfproj{}; + (void) equal(in1, in2, halfpred, halfproj); + (void) equal(begin(in1), end(in1), begin(in2), end(in2), halfpred, halfproj); + + ProjectedBinaryPredicate<0, 1> projpred{}; + ProjectionFor, 0> proj1{}; + ProjectionFor, 1> proj2{}; + (void) equal(in1, in2, projpred, proj1, proj2); + (void) equal(begin(in1), end(in1), begin(in2), end(in2), projpred, proj1, proj2); + } +}; + +template void test_in_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_find/env.lst b/tests/std/tests/P0896R4_ranges_alg_find/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_find/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_find/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp new file mode 100644 index 000000000..d286f85d9 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::find, ranges::iterator_t, std::same_as; + using P = std::pair; + + // Validate dangling story + STATIC_ASSERT(same_as{}, 42)), ranges::dangling>); + STATIC_ASSERT(same_as{}, 42)), int*>); + + std::array const pairs = {{{0, 42}, {2, 42}, {4, 42}}}; + + for (auto [value, _] : pairs) { + { + // Validate range overload [found case] + auto result = find(move_only_range{pairs}, value, get_first); + STATIC_ASSERT(same_as>>); + assert((*result).first == value); + } + { + // Validate iterator + sentinel overload [found case] + move_only_range wrapped_pairs{pairs}; + auto result = find(wrapped_pairs.begin(), wrapped_pairs.end(), value, get_first); + STATIC_ASSERT(same_as>>); + assert((*result).first == value); + } + } + { + // Validate range overload [not found case] + auto result = find(move_only_range{pairs}, 42, get_first); + STATIC_ASSERT(same_as>>); + assert(result == move_only_range{pairs}.end()); + } + { + // Validate iterator + sentinel overload [not found case] + move_only_range wrapped_pairs{pairs}; + auto result = find(wrapped_pairs.begin(), wrapped_pairs.end(), 42, get_first); + STATIC_ASSERT(same_as>>); + assert(result == wrapped_pairs.end()); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}) { + ranges::range_value_t const value{}; + (void) ranges::find(in, value); + + struct type { + bool operator==(type const&) const = default; + }; + using Projection = type (*)(std::iter_common_reference_t>); + (void) ranges::find(in, type{}, Projection{}); + }; +}; + +template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_find_if/env.lst b/tests/std/tests/P0896R4_ranges_alg_find_if/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_find_if/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_find_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find_if/test.cpp new file mode 100644 index 000000000..55a19dde8 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_find_if/test.cpp @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::find_if, ranges::iterator_t, std::same_as; + using P = std::pair; + + std::array const pairs = {{{0, 42}, {2, 42}, {4, 42}}}; + constexpr auto equals = [](auto x) { return [x](auto&& y) { return y == x; }; }; + + // Validate dangling story + STATIC_ASSERT(same_as{}, equals(42))), ranges::dangling>); + STATIC_ASSERT(same_as{}, equals(42))), int*>); + + for (auto [value, _] : pairs) { + { + // Validate range overload [found case] + auto result = find_if(move_only_range{pairs}, equals(value), get_first); + STATIC_ASSERT(same_as>>); + assert((*result).first == value); + } + { + // Validate iterator + sentinel overload [found case] + move_only_range wrapped_pairs{pairs}; + auto result = find_if(wrapped_pairs.begin(), wrapped_pairs.end(), equals(value), get_first); + STATIC_ASSERT(same_as>>); + assert((*result).first == value); + } + } + { + // Validate range overload [not found case] + auto result = find_if(move_only_range{pairs}, equals(42), get_first); + STATIC_ASSERT(same_as>>); + assert(result == move_only_range{pairs}.end()); + } + { + // Validate iterator + sentinel overload [not found case] + move_only_range wrapped_pairs{pairs}; + auto result = find_if(wrapped_pairs.begin(), wrapped_pairs.end(), equals(42), get_first); + STATIC_ASSERT(same_as>>); + assert(result == wrapped_pairs.end()); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}) { + using ranges::iterator_t; + using I = iterator_t; + + (void) ranges::find_if(in, UnaryPredicateFor{}); + (void) ranges::find_if(in, ProjectedUnaryPredicate<>{}, ProjectionFor{}); + } +}; + +template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_find_if_not/env.lst b/tests/std/tests/P0896R4_ranges_alg_find_if_not/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_find_if_not/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_find_if_not/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find_if_not/test.cpp new file mode 100644 index 000000000..f8d61ef29 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_find_if_not/test.cpp @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::find_if_not, ranges::iterator_t, std::same_as; + using P = std::pair; + + std::array pairs = {{{0, 13}, {0, 13}, {0, 13}}}; + constexpr auto equals = [](auto x) { return [x](auto&& y) { return y == x; }; }; + + // Validate dangling story + STATIC_ASSERT(same_as{}, equals(42))), ranges::dangling>); + STATIC_ASSERT(same_as{}, equals(42))), int*>); + + for (auto& [value, _] : pairs) { + value = 42; + { + // Validate range overload [found case] + auto result = find_if_not(move_only_range{pairs}, equals(0), get_first); + STATIC_ASSERT(same_as>>); + assert((*result).first == 42); + } + { + // Validate iterator + sentinel overload [found case] + move_only_range wrapped_pairs{pairs}; + auto result = find_if_not(wrapped_pairs.begin(), wrapped_pairs.end(), equals(0), get_first); + STATIC_ASSERT(same_as>>); + assert((*result).first == 42); + } + value = 0; + } + { + // Validate range overload [not found case] + auto result = find_if_not(move_only_range{pairs}, equals(0), get_first); + STATIC_ASSERT(same_as>>); + assert(result == move_only_range{pairs}.end()); + } + { + // Validate iterator + sentinel overload [not found case] + move_only_range wrapped_pairs{pairs}; + auto result = find_if_not(wrapped_pairs.begin(), wrapped_pairs.end(), equals(0), get_first); + STATIC_ASSERT(same_as>>); + assert(result == wrapped_pairs.end()); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}) { + using ranges::iterator_t; + using I = iterator_t; + + (void) ranges::find_if_not(in, UnaryPredicateFor{}); + (void) ranges::find_if_not(in, ProjectedUnaryPredicate<>{}, ProjectionFor{}); + } +}; + +template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_for_each/env.lst b/tests/std/tests/P0896R4_ranges_alg_for_each/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_for_each/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_for_each/test.cpp b/tests/std/tests/P0896R4_ranges_alg_for_each/test.cpp new file mode 100644 index 000000000..b570ea6f2 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_for_each/test.cpp @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::for_each, ranges::for_each_result, ranges::in_fun_result, ranges::iterator_t; + using std::identity, std::same_as; + using P = std::pair; + using R = std::array; + + // Validate that for_each_result aliases in_fun_result + STATIC_ASSERT(same_as, in_fun_result>); + + // Validate dangling story + STATIC_ASSERT( + same_as{}, identity{})), for_each_result>); + STATIC_ASSERT(same_as{}, identity{})), for_each_result>); + + R const values = {{{0, 42}, {2, 42}, {4, 42}}}; + auto incr = [](auto& y) { ++y; }; + + { + auto pairs = values; + auto result = for_each(move_only_range{pairs}, incr, get_first); + STATIC_ASSERT(same_as>, decltype(incr)>>); + assert(result.in == move_only_range{pairs}.end()); + int some_value = 1729; + result.fun(some_value); + assert(some_value == 1730); + R const expected = {{{1, 42}, {3, 42}, {5, 42}}}; + assert(ranges::equal(pairs, expected)); + } + { + auto pairs = values; + move_only_range wrapped_pairs{pairs}; + auto result = for_each(wrapped_pairs.begin(), wrapped_pairs.end(), incr, get_second); + STATIC_ASSERT(same_as>, decltype(incr)>>); + assert(result.in == wrapped_pairs.end()); + int some_value = 1729; + result.fun(some_value); + assert(some_value == 1730); + R const expected = {{{0, 43}, {2, 43}, {4, 43}}}; + assert(ranges::equal(pairs, expected)); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}) { + using I = ranges::iterator_t; + using Fun = void (*)(std::iter_common_reference_t); + ranges::for_each(in, Fun{}); + ranges::for_each(ranges::begin(in), ranges::end(in), Fun{}); + + using ProjFun = void (*)(unique_tag<0>); + ranges::for_each(in, ProjFun{}, ProjectionFor{}); + ranges::for_each(ranges::begin(in), ranges::end(in), ProjFun{}, ProjectionFor{}); + } +}; + +template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_for_each_n/env.lst b/tests/std/tests/P0896R4_ranges_alg_for_each_n/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_for_each_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_for_each_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_for_each_n/test.cpp new file mode 100644 index 000000000..9f5f824a6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_for_each_n/test.cpp @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::for_each_n, ranges::for_each_n_result, ranges::in_fun_result, ranges::iterator_t; + using std::same_as; + using P = std::pair; + using R = std::array; + + // Validate that for_each_n_result aliases in_fun_result + STATIC_ASSERT(same_as, in_fun_result>); + + R pairs = {{{0, 42}, {2, 42}, {4, 42}}}; + auto incr = [](auto& y) { ++y; }; + + move_only_range wrapped_pairs{pairs}; + auto result = for_each_n(wrapped_pairs.begin(), ranges::distance(pairs), incr, get_first); + STATIC_ASSERT(same_as>, decltype(incr)>>); + assert(result.in == wrapped_pairs.end()); + int some_value = 1729; + result.fun(some_value); + assert(some_value == 1730); + R const expected = {{{1, 42}, {3, 42}, {5, 42}}}; + assert(ranges::equal(pairs, expected)); +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In in = {}) { + using std::iter_difference_t; + + using Fun = void (*)(std::iter_common_reference_t); + ranges::for_each_n(std::move(in), iter_difference_t{}, Fun{}); + + using ProjFun = void (*)(unique_tag<0>); + ranges::for_each_n(std::move(in), iter_difference_t{}, ProjFun{}, ProjectionFor{}); + } +}; + +template void test_counted_out(); diff --git a/tests/std/tests/P0896R4_ranges_alg_mismatch/env.lst b/tests/std/tests/P0896R4_ranges_alg_mismatch/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_mismatch/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_mismatch/test.cpp b/tests/std/tests/P0896R4_ranges_alg_mismatch/test.cpp new file mode 100644 index 000000000..509db6241 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_mismatch/test.cpp @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::dangling, ranges::equal_to, ranges::iterator_t, ranges::mismatch, ranges::mismatch_result; + using std::same_as; + using P = std::pair; + using R = std::array; + + R const x = {{{0, 42}, {2, 42}, {4, 42}}}; + std::array, 3> const y = {{{13, 0}, {13, 2}, {13, 5}}}; + + // Validate that mismatch_result aliases in_in_result + STATIC_ASSERT(same_as, ranges::in_in_result>); + + // Validate dangling story + STATIC_ASSERT( + same_as{}, borrowed{})), mismatch_result>); + STATIC_ASSERT(same_as{}, borrowed{})), mismatch_result>); + STATIC_ASSERT(same_as{}, borrowed{})), mismatch_result>); + STATIC_ASSERT(same_as{}, borrowed{})), mismatch_result>); + + { + // Validate sized ranges + auto result = mismatch(x, y, equal_to{}, get_first, get_second); + using I1 = iterator_t; + using I2 = std::array, 3>::const_iterator; + STATIC_ASSERT(same_as>); + assert((*result.in1 == P{4, 42})); + assert((*result.in2 == std::pair{13, 5})); + } + { + // Validate non-sized ranges + auto result = mismatch(move_only_range{x}, move_only_range{y}, equal_to{}, get_first, get_second); + using I1 = iterator_t>; + using I2 = iterator_t const>>; + STATIC_ASSERT(same_as>); + assert((*result.in1 == P{4, 42})); + assert((*result.in2 == std::pair{13, 5})); + } + { + // Validate sized iterator + sentinel pairs + auto result = mismatch(x.begin(), x.end(), y.begin(), y.end(), equal_to{}, get_first, get_second); + using I1 = iterator_t; + using I2 = std::array, 3>::const_iterator; + STATIC_ASSERT(same_as>); + assert((*result.in1 == P{4, 42})); + assert((*result.in2 == std::pair{13, 5})); + } + { + // Validate non-sized iterator + sentinel pairs + move_only_range wrapped_x{x}; + move_only_range wrapped_y{y}; + auto result = mismatch( + wrapped_x.begin(), wrapped_x.end(), wrapped_y.begin(), wrapped_y.end(), equal_to{}, get_first, get_second); + using I1 = iterator_t; + using I2 = iterator_t; + STATIC_ASSERT(same_as>); + assert((*result.in1 == P{4, 42})); + assert((*result.in2 == std::pair{13, 5})); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In1&& in1 = {}, In2&& in2 = {}) { + using ranges::begin, ranges::end, ranges::mismatch, ranges::iterator_t; + + if constexpr (!is_permissive) { + (void) mismatch(in1, in2); + (void) mismatch(begin(in1), end(in1), begin(in2), end(in2)); + } + + BinaryPredicateFor, iterator_t> pred{}; + (void) mismatch(in1, in2, pred); + (void) mismatch(begin(in1), end(in1), begin(in2), end(in2), pred); + + HalfProjectedBinaryPredicateFor> halfpred{}; + ProjectionFor> halfproj{}; + (void) mismatch(in1, in2, halfpred, halfproj); + (void) mismatch(begin(in1), end(in1), begin(in2), end(in2), halfpred, halfproj); + + ProjectedBinaryPredicate<0, 1> projpred{}; + ProjectionFor, 0> proj1{}; + ProjectionFor, 1> proj2{}; + (void) mismatch(in1, in2, projpred, proj1, proj2); + (void) mismatch(begin(in1), end(in1), begin(in2), end(in2), projpred, proj1, proj2); + } +}; + +template void test_in_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_none_of/env.lst b/tests/std/tests/P0896R4_ranges_alg_none_of/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_none_of/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_none_of/test.cpp b/tests/std/tests/P0896R4_ranges_alg_none_of/test.cpp new file mode 100644 index 000000000..a160dd6bb --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_none_of/test.cpp @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +// +#include + +constexpr auto is_even = [](auto const& x) { return x % 2 == 0; }; +constexpr auto is_odd = [](auto const& x) { return x % 2 != 0; }; + +constexpr void smoke_test() { + using ranges::none_of; + constexpr std::array, 3> pairs = {{{0, 13}, {7, 13}, {4, 13}}}; + + assert(!none_of(move_only_range{pairs}, is_even, get_first)); + assert(none_of(move_only_range{pairs}, is_even, get_second)); + assert(!none_of(move_only_range{pairs}, is_odd, get_first)); + assert(!none_of(move_only_range{pairs}, is_odd, get_second)); + { + move_only_range wrapped_pairs{pairs}; + assert(!none_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_even, get_first)); + } + { + move_only_range wrapped_pairs{pairs}; + assert(none_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_even, get_second)); + } + { + move_only_range wrapped_pairs{pairs}; + assert(!none_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_odd, get_first)); + } + { + move_only_range wrapped_pairs{pairs}; + assert(!none_of(wrapped_pairs.begin(), wrapped_pairs.end(), is_odd, get_second)); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In&& in = {}) { + (void) ranges::none_of(in, UnaryPredicateFor>{}); + (void) ranges::none_of(in, ProjectedUnaryPredicate<>{}, ProjectionFor>{}); + } +}; + +template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_algorithm_machinery/env.lst b/tests/std/tests/P0896R4_ranges_algorithm_machinery/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_algorithm_machinery/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_algorithm_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp new file mode 100644 index 000000000..ed03a8bfc --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp @@ -0,0 +1,524 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +namespace ranges = std::ranges; + +int main() {} // COMPILE-ONLY + +namespace detail { + static constexpr bool permissive() { + return false; + } + + template + struct DependentBase { + static constexpr bool permissive() { + return true; + } + }; + + template + struct Derived : DependentBase { + static constexpr bool test() { + return permissive(); + } + }; +} +constexpr bool is_permissive = detail::Derived::test(); + +template +struct borrowed { // borrowed is a borrowed_range; borrowed is not + int* begin() const; + int* end() const; +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range> = true; + +struct simple_reference { + operator int() const; +}; + +struct simple_common_reference { + simple_common_reference(simple_reference); + simple_common_reference(int); +}; + +template +struct simple_iter_archetype { + using value_type = int; + + // 0: not indirectly_readable + simple_reference operator*() const requires(I != 0); + + friend void iter_swap(simple_iter_archetype const&, simple_iter_archetype const&) {} +}; +STATIC_ASSERT(!std::indirectly_readable>); +STATIC_ASSERT(std::indirectly_readable>); + +template