[libcxx-commits] [libcxx] 1d1a191 - [libc++] Implement ranges::reverse
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Tue May 24 01:33:58 PDT 2022
Author: Nikolas Klauser
Date: 2022-05-24T10:33:57+02:00
New Revision: 1d1a191edcfa87bf77331ffcc8fa29562b17f517
URL: https://github.com/llvm/llvm-project/commit/1d1a191edcfa87bf77331ffcc8fa29562b17f517
DIFF: https://github.com/llvm/llvm-project/commit/1d1a191edcfa87bf77331ffcc8fa29562b17f517.diff
LOG: [libc++] Implement ranges::reverse
Reviewed By: var-const, #libc
Spies: libcxx-commits, mgorny
Differential Revision: https://reviews.llvm.org/D125752
Added:
libcxx/include/__algorithm/ranges_reverse.h
libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/ranges.reverse.pass.cpp
Modified:
libcxx/docs/Status/RangesAlgorithms.csv
libcxx/include/CMakeLists.txt
libcxx/include/algorithm
libcxx/include/module.modulemap
libcxx/test/libcxx/private_headers.verify.cpp
libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
libcxx/test/support/almost_satisfies_types.h
Removed:
################################################################################
diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index 3664106093c41..4395c91aa24b7 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -67,7 +67,7 @@ Merge,set_symmetric_
diff erence,Not assigned,n/a,Not started
Merge,set_union,Not assigned,n/a,Not started
Permutation,remove,Not assigned,n/a,Not started
Permutation,remove_if,Not assigned,n/a,Not started
-Permutation,reverse,Not assigned,n/a,Not started
+Permutation,reverse,Nikolas Klauser,`D125752 <https://llvm.org/D125752>`_,✅
Permutation,rotate,Not assigned,n/a,Not started
Permutation,shuffle,Not assigned,n/a,Not started
Permutation,unique,Not assigned,n/a,Not started
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 74336ed19703e..b6e4f6d6cf526 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -85,6 +85,7 @@ set(files
__algorithm/ranges_minmax.h
__algorithm/ranges_minmax_element.h
__algorithm/ranges_mismatch.h
+ __algorithm/ranges_reverse.h
__algorithm/ranges_swap_ranges.h
__algorithm/ranges_transform.h
__algorithm/remove.h
diff --git a/libcxx/include/__algorithm/ranges_reverse.h b/libcxx/include/__algorithm/ranges_reverse.h
new file mode 100644
index 0000000000000..e7555d0f9acd9
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_reverse.h
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_REVERSE_H
+#define _LIBCPP___ALGORITHM_RANGES_REVERSE_H
+
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/next.h>
+#include <__iterator/permutable.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/dangling.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 {
+namespace __reverse {
+struct __fn {
+
+ template <bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent>
+ requires permutable<_Iter>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ _Iter operator()(_Iter __first, _Sent __last) const {
+ if constexpr (random_access_iterator<_Iter>) {
+ if (__first == __last)
+ return __first;
+
+ auto __end = ranges::next(__first, __last);
+ auto __ret = __end;
+
+ while (__first < --__end) {
+ ranges::iter_swap(__first, __end);
+ ++__first;
+ }
+ return __ret;
+ } else {
+ auto __end = ranges::next(__first, __last);
+ auto __ret = __end;
+
+ while (__first != __end) {
+ if (__first == --__end)
+ break;
+
+ ranges::iter_swap(__first, __end);
+ ++__first;
+ }
+ return __ret;
+ }
+ }
+
+ template <bidirectional_range _Range>
+ requires permutable<iterator_t<_Range>>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ borrowed_iterator_t<_Range> operator()(_Range&& __range) const {
+ return (*this)(ranges::begin(__range), ranges::end(__range));
+ }
+
+};
+} // namespace __reverse
+
+inline namespace __cpo {
+ inline constexpr auto reverse = __reverse::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+#endif // _LIBCPP___ALGORITHM_RANGES_REVERSE_H
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index a95fa769f024a..224c9e1ab8465 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -274,6 +274,15 @@ namespace ranges {
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
constexpr bool ranges::is_partitioned(R&& r, Pred pred, Proj proj = {}); // since C++20
+
+ template<bidirectional_iterator I, sentinel_for<I> S>
+ requires permutable<I>
+ constexpr I ranges::reverse(I first, S last); // since C++20
+
+ template<bidirectional_range R>
+ requires permutable<iterator_t<R>>
+ constexpr borrowed_iterator_t<R> ranges::reverse(R&& r); // since C++20
+
}
constexpr bool // constexpr in C++20
@@ -1009,6 +1018,7 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/ranges_minmax.h>
#include <__algorithm/ranges_minmax_element.h>
#include <__algorithm/ranges_mismatch.h>
+#include <__algorithm/ranges_reverse.h>
#include <__algorithm/ranges_swap_ranges.h>
#include <__algorithm/ranges_transform.h>
#include <__algorithm/remove.h>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index d62e6ae4df47f..132793f1172c3 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -318,6 +318,7 @@ module std [system] {
module ranges_minmax { private header "__algorithm/ranges_minmax.h" }
module ranges_minmax_element { private header "__algorithm/ranges_minmax_element.h" }
module ranges_mismatch { private header "__algorithm/ranges_mismatch.h" }
+ module ranges_reverse { private header "__algorithm/ranges_reverse.h" }
module ranges_swap_ranges { private header "__algorithm/ranges_swap_ranges.h" }
module ranges_transform { private header "__algorithm/ranges_transform.h" }
module remove { private header "__algorithm/remove.h" }
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index d85a7e99fa81f..fe0f19baf6af9 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -122,6 +122,7 @@ END-SCRIPT
#include <__algorithm/ranges_minmax.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_minmax.h'}}
#include <__algorithm/ranges_minmax_element.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_minmax_element.h'}}
#include <__algorithm/ranges_mismatch.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_mismatch.h'}}
+#include <__algorithm/ranges_reverse.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_reverse.h'}}
#include <__algorithm/ranges_swap_ranges.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_swap_ranges.h'}}
#include <__algorithm/ranges_transform.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_transform.h'}}
#include <__algorithm/remove.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/remove.h'}}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/ranges.reverse.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/ranges.reverse.pass.cpp
new file mode 100644
index 0000000000000..14474706a15e7
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/ranges.reverse.pass.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <algorithm>
+
+// template<bidirectional_iterator I, sentinel_for<I> S>
+// requires permutable<I>
+// constexpr I ranges::reverse(I first, S last);
+// template<bidirectional_range R>
+// requires permutable<iterator_t<R>>
+// constexpr borrowed_iterator_t<R> ranges::reverse(R&& r);
+
+#include <algorithm>
+#include <array>
+#include <concepts>
+#include <ranges>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+
+template <class Iter, class Sent = sentinel_wrapper<Iter>>
+concept HasReverseIt = requires (Iter first, Sent last) { std::ranges::reverse(first, last); };
+
+static_assert(HasReverseIt<int*>);
+static_assert(!HasReverseIt<BidirectionalIteratorNotDerivedFrom>);
+static_assert(!HasReverseIt<BidirectionalIteratorNotDecrementable>);
+static_assert(!HasReverseIt<PermutableNotForwardIterator>);
+static_assert(!HasReverseIt<PermutableNotSwappable>);
+
+
+template <class Range>
+concept HasReverseR = requires (Range range) { std::ranges::reverse(range); };
+
+static_assert(HasReverseR<UncheckedRange<int*>>);
+static_assert(!HasReverseR<BidirectionalRangeNotDerivedFrom>);
+static_assert(!HasReverseR<BidirectionalRangeNotDecrementable>);
+static_assert(!HasReverseR<PermutableRangeNotForwardIterator>);
+static_assert(!HasReverseR<PermutableRangeNotSwappable>);
+
+template <class Iter, class Sent, size_t N>
+constexpr void test(std::array<int, N> value, std::array<int, N> expected) {
+ {
+ auto val = value;
+ std::same_as<Iter> decltype(auto) ret = std::ranges::reverse(Iter(val.data()), Sent(Iter(val.data() + val.size())));
+ assert(val == expected);
+ assert(base(ret) == val.data() + val.size());
+ }
+ {
+ auto val = value;
+ auto range = std::ranges::subrange(Iter(val.data()), Sent(Iter(val.data() + val.size())));
+ std::same_as<Iter> decltype(auto) ret = std::ranges::reverse(range);
+ assert(val == expected);
+ assert(base(ret) == val.data() + val.size());
+ }
+}
+
+template <class Iter, class Sent = Iter>
+constexpr void test_iterators() {
+ // simple test
+ test<Iter, Sent, 4>({1, 2, 3, 4}, {4, 3, 2, 1});
+ // check that an odd number of elements works
+ test<Iter, Sent, 7>({1, 2, 3, 4, 5, 6, 7}, {7, 6, 5, 4, 3, 2, 1});
+ // check that an empty range works
+ test<Iter, Sent, 0>({}, {});
+ // check that a single element works
+ test<Iter, Sent, 1>({5}, {5});
+}
+
+struct SwapCounter {
+ int* counter;
+ constexpr SwapCounter(int* counter_) : counter(counter_) {}
+ friend constexpr void swap(SwapCounter& lhs, SwapCounter&) { ++*lhs.counter; }
+};
+
+constexpr bool test() {
+ test_iterators<bidirectional_iterator<int*>>();
+ test_iterators<bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>();
+ test_iterators<random_access_iterator<int*>>();
+ test_iterators<random_access_iterator<int*>, sentinel_wrapper<random_access_iterator<int*>>>();
+ test_iterators<contiguous_iterator<int*>>();
+ test_iterators<contiguous_iterator<int*>, sentinel_wrapper<contiguous_iterator<int*>>>();
+ test_iterators<int*>();
+
+ // check that std::ranges::dangling is returned
+ {
+ [[maybe_unused]] std::same_as<std::ranges::dangling> auto ret = std::ranges::reverse(std::array {1, 2, 3, 4});
+ }
+
+ {
+ {
+ int counter = 0;
+ SwapCounter a[] = {&counter, &counter, &counter, &counter};
+ std::ranges::reverse(a);
+ assert(counter == 2);
+ }
+ {
+ int counter = 0;
+ SwapCounter a[] = {&counter, &counter, &counter, &counter};
+ std::ranges::reverse(a, a + 4);
+ assert(counter == 2);
+ }
+ }
+
+ return true;
+}
+
+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
index 812b58595b9a4..03c0563597b98 100644
--- 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
@@ -125,7 +125,7 @@ static_assert(test(std::ranges::mismatch, a, a));
//static_assert(test(std::ranges::replace_copy, a, a, 42, 43));
//static_assert(test(std::ranges::replace_copy_if, a, a, odd, 43));
//static_assert(test(std::ranges::replace_if, a, odd, 43));
-//static_assert(test(std::ranges::reverse, a));
+static_assert(test(std::ranges::reverse, a));
//static_assert(test(std::ranges::reverse_copy, a, a));
//static_assert(test(std::ranges::rotate, a, a+5));
//static_assert(test(std::ranges::rotate_copy, a, a+5, a));
diff --git a/libcxx/test/support/almost_satisfies_types.h b/libcxx/test/support/almost_satisfies_types.h
index 95d9b4736c3b7..49984a42c88e4 100644
--- a/libcxx/test/support/almost_satisfies_types.h
+++ b/libcxx/test/support/almost_satisfies_types.h
@@ -139,4 +139,85 @@ class WeaklyIncrementableNotMovable {
static_assert(!std::movable<WeaklyIncrementableNotMovable>);
static_assert(!std::weakly_incrementable<WeaklyIncrementableNotMovable>);
+class BidirectionalIteratorNotDerivedFrom {
+public:
+ using
diff erence_type = long;
+ using value_type = int;
+ using iterator_category = std::forward_iterator_tag;
+
+ BidirectionalIteratorNotDerivedFrom& operator++();
+ BidirectionalIteratorNotDerivedFrom operator++(int);
+ BidirectionalIteratorNotDerivedFrom& operator--();
+ BidirectionalIteratorNotDerivedFrom operator--(int);
+ int& operator*() const;
+
+ bool operator==(const BidirectionalIteratorNotDerivedFrom&) const = default;
+};
+
+using BidirectionalRangeNotDerivedFrom = UncheckedRange<BidirectionalIteratorNotDerivedFrom>;
+
+static_assert(std::forward_iterator<BidirectionalIteratorNotDerivedFrom>);
+static_assert(!std::bidirectional_iterator<BidirectionalIteratorNotDerivedFrom>);
+static_assert(!std::ranges::bidirectional_range<BidirectionalRangeNotDerivedFrom>);
+
+class BidirectionalIteratorNotDecrementable {
+public:
+ using
diff erence_type = long;
+ using value_type = int;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ BidirectionalIteratorNotDecrementable& operator++();
+ BidirectionalIteratorNotDecrementable operator++(int);
+ int& operator*() const;
+
+ bool operator==(const BidirectionalIteratorNotDecrementable&) const = default;
+};
+
+using BidirectionalRangeNotDecrementable = UncheckedRange<BidirectionalIteratorNotDecrementable>;
+
+static_assert(std::forward_iterator<BidirectionalIteratorNotDecrementable>);
+static_assert(!std::bidirectional_iterator<BidirectionalIteratorNotDecrementable>);
+static_assert(!std::ranges::bidirectional_range<BidirectionalRangeNotDecrementable>);
+
+class PermutableNotForwardIterator {
+public:
+ using
diff erence_type = long;
+ using value_type = int;
+ using iterator_category = std::input_iterator_tag;
+
+ PermutableNotForwardIterator& operator++();
+ void operator++(int);
+ int& operator*() const;
+};
+
+using PermutableRangeNotForwardIterator = UncheckedRange<PermutableNotForwardIterator>;
+
+static_assert(std::input_iterator<PermutableNotForwardIterator>);
+static_assert(!std::forward_iterator<PermutableNotForwardIterator>);
+static_assert(!std::permutable<PermutableNotForwardIterator>);
+
+class PermutableNotSwappable {
+public:
+ class NotSwappable {
+ NotSwappable(NotSwappable&&) = delete;
+ };
+
+ using
diff erence_type = long;
+ using value_type = NotSwappable;
+ using iterator_category = std::contiguous_iterator_tag;
+
+ PermutableNotSwappable& operator++();
+ PermutableNotSwappable operator++(int);
+ NotSwappable& operator*() const;
+
+ bool operator==(const PermutableNotSwappable&) const = default;
+};
+
+using PermutableRangeNotSwappable = UncheckedRange<PermutableNotSwappable>;
+
+static_assert(std::input_iterator<PermutableNotSwappable>);
+static_assert(std::forward_iterator<PermutableNotSwappable>);
+static_assert(!std::permutable<PermutableNotSwappable>);
+static_assert(!std::indirectly_swappable<PermutableNotSwappable>);
+
#endif // ALMOST_SATISFIES_TYPES_H
More information about the libcxx-commits
mailing list