diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -36,10 +36,10 @@ Read-only,is_permutation,Not assigned,n/a,Not started Read-only,for_each,Not assigned,n/a,Not started Read-only,for_each_n,Not assigned,n/a,Not started -Write,copy,Not assigned,n/a,Not started -Write,copy_if,Not assigned,n/a,Not started -Write,copy_n,Not assigned,n/a,Not started -Write,copy_backward,Not assigned,n/a,Not started +Write,copy,Nikolas Klauser,`D122982 `_,✅ +Write,copy_if,Nikolas Klauser,`D122982 `_,✅ +Write,copy_n,Nikolas Klauser,`D122982 `_,✅ +Write,copy_backward,Nikolas Klauser,`D122982 `_,✅ Write,move,Not assigned,n/a,Not started Write,move_backward,Not assigned,n/a,Not started Write,fill,Not assigned,n/a,Not started diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -66,6 +66,10 @@ __algorithm/pop_heap.h __algorithm/prev_permutation.h __algorithm/push_heap.h + __algorithm/ranges_copy.h + __algorithm/ranges_copy_backward.h + __algorithm/ranges_copy_if.h + __algorithm/ranges_copy_n.h __algorithm/ranges_count.h __algorithm/ranges_count_if.h __algorithm/ranges_find.h diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h --- a/libcxx/include/__algorithm/copy.h +++ b/libcxx/include/__algorithm/copy.h @@ -12,6 +12,9 @@ #include <__algorithm/unwrap_iter.h> #include <__config> #include <__iterator/iterator_traits.h> +#include <__iterator/reverse_iterator.h> +#include <__utility/move.h> +#include <__utility/pair.h> #include #include @@ -23,53 +26,74 @@ // copy -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 -_OutputIterator -__copy_constexpr(_InputIterator __first, _InputIterator __last, _OutputIterator __result) -{ - for (; __first != __last; ++__first, (void) ++__result) - *__result = *__first; - return __result; +template +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 +pair<_InIter, _OutIter> __copy_impl(_InIter __first, _Sent __last, _OutIter __result) { + while (__first != __last) { + *__result = *__first; + ++__first; + ++__result; + } + return pair<_InIter, _OutIter>(std::move(__first), std::move(__result)); } -template -inline _LIBCPP_INLINE_VISIBILITY -_OutputIterator -__copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result) -{ - return _VSTD::__copy_constexpr(__first, __last, __result); +template ::type, _OutValueT>::value + && is_trivially_copy_assignable<_OutValueT>::value> > +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 +pair<_InValueT*, _OutValueT*> __copy_impl(_InValueT* __first, _InValueT* __last, _OutValueT* __result) { + if (__libcpp_is_constant_evaluated() +// TODO: Remove this once GCC supports __builtin_memmove during constant evaluation +#ifndef _LIBCPP_COMPILER_GCC + && !is_trivially_copyable<_InValueT>::value +#endif + ) + return std::__copy_impl<_InValueT*, _InValueT*, _OutValueT*>(__first, __last, __result); + const size_t __n = static_cast(__last - __first); + if (__n > 0) + ::__builtin_memmove(__result, __first, __n * sizeof(_OutValueT)); + return std::make_pair(__first + __n, __result + __n); +} + +template ::type, _OutValueT>::value + && is_trivially_copy_assignable<_OutValueT>::value> > +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 +pair, reverse_iterator<_OutValueT*> > +__copy_impl(reverse_iterator<_InValueT*> __first, + reverse_iterator<_InValueT*> __last, + reverse_iterator<_OutValueT*> __result) { + auto __first_base = __first.base(); + auto __last_base = __last.base(); + auto __result_base = __result.base(); + auto __result_first = __result_base - (__first_base - __last_base); + std::__copy_impl(__last_base, __first_base, __result_first); + return std::make_pair(__last, reverse_iterator<_OutValueT*>(__result_first)); +} + +template +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 +pair<_InIter, _OutIter> __copy(_InIter __first, _Sent __last, _OutIter __result) { + return std::__copy_impl(std::move(__first), std::move(__last), std::move(__result)); } -template -inline _LIBCPP_INLINE_VISIBILITY -typename enable_if -< - is_same::type, _Up>::value && - is_trivially_copy_assignable<_Up>::value, - _Up* ->::type -__copy(_Tp* __first, _Tp* __last, _Up* __result) -{ - const size_t __n = static_cast(__last - __first); - if (__n > 0) - _VSTD::memmove(__result, __first, __n * sizeof(_Up)); - return __result + __n; +template ::value + && is_copy_constructible<_Sent>::value + && is_copy_constructible<_OutIter>::value> > +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 +pair<_InIter, _OutIter> __copy(_InIter __first, _Sent __last, _OutIter __result) { + auto __ret = std::__copy_impl(std::__unwrap_iter(__first), std::__unwrap_iter(__last), std::__unwrap_iter(__result)); + return std::make_pair(std::__rewrap_iter(__first, __ret.first), std::__rewrap_iter(__result, __ret.second)); } template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 _OutputIterator -copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result) -{ - if (__libcpp_is_constant_evaluated()) { - return _VSTD::__copy_constexpr(__first, __last, __result); - } else { - return _VSTD::__rewrap_iter(__result, - _VSTD::__copy(_VSTD::__unwrap_iter(__first), - _VSTD::__unwrap_iter(__last), - _VSTD::__unwrap_iter(__result))); - } +copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result) { + return std::__copy(__first, __last, __result).second; } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/copy_backward.h b/libcxx/include/__algorithm/copy_backward.h --- a/libcxx/include/__algorithm/copy_backward.h +++ b/libcxx/include/__algorithm/copy_backward.h @@ -9,9 +9,11 @@ #ifndef _LIBCPP___ALGORITHM_COPY_BACKWARD_H #define _LIBCPP___ALGORITHM_COPY_BACKWARD_H +#include <__algorithm/copy.h> #include <__algorithm/unwrap_iter.h> #include <__config> #include <__iterator/iterator_traits.h> +#include <__iterator/reverse_iterator.h> #include #include @@ -21,57 +23,29 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 -_OutputIterator -__copy_backward_constexpr(_BidirectionalIterator __first, _BidirectionalIterator __last, _OutputIterator __result) -{ - while (__first != __last) - *--__result = *--__last; - return __result; +template +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 +pair<_Iter1, _Iter2> __copy_backward_impl(_Iter1 __first, _Sent1 __last, _Iter2 __result) { + auto __ret = std::__copy(reverse_iterator<_Iter1>(__last), + reverse_iterator<_Sent1>(__first), + reverse_iterator<_Iter2>(__result)); + return pair<_Iter1, _Iter2>(__ret.first.base(), __ret.second.base()); } -template -inline _LIBCPP_INLINE_VISIBILITY -_OutputIterator -__copy_backward(_BidirectionalIterator __first, _BidirectionalIterator __last, _OutputIterator __result) -{ - return _VSTD::__copy_backward_constexpr(__first, __last, __result); -} - -template -inline _LIBCPP_INLINE_VISIBILITY -typename enable_if -< - is_same::type, _Up>::value && - is_trivially_copy_assignable<_Up>::value, - _Up* ->::type -__copy_backward(_Tp* __first, _Tp* __last, _Up* __result) -{ - const size_t __n = static_cast(__last - __first); - if (__n > 0) - { - __result -= __n; - _VSTD::memmove(__result, __first, __n * sizeof(_Up)); - } - return __result; +template +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 +pair<_Iter1, _Iter2> __copy_backward(_Iter1 __first, _Sent1 __last, _Iter2 __result) { + auto __ret = std::__copy_backward_impl(std::__unwrap_iter(__first), + std::__unwrap_iter(__last), + std::__unwrap_iter(__result)); + return pair<_Iter1, _Iter2>(std::__rewrap_iter(__first, __ret.first), std::__rewrap_iter(__result, __ret.second)); } template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _BidirectionalIterator2 -copy_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last, - _BidirectionalIterator2 __result) -{ - if (__libcpp_is_constant_evaluated()) { - return _VSTD::__copy_backward_constexpr(__first, __last, __result); - } else { - return _VSTD::__rewrap_iter(__result, - _VSTD::__copy_backward(_VSTD::__unwrap_iter(__first), - _VSTD::__unwrap_iter(__last), - _VSTD::__unwrap_iter(__result))); - } +copy_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last, _BidirectionalIterator2 __result) { + return std::__copy_backward(__first, __last, __result).second; } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/ranges_copy.h b/libcxx/include/__algorithm/ranges_copy.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_copy.h @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_COPY_H +#define _LIBCPP___ALGORITHM_RANGES_COPY_H + +#include <__algorithm/copy.h> +#include <__algorithm/in_out_result.h> +#include <__config> +#include <__functional/identity.h> +#include <__iterator/concepts.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/dangling.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { + +template +using copy_result = in_out_result<_InIter, _OutIter>; + +namespace __copy { +struct __fn { + + template _Sent, weakly_incrementable _OutIter> + requires indirectly_copyable<_InIter, _OutIter> + _LIBCPP_HIDE_FROM_ABI constexpr + copy_result<_InIter, _OutIter> operator()(_InIter __first, _Sent __last, _OutIter __result) const { + auto __ret = std::__copy(std::move(__first), std::move(__last), std::move(__result)); + return {std::move(__ret.first), std::move(__ret.second)}; + } + + template + requires indirectly_copyable, _OutIter> + _LIBCPP_HIDE_FROM_ABI constexpr + copy_result, _OutIter> operator()(_Range&& __r, _OutIter __result) const { + auto __ret = std::__copy(ranges::begin(__r), ranges::end(__r), std::move(__result)); + return {std::move(__ret.first), std::move(__ret.second)}; + } +}; +} // namespace __copy + +inline namespace __cpo { + inline constexpr auto copy = __copy::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_COPY_H diff --git a/libcxx/include/__algorithm/ranges_copy_backward.h b/libcxx/include/__algorithm/ranges_copy_backward.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_copy_backward.h @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_COPY_BACKWARD_H +#define _LIBCPP___ALGORITHM_RANGES_COPY_BACKWARD_H + +#include <__algorithm/copy_backward.h> +#include <__algorithm/in_out_result.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/reverse_iterator.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/dangling.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { + +template +using copy_backward_result = in_out_result<_Ip, _Op>; + +namespace __copy_backward { +struct __fn { + + template _Sent1, bidirectional_iterator _InIter2> + requires indirectly_copyable<_InIter1, _InIter2> + _LIBCPP_HIDE_FROM_ABI constexpr + copy_backward_result<_InIter1, _InIter2> operator()(_InIter1 __first, _Sent1 __last, _InIter2 __result) const { + auto __ret = std::__copy_backward(std::move(__first), std::move(__last), std::move(__result)); + return {std::move(__ret.first), std::move(__ret.second)}; + } + + template + requires indirectly_copyable, _Iter> + _LIBCPP_HIDE_FROM_ABI constexpr + copy_backward_result, _Iter> operator()(_Range&& __r, _Iter __result) const { + auto __ret = std::__copy_backward(ranges::begin(__r), + ranges::end(__r), + std::move(__result)); + return {std::move(__ret.first), std::move(__ret.second)}; + } +}; +} // namespace __copy_backward + +inline namespace __cpo { + inline constexpr auto copy_backward = __copy_backward::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_COPY_BACKWARD_H diff --git a/libcxx/include/__algorithm/ranges_copy_if.h b/libcxx/include/__algorithm/ranges_copy_if.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_copy_if.h @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_COPY_IF_H +#define _LIBCPP___ALGORITHM_RANGES_COPY_IF_H + +#include <__algorithm/in_out_result.h> +#include <__config> +#include <__functional/identity.h> +#include <__functional/invoke.h> +#include <__iterator/concepts.h> +#include <__iterator/projected.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/dangling.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { + +template +using copy_if_result = in_out_result<_Ip, _Op>; + +namespace __copy_if { +struct __fn { + + template + _LIBCPP_HIDE_FROM_ABI static constexpr + copy_if_result <_InIter, _OutIter> + __copy_if_impl(_InIter __first, _Sent __last, _OutIter __result, _Pred& __pred, _Proj& __proj) { + for (; __first != __last; ++__first) { + if (std::invoke(__pred, std::invoke(__proj, *__first))) { + *__result = *__first; + ++__result; + } + } + return {std::move(__first), std::move(__result)}; + } + + template _Sent, weakly_incrementable _OutIter, class _Proj = identity, + indirect_unary_predicate> _Pred> + requires indirectly_copyable<_Iter, _OutIter> + _LIBCPP_HIDE_FROM_ABI constexpr + copy_if_result<_Iter, _OutIter> + operator()(_Iter __first, _Sent __last, _OutIter __result, _Pred __pred, _Proj __proj = {}) const { + return __copy_if_impl(std::move(__first), std::move(__last), std::move(__result), __pred, __proj); + } + + template , _Proj>> _Pred> + requires indirectly_copyable, _OutIter> + _LIBCPP_HIDE_FROM_ABI constexpr + copy_if_result, _OutIter> + operator()(_Range&& __r, _OutIter __result, _Pred __pred, _Proj __proj = {}) const { + return __copy_if_impl(ranges::begin(__r), ranges::end(__r), std::move(__result), __pred, __proj); + } +}; +} // namespace __copy_if + +inline namespace __cpo { + inline constexpr auto copy_if = __copy_if::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_COPY_IF_H diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_copy_n.h @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_COPY_N_H +#define _LIBCPP___ALGORITHM_RANGES_COPY_N_H + +#include <__algorithm/copy.h> +#include <__algorithm/in_out_result.h> +#include <__algorithm/ranges_copy.h> +#include <__config> +#include <__functional/identity.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/unreachable_sentinel.h> +#include <__iterator/wrap_iter.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +namespace ranges { + +template +using copy_n_result = in_out_result<_Ip, _Op>; + +namespace __copy_n { +struct __fn { + + template + _LIBCPP_HIDE_FROM_ABI constexpr static + copy_n_result<_InIter, _OutIter> __go(_InIter __first, _DiffType __n, _OutIter __result) { + while (__n != 0) { + *__result = *__first; + ++__first; + ++__result; + --__n; + } + return {std::move(__first), std::move(__result)}; + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr static + copy_n_result<_InIter, _OutIter> __go(_InIter __first, _DiffType __n, _OutIter __result) { + auto __ret = std::__copy(__first, __first + __n, __result); + return {__ret.first, __ret.second}; + } + + template + requires indirectly_copyable<_Ip, _Op> + _LIBCPP_HIDE_FROM_ABI constexpr + copy_n_result<_Ip, _Op> operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const { + return __go(std::move(__first), __n, std::move(__result)); + } +}; +} // namespace __copy_n + +inline namespace __cpo { + inline constexpr auto copy_n = __copy_n::__fn{}; +} // namespace __cpo +} // namespace ranges + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ALGORITHM_RANGES_COPY_N_H diff --git a/libcxx/include/__algorithm/unwrap_iter.h b/libcxx/include/__algorithm/unwrap_iter.h --- a/libcxx/include/__algorithm/unwrap_iter.h +++ b/libcxx/include/__algorithm/unwrap_iter.h @@ -64,14 +64,14 @@ } template -_LIBCPP_HIDE_FROM_ABI +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _OrigIter __rewrap_iter(_OrigIter, _OrigIter __result) { return __result; } template -_LIBCPP_HIDE_FROM_ABI +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _OrigIter __rewrap_iter(_OrigIter __first, _UnwrappedIter __result) { // Precondition: __result is reachable from __first diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -191,6 +191,53 @@ indirect_strict_weak_order, Proj>> Comp = ranges::less> constexpr ranges::minmax_element_result> minmax_element(R&& r, Comp comp = {}, Proj proj = {}); // since C++20 + + template + using copy_result = in_out_result; // since C++20 + + template + using copy_n_result = in_out_result; // since C++20 + + template + using copy_if_result = in_out_result; // since C++20 + + template + using copy_backward_result = in_out_result; // since C++20 + + template S, weakly_incrementable O> + requires indirectly_copyable + constexpr ranges::copy_result ranges::copy(I first, S last, O result); // since C++20 + + template + requires indirectly_copyable, O> + constexpr ranges::copy_result, O> ranges::copy(R&& r, O result); // since C++20 + + template + requires indirectly_copyable + constexpr ranges::copy_n_result + ranges::copy_n(I first, iter_difference_t n, O result); // since C++20 + + template S, weakly_incrementable O, class Proj = identity, + indirect_unary_predicate> Pred> + requires indirectly_copyable + constexpr ranges::copy_if_result + ranges::copy_if(I first, S last, O result, Pred pred, Proj proj = {}); // since C++20 + + template, Proj>> Pred> + requires indirectly_copyable, O> + constexpr ranges::copy_if_result, O> + ranges::copy_if(R&& r, O result, Pred pred, Proj proj = {}); // since C++20 + + template S1, bidirectional_iterator I2> + requires indirectly_copyable + constexpr ranges::copy_backward_result + ranges::copy_backward(I1 first, S1 last, I2 result); // since C++20 + + template + requires indirectly_copyable, I> + constexpr ranges::copy_backward_result, I> + ranges::copy_backward(R&& r, I result); // since C++20 } constexpr bool // constexpr in C++20 @@ -908,6 +955,10 @@ #include <__algorithm/pop_heap.h> #include <__algorithm/prev_permutation.h> #include <__algorithm/push_heap.h> +#include <__algorithm/ranges_copy.h> +#include <__algorithm/ranges_copy_backward.h> +#include <__algorithm/ranges_copy_if.h> +#include <__algorithm/ranges_copy_n.h> #include <__algorithm/ranges_count.h> #include <__algorithm/ranges_count_if.h> #include <__algorithm/ranges_find.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -294,6 +294,10 @@ module pop_heap { private header "__algorithm/pop_heap.h" } module prev_permutation { private header "__algorithm/prev_permutation.h" } module push_heap { private header "__algorithm/push_heap.h" } + module ranges_copy { private header "__algorithm/ranges_copy.h" } + module ranges_copy_backward { private header "__algorithm/ranges_copy_backward.h" } + module ranges_copy_if { private header "__algorithm/ranges_copy_if.h" } + module ranges_copy_n { private header "__algorithm/ranges_copy_n.h" } module ranges_count { private header "__algorithm/ranges_count.h" } module ranges_count_if { private header "__algorithm/ranges_count_if.h" } module ranges_find { private header "__algorithm/ranges_find.h" } diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -103,6 +103,10 @@ #include <__algorithm/pop_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pop_heap.h'}} #include <__algorithm/prev_permutation.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/prev_permutation.h'}} #include <__algorithm/push_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/push_heap.h'}} +#include <__algorithm/ranges_copy.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy.h'}} +#include <__algorithm/ranges_copy_backward.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_backward.h'}} +#include <__algorithm/ranges_copy_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_if.h'}} +#include <__algorithm/ranges_copy_n.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_n.h'}} #include <__algorithm/ranges_count.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_count.h'}} #include <__algorithm/ranges_count_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_count_if.h'}} #include <__algorithm/ranges_find.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_find.h'}} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp @@ -0,0 +1,208 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template S, weakly_incrementable O> +// requires indirectly_copyable +// constexpr ranges::copy_result ranges::copy(I first, S last, O result); +// template +// requires indirectly_copyable, O> +// constexpr ranges::copy_result, O> ranges::copy(R&& r, O result); + +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +template > +concept HasCopyIt = requires(In in, Sent sent, Out out) { std::ranges::copy(in, sent, out); }; + +static_assert(HasCopyIt); +static_assert(!HasCopyIt); +static_assert(!HasCopyIt); +static_assert(!HasCopyIt); +static_assert(!HasCopyIt); +struct NotIndirectlyCopyable {}; +static_assert(!HasCopyIt); +static_assert(!HasCopyIt); +static_assert(!HasCopyIt); + +template +concept HasCopyR = requires(Range range, Out out) { std::ranges::copy(range, out); }; + +static_assert(HasCopyR, int*>); +static_assert(!HasCopyR); +static_assert(!HasCopyR); +static_assert(!HasCopyR); +static_assert(!HasCopyR); +static_assert(!HasCopyR, int*>); +static_assert(!HasCopyR); +static_assert(!HasCopyR); + +static_assert(std::is_same_v, std::ranges::in_out_result>); + +template +constexpr void test_iterators() { + { // simple test + { + std::array in {1, 2, 3, 4}; + std::array out; + std::same_as> auto ret = + std::ranges::copy(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data())); + assert(in == out); + assert(base(ret.in) == in.data() + in.size()); + assert(base(ret.out) == out.data() + out.size()); + } + { + std::array in {1, 2, 3, 4}; + std::array out; + auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); + std::same_as> auto ret = std::ranges::copy(range, Out(out.data())); + assert(in == out); + assert(base(ret.in) == in.data() + in.size()); + assert(base(ret.out) == out.data() + out.size()); + } + } + + { // check that an empty range works + { + std::array in; + std::array out; + auto ret = std::ranges::copy(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data())); + assert(base(ret.in) == in.data()); + assert(base(ret.out) == out.data()); + } + { + std::array in; + std::array out; + auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); + auto ret = std::ranges::copy(range, Out(out.data())); + assert(base(ret.in) == in.data()); + assert(base(ret.out) == out.data()); + } + } +} + +template +constexpr void test_in_iterators() { + test_iterators, Out, sentinel_wrapper>>(); + test_iterators, Out>(); + test_iterators, Out>(); + test_iterators, Out>(); + test_iterators, Out>(); +} + +constexpr bool test() { + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + + { // check that ranges::dangling is returned + std::array out; + std::same_as> auto ret = + std::ranges::copy(std::array {1, 2, 3, 4}, out.data()); + assert(ret.out == out.data() + 4); + assert((out == std::array{1, 2, 3, 4})); + } + + { // check that an iterator is returned with a borrowing range + std::array in {1, 2, 3, 4}; + std::array out; + std::same_as> auto ret = std::ranges::copy(std::views::all(in), out.data()); + assert(ret.in == in.data() + 4); + assert(ret.out == out.data() + 4); + assert(in == out); + } + + { // check that every element is copied exactly once + struct CopyOnce { + bool copied = false; + constexpr CopyOnce() = default; + constexpr CopyOnce(const CopyOnce& other) = delete; + constexpr CopyOnce& operator=(const CopyOnce& other) { + assert(!other.copied); + copied = true; + return *this; + } + }; + { + std::array in {}; + std::array out {}; + auto ret = std::ranges::copy(in.begin(), in.end(), out.begin()); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; })); + } + { + std::array in {}; + std::array out {}; + auto ret = std::ranges::copy(in, out.begin()); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; })); + } + } + + { // check that the range is copied forwards + struct OnlyForwardsCopyable { + OnlyForwardsCopyable* next = nullptr; + bool canCopy = false; + OnlyForwardsCopyable() = default; + constexpr OnlyForwardsCopyable& operator=(const OnlyForwardsCopyable&) { + assert(canCopy); + if (next != nullptr) + next->canCopy = true; + return *this; + } + }; + { + std::array in {}; + std::array out {}; + out[0].next = &out[1]; + out[1].next = &out[2]; + out[0].canCopy = true; + auto ret = std::ranges::copy(in.begin(), in.end(), out.begin()); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + assert(out[0].canCopy); + assert(out[1].canCopy); + assert(out[2].canCopy); + } + { + std::array in {}; + std::array out {}; + out[0].next = &out[1]; + out[1].next = &out[2]; + out[0].canCopy = true; + auto ret = std::ranges::copy(in, out.begin()); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + assert(out[0].canCopy); + assert(out[1].canCopy); + assert(out[2].canCopy); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp @@ -0,0 +1,210 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + + +// template S1, bidirectional_iterator I2> +// requires indirectly_copyable +// constexpr ranges::copy_backward_result +// ranges::copy_backward(I1 first, S1 last, I2 result); +// template +// requires indirectly_copyable, I> +// constexpr ranges::copy_backward_result, I> +// ranges::copy_backward(R&& r, I result); + +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +template > +concept HasCopyBackwardIt = requires(In in, Sent sent, Out out) { std::ranges::copy_backward(in, sent, out); }; + +static_assert(HasCopyBackwardIt); +static_assert(!HasCopyBackwardIt); +static_assert(!HasCopyBackwardIt); +static_assert(!HasCopyBackwardIt); +static_assert(!HasCopyBackwardIt); +struct NotIndirectlyCopyable {}; +static_assert(!HasCopyBackwardIt); +static_assert(!HasCopyBackwardIt); +static_assert(!HasCopyBackwardIt); + +template +concept HasCopyBackwardR = requires(Range range, Out out) { std::ranges::copy_backward(range, out); }; + +static_assert(HasCopyBackwardR, int*>); +static_assert(!HasCopyBackwardR); +static_assert(!HasCopyBackwardR); +static_assert(!HasCopyBackwardR); +static_assert(!HasCopyBackwardR); +static_assert(!HasCopyBackwardR, int*>); +static_assert(!HasCopyBackwardR); +static_assert(!HasCopyBackwardR); + +static_assert(std::is_same_v, std::ranges::in_out_result>); + +template +constexpr void test_iterators() { + { // simple test + { + std::array in {1, 2, 3, 4}; + std::array out; + std::same_as> auto ret = + std::ranges::copy_backward(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data() + out.size())); + assert(in == out); + assert(base(ret.in) == in.data()); + assert(base(ret.out) == out.data()); + } + { + std::array in {1, 2, 3, 4}; + std::array out; + auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); + std::same_as> auto ret = + std::ranges::copy_backward(range, Out(out.data() + out.size())); + assert(in == out); + assert(base(ret.in) == in.data()); + assert(base(ret.out) == out.data()); + } + } + + { // check that an empty range works + { + std::array in; + std::array out; + auto ret = + std::ranges::copy_backward(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data() + out.size())); + assert(base(ret.in) == in.data()); + assert(base(ret.out) == out.data()); + } + { + std::array in; + std::array out; + auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); + auto ret = std::ranges::copy_backward(range, Out(out.data())); + assert(base(ret.in) == in.data()); + assert(base(ret.out) == out.data()); + } + } +} + +template +constexpr void test_in_iterators() { + test_iterators, Out>(); + test_iterators, Out>(); + test_iterators, Out>(); +} + +constexpr bool test() { + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + + { // check that ranges::dangling is returned + std::array out; + std::same_as> auto ret = + std::ranges::copy_backward(std::array {1, 2, 3, 4}, out.data() + out.size()); + assert(ret.out == out.data()); + assert((out == std::array{1, 2, 3, 4})); + } + + { // check that an iterator is returned with a borrowing range + std::array in {1, 2, 3, 4}; + std::array out; + std::same_as> auto ret = + std::ranges::copy_backward(std::views::all(in), out.data() + out.size()); + assert(ret.in == in.data()); + assert(ret.out == out.data()); + assert(in == out); + } + + { // check that every element is copied exactly once + struct CopyOnce { + bool copied = false; + constexpr CopyOnce() = default; + constexpr CopyOnce(const CopyOnce& other) = delete; + constexpr CopyOnce& operator=(const CopyOnce& other) { + assert(!other.copied); + copied = true; + return *this; + } + }; + { + std::array in {}; + std::array out {}; + auto ret = std::ranges::copy_backward(in.begin(), in.end(), out.end()); + assert(ret.in == in.begin()); + assert(ret.out == out.begin()); + assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; })); + } + { + std::array in {}; + std::array out {}; + auto ret = std::ranges::copy_backward(in, out.end()); + assert(ret.in == in.begin()); + assert(ret.out == out.begin()); + assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; })); + } + } + + { // check that the range is copied backwards + struct OnlyBackwardsCopyable { + OnlyBackwardsCopyable* next = nullptr; + bool canCopy = false; + OnlyBackwardsCopyable() = default; + constexpr OnlyBackwardsCopyable& operator=(const OnlyBackwardsCopyable&) { + assert(canCopy); + if (next != nullptr) + next->canCopy = true; + return *this; + } + }; + { + std::array in {}; + std::array out {}; + out[1].next = &out[0]; + out[2].next = &out[1]; + out[2].canCopy = true; + auto ret = std::ranges::copy_backward(in, out.end()); + assert(ret.in == in.begin()); + assert(ret.out == out.begin()); + assert(out[0].canCopy); + assert(out[1].canCopy); + assert(out[2].canCopy); + } + { + std::array in {}; + std::array out {}; + out[1].next = &out[0]; + out[2].next = &out[1]; + out[2].canCopy = true; + auto ret = std::ranges::copy_backward(in.begin(), in.end(), out.end()); + assert(ret.in == in.begin()); + assert(ret.out == out.begin()); + assert(out[0].canCopy); + assert(out[1].canCopy); + assert(out[2].canCopy); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_if.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_if.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_if.pass.cpp @@ -0,0 +1,220 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template S, weakly_incrementable O, class Proj = identity, +// indirect_unary_predicate> Pred> +// requires indirectly_copyable +// constexpr ranges::copy_if_result +// ranges::copy_if(I first, S last, O result, Pred pred, Proj proj = {}); +// template, Proj>> Pred> +// requires indirectly_copyable, O> +// constexpr ranges::copy_if_result, O> +// ranges::copy_if(R&& r, O result, Pred pred, Proj proj = {}); + +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +struct Functor { + bool operator()(int); +}; + +template , class Func = Functor> +concept HasCopyIfIt = requires(In first, Sent last, Out result) { std::ranges::copy_if(first, last, result, Func{}); }; + +static_assert(HasCopyIfIt); +static_assert(!HasCopyIfIt); +static_assert(!HasCopyIfIt); +static_assert(!HasCopyIfIt); +static_assert(!HasCopyIfIt); +struct NotIndirectlyCopyable {}; +static_assert(!HasCopyIfIt); +static_assert(!HasCopyIfIt); +static_assert(!HasCopyIfIt); + +static_assert(!HasCopyIfIt); +static_assert(!HasCopyIfIt); + +template +concept HasCopyIfR = requires(Range range, Out out) { std::ranges::copy_if(range, out, Func{}); }; + +static_assert(HasCopyIfR, int*>); +static_assert(!HasCopyIfR); +static_assert(!HasCopyIfR); +static_assert(!HasCopyIfR); +static_assert(!HasCopyIfR); +static_assert(!HasCopyIfR, int*>); +static_assert(!HasCopyIfR); +static_assert(!HasCopyIfR); + +static_assert(std::is_same_v, std::ranges::in_out_result>); + +template +constexpr void test_iterators() { + { // simple test + { + std::array in = {1, 2, 3, 4}; + std::array out; + std::same_as> auto ret = + std::ranges::copy_if(In(in.data()), + Sent(In(in.data() + in.size())), + Out(out.data()), + [](int) { return true; }); + assert(in == out); + assert(base(ret.in) == in.data() + in.size()); + assert(base(ret.out) == out.data() + out.size()); + } + { + std::array in = {1, 2, 3, 4}; + std::array out; + auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); + std::same_as> auto ret = + std::ranges::copy_if(range, Out(out.data()), [](int) { return true; }); + assert(in == out); + assert(base(ret.in) == in.data() + in.size()); + assert(base(ret.out) == out.data() + out.size()); + } + } + + { // check that an empty range works + { + std::array in; + std::array out; + auto ret = std::ranges::copy_if(In(in.data()), Sent(In(in.data())), Out(out.data()), [](int) { return true; }); + assert(base(ret.in) == in.data()); + assert(base(ret.out) == out.data()); + } + { + std::array in; + std::array out; + auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data()))); + auto ret = std::ranges::copy_if(range, Out(out.data()), [](int) { return true; }); + assert(base(ret.in) == in.data()); + assert(base(ret.out) == out.data()); + } + } + + { // check that the predicate is used + { + std::array in = {4, 6, 87, 3, 88, 44, 45, 9}; + std::array out; + auto ret = std::ranges::copy_if(In(in.data()), + Sent(In(in.data() + in.size())), + Out(out.data()), + [](int i) { return i % 2 == 0; }); + assert((out == std::array{4, 6, 88, 44})); + assert(base(ret.in) == in.data() + in.size()); + assert(base(ret.out) == out.data() + out.size()); + } + { + std::array in = {4, 6, 87, 3, 88, 44, 45, 9}; + std::array out; + auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); + auto ret = std::ranges::copy_if(range, Out(out.data()), [](int i) { return i % 2 == 0; }); + assert((out == std::array{4, 6, 88, 44})); + assert(base(ret.in) == in.data() + in.size()); + assert(base(ret.out) == out.data() + out.size()); + } + } +} + +template +constexpr bool test_in_iterators() { + test_iterators, Out, sentinel_wrapper>>(); + test_iterators, Out, sentinel_wrapper>>(); + test_iterators, Out>(); + test_iterators, Out>(); + test_iterators, Out>(); + test_iterators, Out>(); + test_iterators(); + + return true; +} + +constexpr bool test() { + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators(); + + { // check that std::invoke is used + { + struct S { int val; int other; }; + std::array in = {{{4, 2}, {1, 3}, {3, 4}, {3, 5}}}; + std::array out; + auto ret = std::ranges::copy_if(in.begin(), in.end(), out.begin(), [](int i) { return i == 3; }, &S::val); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + assert(out[0].val == 3); + assert(out[0].other == 4); + assert(out[1].val == 3); + assert(out[1].other == 5); + } + { + struct S { int val; int other; }; + std::array in = {{{4, 2}, {1, 3}, {3, 4}, {3, 5}}}; + std::array out; + auto ret = std::ranges::copy_if(in, out.begin(), [](int i) { return i == 3; }, &S::val); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + assert(out[0].val == 3); + assert(out[0].other == 4); + assert(out[1].val == 3); + assert(out[1].other == 5); + } + } + + { // check that the complexity requirements are met + { + int predicateCount = 0; + int projectionCount = 0; + auto pred = [&](int i) { ++predicateCount; return i != 0; }; + auto proj = [&](int i) { ++projectionCount; return i; }; + + int a[] = {5, 4, 3, 2, 1}; + int b[5]; + std::ranges::copy_if(a, a + 5, b, pred, proj); + assert(predicateCount == 5); + assert(projectionCount == 5); + } + { + int predicateCount = 0; + int projectionCount = 0; + auto pred = [&](int i) { ++predicateCount; return i != 0; }; + auto proj = [&](int i) { ++projectionCount; return i; }; + + int a[] = {5, 4, 3, 2, 1}; + int b[5]; + std::ranges::copy_if(a, b, pred, proj); + assert(predicateCount == 5); + assert(projectionCount == 5); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// requires indirectly_copyable +// constexpr ranges::copy_n_result +// ranges::copy_n(I first, iter_difference_t n, O result); + +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +template +concept HasCopyNIt = requires(In in, Count count, Out out) { std::ranges::copy_n(in, count, out); }; + +static_assert(HasCopyNIt); +static_assert(!HasCopyNIt); +static_assert(!HasCopyNIt); +static_assert(!HasCopyNIt); +static_assert(!HasCopyNIt); +struct NotIndirectlyCopyable {}; +static_assert(!HasCopyNIt); +static_assert(!HasCopyNIt); +static_assert(!HasCopyNIt); + +static_assert(std::is_same_v, std::ranges::in_out_result>); + +template +constexpr void test_iterators() { + { // simple test + std::array in {1, 2, 3, 4}; + std::array out; + std::same_as> auto ret = + std::ranges::copy_n(In(in.data()), in.size(), Out(out.data())); + assert(in == out); + assert(base(ret.in) == in.data() + in.size()); + assert(base(ret.out) == out.data() + out.size()); + } + + { // check that an empty range works + std::array in; + std::array out; + auto ret = std::ranges::copy_n(In(in.data()), in.size(), Out(out.begin())); + assert(base(ret.in) == in.data()); + assert(base(ret.out) == out.data()); + } +} + +template +constexpr void test_in_iterators() { + test_iterators, Out, sentinel_wrapper>>(); + test_iterators, Out>(); + test_iterators, Out>(); + test_iterators, Out>(); + test_iterators, Out>(); +} + +constexpr bool test() { + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + test_in_iterators>(); + + { // check that every element is copied exactly once + struct CopyOnce { + bool copied = false; + constexpr CopyOnce() = default; + constexpr CopyOnce(const CopyOnce& other) = delete; + constexpr CopyOnce& operator=(const CopyOnce& other) { + assert(!other.copied); + copied = true; + return *this; + } + }; + std::array in {}; + std::array out {}; + auto ret = std::ranges::copy_n(in.data(), in.size(), out.begin()); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; })); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}