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,Nikolas Klauser,`D116303 `_,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 @@ -61,6 +61,7 @@ __algorithm/pop_heap.h __algorithm/prev_permutation.h __algorithm/push_heap.h + __algorithm/ranges_swap_ranges.h __algorithm/remove.h __algorithm/remove_copy.h __algorithm/remove_copy_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,66 @@ +//===----------------------------------------------------------------------===// +// +// 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 <__utility/move.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 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 {_VSTD::move(__first1), _VSTD::move(__first2)}; + } + + template + requires indirectly_swappable, iterator_t<_R2>> + _LIBCPP_HIDE_FROM_ABI constexpr + 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); @@ -731,6 +741,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 @@ -281,6 +281,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,216 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_iterators.h" + +constexpr void test_different_lengths() { + using Expected = std::ranges::swap_ranges_result; + 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); + std::same_as auto r3 = std::ranges::swap_ranges(j, j + 1, i, i + 3); + assert(r3.in1 == j + 1); + assert(r3.in2 == i + 1); + assert(i[0] == 4); + assert(i[1] == 2); + assert(i[2] == 3); + assert(j[0] == 1); + std::same_as auto r4 = std::ranges::swap_ranges(j, i); + assert(r4.in1 == j + 1); + assert(r4.in2 == i + 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}; + std::ranges::swap_ranges(std::ranges::subrange(r1), r2); + assert(r1[0] == 4); + assert(r1[1] == 5); + assert(r1[2] == 6); + assert(r2[0] == 1); + assert(r2[1] == 2); + assert(r2[2] == 3); + } + { + int r1[] = {1, 2, 3}; + int r2[] = {4, 5, 6}; + std::ranges::swap_ranges(r1, std::ranges::subrange(r2)); + assert(r1[0] == 4); + assert(r1[1] == 5); + assert(r1[2] == 6); + assert(r2[0] == 1); + assert(r2[1] == 2); + assert(r2[2] == 3); + } + { + int r1[] = {1, 2, 3}; + int r2[] = {4, 5, 6}; + std::ranges::swap_ranges(std::ranges::subrange(r1), std::ranges::subrange(r2)); + assert(r1[0] == 4); + assert(r1[1] == 5); + assert(r1[2] == 6); + assert(r2[0] == 1); + assert(r2[1] == 2); + assert(r2[2] == 3); + } +} + +constexpr void test_sentinel() { + int i[3] = {1, 2, 3}; + int j[3] = {4, 5, 6}; + using It = cpp17_input_iterator; + using Sent = sentinel_wrapper; + using Expected = std::ranges::swap_ranges_result; + std::same_as auto r = + std::ranges::swap_ranges(It(i), Sent(It(i + 3)), It(j), Sent(It(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() { + using Expected = std::ranges::swap_ranges_result; + int i[3] = {1, 2, 3}; + int j[3] = {4, 5, 6}; + std::same_as auto r = + std::ranges::swap_ranges(Iter1(i), sentinel_wrapper(Iter1(i + 3)), Iter2(j), sentinel_wrapper(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 void test_rval_range() { + { + using Expected = std::ranges::swap_ranges_result; + std::array r = {1, 2, 3}; + std::same_as auto a = std::ranges::swap_ranges(r, std::array{4, 5, 6}); + assert((r == std::array{4, 5, 6})); + assert(a.in1 == r.begin() + 3); + } + { + std::array r = {1, 2, 3}; + using Expected = std::ranges::swap_ranges_result; + std::same_as auto b = std::ranges::swap_ranges(std::array{4, 5, 6}, r); + assert((r == std::array{4, 5, 6})); + assert(b.in2 == r.begin() + 3); + } +} + +constexpr bool test() { + test_range, std::array>(); + + test_iterators, cpp20_input_iterator>(); + test_iterators, forward_iterator>(); + test_iterators, bidirectional_iterator>(); + test_iterators, random_access_iterator>(); + test_iterators, int*>(); + + test_iterators, cpp20_input_iterator>(); + test_iterators, forward_iterator>(); + test_iterators, bidirectional_iterator>(); + test_iterators, random_access_iterator>(); + test_iterators, int*>(); + + test_iterators, cpp20_input_iterator>(); + test_iterators, forward_iterator>(); + test_iterators, bidirectional_iterator>(); + test_iterators, random_access_iterator>(); + test_iterators, int*>(); + + test_iterators, cpp20_input_iterator>(); + 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_iterators(); + + test_sentinel(); + test_different_lengths(); + test_borrowed_input_range(); + test_rval_range(); + + return true; +} + +static_assert(std::same_as, std::ranges::in_in_result>); + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp --- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp +++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp @@ -142,7 +142,7 @@ //static_assert(test(std::ranges::stable_partition, a, odd)); //static_assert(test(std::ranges::stable_sort, a)); //static_assert(test(std::ranges::starts_with, a, a)); -//static_assert(test(std::ranges::swap_ranges, a, a)); +static_assert(test(std::ranges::swap_ranges, a, a)); //static_assert(test(std::ranges::transform, a, a, triple)); //static_assert(test(std::ranges::unique, a)); //static_assert(test(std::ranges::unique_copy, a, a));