[libc++] Implement ranges::reverse

Reviewed By: var-const, #libc

Spies: libcxx-commits, mgorny

Differential Revision: https://reviews.llvm.org/D125752
This commit is contained in:
Nikolas Klauser 2022-05-24 10:32:50 +02:00
parent b07880454b
commit 1d1a191edc
9 changed files with 299 additions and 2 deletions

View File

@ -67,7 +67,7 @@ Merge,set_symmetric_difference,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

1 Category,Algorithm,Assignee,CL,Complete
67 Merge,set_union,Not assigned,n/a,Not started
68 Permutation,remove,Not assigned,n/a,Not started
69 Permutation,remove_if,Not assigned,n/a,Not started
70 Permutation,reverse,Not assigned,n/a,Not started Permutation,reverse,Nikolas Klauser,`D125752 <https://llvm.org/D125752>`_,✅
71 Permutation,rotate,Not assigned,n/a,Not started
72 Permutation,shuffle,Not assigned,n/a,Not started
73 Permutation,unique,Not assigned,n/a,Not started

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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" }

View File

@ -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'}}

View File

@ -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;
}

View File

@ -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));

View File

@ -139,4 +139,85 @@ public:
static_assert(!std::movable<WeaklyIncrementableNotMovable>);
static_assert(!std::weakly_incrementable<WeaklyIncrementableNotMovable>);
class BidirectionalIteratorNotDerivedFrom {
public:
using difference_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 difference_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 difference_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 difference_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