forked from OSchip/llvm-project
[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:
parent
b07880454b
commit
1d1a191edc
|
@ -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
|
||||
|
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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>
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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'}}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue