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 @@ -53,7 +53,7 @@ Write,replace_if,Not assigned,n/a,Not started Write,replace_copy,Not assigned,n/a,Not started Write,replace_copy_if,Not assigned,n/a,Not started -Write,swap_ranges,Not assigned,n/a,Not started +Write,swap_ranges,Not assigned,n/a,Complete Write,reverse_copy,Not assigned,n/a,Not started Write,rotate_copy,Not assigned,n/a,Not started Write,sample,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 @@ -60,6 +60,7 @@ __algorithm/pop_heap.h __algorithm/prev_permutation.h __algorithm/push_heap.h + __algorithm/ranges_swap_ranges.h __algorithm/remove_copy_if.h __algorithm/remove_copy.h __algorithm/remove_if.h diff --git a/libcxx/include/__algorithm/ranges_swap_ranges.h b/libcxx/include/__algorithm/ranges_swap_ranges.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_swap_ranges.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_SWAP_RANGES_H +#define _LIBCPP___ALGORITHM_RANGES_SWAP_RANGES_H + +#include <__algorithm/in_in_result.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/iter_swap.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/dangling.h> +#include + +#ifndef _LIBCPP_HAS_NO_RANGES + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { +template +using swap_ranges_result = in_in_result<_I1, _I2>; + +namespace __swap_ranges { +struct __fn { + template _S1, + input_iterator _I2, sentinel_for<_I2> _S2> + requires indirectly_swappable<_I1, _I2> + _LIBCPP_HIDE_FROM_ABI constexpr + ranges::swap_ranges_result<_I1, _I2> operator()(_I1 __first1, _S1 __last1, _I2 __first2, _S2 __last2) const + { + while (__first1 != __last1 && __first2 != __last2) + { + ranges::iter_swap(__first1, __first2); + ++__first1; + ++__first2; + } + return {__first1, __first2}; + } + + template + requires indirectly_swappable, iterator_t<_R2>> + constexpr ranges::swap_ranges_result, borrowed_iterator_t<_R2>> + operator()(_R1&& __r1, _R2&& __r2) const + { + return operator()(ranges::begin(__r1), ranges::end(__r1), + ranges::begin(__r2), ranges::end(__r2)); + } +}; +} // namespace __swap_ranges + +inline namespace __cpo { + inline constexpr auto swap_ranges = __swap_ranges::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_HAS_NO_RANGES + +#endif // _LIBCPP___ALGORITHM_RANGES_SWAP_RANGES_H diff --git a/libcxx/include/__algorithm/swap_ranges.h b/libcxx/include/__algorithm/swap_ranges.h --- a/libcxx/include/__algorithm/swap_ranges.h +++ b/libcxx/include/__algorithm/swap_ranges.h @@ -11,7 +11,6 @@ #include <__config> #include <__utility/swap.h> -#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -193,6 +193,16 @@ constexpr ForwardIterator2 // constexpr in C++20 swap_ranges(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2); +template S1, input_iterator I2, sentinel_for S2> + requires indirectly_swappable + constexpr ranges::swap_ranges_result + ranges::swap_ranges(I1 first1, S1 last1, I2 first2, S2 last2); + +template + requires indirectly_swappable, iterator_t> + constexpr ranges::swap_ranges_result, borrowed_iterator_t> + ranges::swap_ranges(R1&& r1, R2&& r2); + template constexpr void // constexpr in C++20 iter_swap(ForwardIterator1 a, ForwardIterator2 b); @@ -724,6 +734,7 @@ #include <__algorithm/pop_heap.h> #include <__algorithm/prev_permutation.h> #include <__algorithm/push_heap.h> +#include <__algorithm/ranges_swap_ranges.h> #include <__algorithm/remove.h> #include <__algorithm/remove_copy.h> #include <__algorithm/remove_copy_if.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -280,6 +280,7 @@ 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_swap_ranges { private header "__algorithm/ranges_swap_ranges.h" } module remove { private header "__algorithm/remove.h" } module remove_copy { private header "__algorithm/remove_copy.h" } module remove_copy_if { private header "__algorithm/remove_copy_if.h" } diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/ranges_swap_ranges.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/ranges_swap_ranges.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/algorithm/ranges_swap_ranges.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_swap_ranges.h'}} +#include <__algorithm/ranges_swap_ranges.h> diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.swap/ranges.swap_ranges.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.swap/ranges.swap_ranges.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.swap/ranges.swap_ranges.pass.cpp @@ -0,0 +1,176 @@ +//===----------------------------------------------------------------------===// +// +// 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-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template S1, input_­iterator I2, sentinel_­for S2> +// requires indirectly_­swappable +// constexpr ranges::swap_ranges_result +// ranges::swap_ranges(I1 first1, S1 last1, I2 first2, S2 last2); +// template +// requires indirectly_­swappable, iterator_t> +// constexpr ranges::swap_ranges_result, borrowed_iterator_t> +// ranges::swap_ranges(R1&& r1, R2&& r2); + +#include +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +constexpr void test_different_lengths() { + int i[3] = {1, 2, 3}; + int j[1] = {4}; + std::same_as> auto r = std::ranges::swap_ranges(i, i + 3, j, j + 1); + assert(r.in1 == i + 1); + assert(r.in2 == j + 1); + assert(i[0] == 4); + assert(i[1] == 2); + assert(i[2] == 3); + assert(j[0] == 1); + std::same_as> auto r2 = std::ranges::swap_ranges(i, j); + assert(r2.in1 == i + 1); + assert(r2.in2 == j + 1); + assert(i[0] == 1); + assert(i[1] == 2); + assert(i[2] == 3); + assert(j[0] == 4); +} + +template +constexpr void test_range() { + Range1 r1 = {1, 2, 3}; + Range2 r2 = {4, 5, 6}; + + using It1 = typename Range1::iterator; + using It2 = typename Range2::iterator; + + std::same_as> auto r = std::ranges::swap_ranges(r1, r2); + assert(r.in1 == std::ranges::end(r1)); + assert(r.in2 == std::ranges::end(r2)); + + assert((r1 == Range1{4, 5, 6})); + assert((r2 == Range2{1, 2, 3})); +} + +constexpr void test_borrowed_input_range() { + int r1[] = {1, 2, 3}; + int r2[] = {4, 5, 6}; + + using It = cpp17_input_iterator; + using Sentinel = sentinel_wrapper; + + std::ranges::subrange b1{It(r1), Sentinel(It(r1 + 2))}; + std::ranges::subrange b2{It(r2 + 1), Sentinel(It(r2 + 3))}; + + std::ranges::swap_ranges(b1, b2); + + assert(r1[0] == 5); + assert(r1[1] == 6); + assert(r1[2] == 3); + assert(r2[0] == 4); + assert(r2[1] == 1); + assert(r2[2] == 2); +} + +constexpr void test_sentinel() { + int i[3] = {1, 2, 3}; + int j[3] = {4, 5, 6}; + using Iter = cpp17_input_iterator; + using Sentinel = sentinel_wrapper; + std::same_as> auto r = + std::ranges::swap_ranges(Iter(i), Sentinel(Iter(i + 3)), Iter(j), Sentinel(Iter(j + 3))); + assert(base(r.in1) == i + 3); + assert(base(r.in2) == j + 3); + assert(i[0] == 4); + assert(i[1] == 5); + assert(i[2] == 6); + assert(j[0] == 1); + assert(j[1] == 2); + assert(j[2] == 3); +} + +template +constexpr void test_iterators() { + int i[3] = {1, 2, 3}; + int j[3] = {4, 5, 6}; + std::same_as> auto r = + std::ranges::swap_ranges(Iter1(i), Iter1(i + 3), Iter2(j), Iter2(j + 3)); + assert(base(r.in1) == i + 3); + assert(base(r.in2) == j + 3); + assert(i[0] == 4); + assert(i[1] == 5); + assert(i[2] == 6); + assert(j[0] == 1); + assert(j[1] == 2); + assert(j[2] == 3); +} + +constexpr bool constexpr_test() { + test_range, std::array>(); + + test_iterators, forward_iterator>(); + test_iterators, bidirectional_iterator>(); + test_iterators, random_access_iterator>(); + test_iterators, int*>(); + + test_iterators, forward_iterator>(); + test_iterators, bidirectional_iterator>(); + test_iterators, random_access_iterator>(); + test_iterators, int*>(); + + test_iterators, forward_iterator>(); + test_iterators, bidirectional_iterator>(); + test_iterators, random_access_iterator>(); + test_iterators, int*>(); + + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators(); + + test_sentinel(); + test_different_lengths(); + test_borrowed_input_range(); + + return true; +} + +void test_rval_range() { + std::vector r = {1, 2, 3}; + [[maybe_unused]] std::same_as::iterator, std::ranges::dangling>> auto + a = std::ranges::swap_ranges(r, std::vector{4, 5, 6}); + assert((r == std::vector{4, 5, 6})); + [[maybe_unused]] std::same_as::iterator>> auto + b = std::ranges::swap_ranges(std::vector{1, 2, 3}, r); + assert((r == std::vector{1, 2, 3})); +} + +void test() { + test_range, std::list>(); + test_rval_range(); + + struct S{}; + ASSERT_SAME_TYPE(std::ranges::swap_ranges_result, std::ranges::in_in_result); +} + +int main(int, char**) { + constexpr_test(); + test(); + static_assert(constexpr_test()); + return 0; +} diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp --- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp +++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp @@ -12,6 +12,7 @@ // [customization.point.object] // [range.adaptor.object] "A range adaptor object is a customization point object..." +#include #include #include #include @@ -42,6 +43,10 @@ //int arrays[10][10]; //std::pair pairs[10]; +// [alg.modifying.operations] +static_assert(test(std::ranges::swap_ranges, a, a)); +static_assert(test(std::ranges::swap_ranges, a, a + 10, a, a + 10)); + // [concept.swappable] static_assert(test(std::ranges::swap, a, a));