Skip to content

Commit 616a3cc

Browse files
committed
[libc++] Add the std::views::reverse range adaptor
Differential Revision: https://reviews.llvm.org/D110426
1 parent f5755c0 commit 616a3cc

File tree

3 files changed

+283
-0
lines changed

3 files changed

+283
-0
lines changed

libcxx/include/__ranges/reverse_view.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919
#include <__ranges/concepts.h>
2020
#include <__ranges/enable_borrowed_range.h>
2121
#include <__ranges/non_propagating_cache.h>
22+
#include <__ranges/range_adaptor.h>
2223
#include <__ranges/size.h>
24+
#include <__ranges/subrange.h>
2325
#include <__ranges/view_interface.h>
26+
#include <__utility/forward.h>
2427
#include <__utility/move.h>
2528
#include <type_traits>
2629

@@ -104,6 +107,80 @@ namespace ranges {
104107

105108
template<class _Tp>
106109
inline constexpr bool enable_borrowed_range<reverse_view<_Tp>> = enable_borrowed_range<_Tp>;
110+
111+
namespace views {
112+
namespace __reverse {
113+
template<class _Tp>
114+
constexpr bool __is_reverse_view = false;
115+
116+
template<class _Tp>
117+
constexpr bool __is_reverse_view<reverse_view<_Tp>> = true;
118+
119+
template<class _Tp>
120+
constexpr bool __is_sized_reverse_subrange = false;
121+
122+
template<class _Iter>
123+
constexpr bool __is_sized_reverse_subrange<subrange<reverse_iterator<_Iter>, reverse_iterator<_Iter>, subrange_kind::sized>> = true;
124+
125+
template<class _Tp>
126+
constexpr bool __is_unsized_reverse_subrange = false;
127+
128+
template<class _Iter, subrange_kind _Kind>
129+
constexpr bool __is_unsized_reverse_subrange<subrange<reverse_iterator<_Iter>, reverse_iterator<_Iter>, _Kind>> = _Kind == subrange_kind::unsized;
130+
131+
template<class _Tp>
132+
struct __unwrapped_reverse_subrange {
133+
using type = void; // avoid SFINAE-ing out the overload below -- let the concept requirements do it for better diagnostics
134+
};
135+
136+
template<class _Iter, subrange_kind _Kind>
137+
struct __unwrapped_reverse_subrange<subrange<reverse_iterator<_Iter>, reverse_iterator<_Iter>, _Kind>> {
138+
using type = subrange<_Iter, _Iter, _Kind>;
139+
};
140+
141+
struct __fn : __range_adaptor_closure<__fn> {
142+
template<class _Range>
143+
requires __is_reverse_view<remove_cvref_t<_Range>>
144+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
145+
constexpr auto operator()(_Range&& __range) const
146+
noexcept(noexcept(_VSTD::forward<_Range>(__range).base()))
147+
-> decltype( _VSTD::forward<_Range>(__range).base())
148+
{ return _VSTD::forward<_Range>(__range).base(); }
149+
150+
template<class _Range,
151+
class _UnwrappedSubrange = typename __unwrapped_reverse_subrange<remove_cvref_t<_Range>>::type>
152+
requires __is_sized_reverse_subrange<remove_cvref_t<_Range>>
153+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
154+
constexpr auto operator()(_Range&& __range) const
155+
noexcept(noexcept(_UnwrappedSubrange(__range.end().base(), __range.begin().base(), __range.size())))
156+
-> decltype( _UnwrappedSubrange(__range.end().base(), __range.begin().base(), __range.size()))
157+
{ return _UnwrappedSubrange(__range.end().base(), __range.begin().base(), __range.size()); }
158+
159+
template<class _Range,
160+
class _UnwrappedSubrange = typename __unwrapped_reverse_subrange<remove_cvref_t<_Range>>::type>
161+
requires __is_unsized_reverse_subrange<remove_cvref_t<_Range>>
162+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
163+
constexpr auto operator()(_Range&& __range) const
164+
noexcept(noexcept(_UnwrappedSubrange(__range.end().base(), __range.begin().base())))
165+
-> decltype( _UnwrappedSubrange(__range.end().base(), __range.begin().base()))
166+
{ return _UnwrappedSubrange(__range.end().base(), __range.begin().base()); }
167+
168+
template<class _Range>
169+
requires (!__is_reverse_view<remove_cvref_t<_Range>> &&
170+
!__is_sized_reverse_subrange<remove_cvref_t<_Range>> &&
171+
!__is_unsized_reverse_subrange<remove_cvref_t<_Range>>)
172+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
173+
constexpr auto operator()(_Range&& __range) const
174+
noexcept(noexcept(reverse_view{_VSTD::forward<_Range>(__range)}))
175+
-> decltype( reverse_view{_VSTD::forward<_Range>(__range)})
176+
{ return reverse_view{_VSTD::forward<_Range>(__range)}; }
177+
};
178+
}
179+
180+
inline namespace __cpo {
181+
inline constexpr auto reverse = __reverse::__fn{};
182+
}
183+
} // namespace views
107184
} // namespace ranges
108185

109186
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
// UNSUPPORTED: libcpp-no-concepts
11+
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
12+
// REQUIRES: libc++
13+
14+
// Test the libc++ extension that std::views::reverse is marked as [[nodiscard]] to avoid
15+
// the potential for user mistakenly thinking they're calling an algorithm.
16+
17+
#include <ranges>
18+
19+
void test() {
20+
int range[] = {1, 2, 3};
21+
22+
std::views::reverse(range); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
23+
range | std::views::reverse; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
24+
std::views::reverse | std::views::reverse; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
25+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
// UNSUPPORTED: libcpp-no-concepts
11+
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
12+
13+
// std::views::reverse
14+
15+
#include <ranges>
16+
17+
#include <cassert>
18+
#include <concepts>
19+
#include <iterator>
20+
#include <utility>
21+
22+
#include "types.h"
23+
24+
template <class View, class T>
25+
concept CanBePiped = requires (View&& view, T&& t) {
26+
{ std::forward<View>(view) | std::forward<T>(t) };
27+
};
28+
29+
constexpr bool test() {
30+
int buf[] = {1, 2, 3};
31+
32+
// views::reverse(x) is equivalent to x.base() if x is a reverse_view
33+
{
34+
{
35+
BidirRange view(buf, buf + 3);
36+
std::ranges::reverse_view<BidirRange> reversed(view);
37+
std::same_as<BidirRange> auto result = std::views::reverse(reversed);
38+
assert(result.begin_ == buf);
39+
assert(result.end_ == buf + 3);
40+
}
41+
{
42+
// Common use case is worth testing
43+
BidirRange view(buf, buf + 3);
44+
std::same_as<BidirRange> auto result = std::views::reverse(std::views::reverse(view));
45+
assert(result.begin_ == buf);
46+
assert(result.end_ == buf + 3);
47+
}
48+
}
49+
50+
// views::reverse(x) is equivalent to subrange{end, begin, size} if x is a
51+
// sized subrange over reverse iterators
52+
{
53+
using It = bidirectional_iterator<int*>;
54+
using Subrange = std::ranges::subrange<It, It, std::ranges::subrange_kind::sized>;
55+
56+
using ReverseIt = std::reverse_iterator<It>;
57+
using ReverseSubrange = std::ranges::subrange<ReverseIt, ReverseIt, std::ranges::subrange_kind::sized>;
58+
59+
{
60+
BidirRange view(buf, buf + 3);
61+
ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
62+
std::same_as<Subrange> auto result = std::views::reverse(subrange);
63+
assert(result.begin().base() == buf);
64+
assert(result.end().base() == buf + 3);
65+
}
66+
{
67+
// std::move into views::reverse
68+
BidirRange view(buf, buf + 3);
69+
ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
70+
std::same_as<Subrange> auto result = std::views::reverse(std::move(subrange));
71+
assert(result.begin().base() == buf);
72+
assert(result.end().base() == buf + 3);
73+
}
74+
{
75+
// with a const subrange
76+
BidirRange view(buf, buf + 3);
77+
ReverseSubrange const subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
78+
std::same_as<Subrange> auto result = std::views::reverse(subrange);
79+
assert(result.begin().base() == buf);
80+
assert(result.end().base() == buf + 3);
81+
}
82+
}
83+
84+
// views::reverse(x) is equivalent to subrange{end, begin} if x is an
85+
// unsized subrange over reverse iterators
86+
{
87+
using It = bidirectional_iterator<int*>;
88+
using Subrange = std::ranges::subrange<It, It, std::ranges::subrange_kind::unsized>;
89+
90+
using ReverseIt = std::reverse_iterator<It>;
91+
using ReverseSubrange = std::ranges::subrange<ReverseIt, ReverseIt, std::ranges::subrange_kind::unsized>;
92+
93+
{
94+
BidirRange view(buf, buf + 3);
95+
ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
96+
std::same_as<Subrange> auto result = std::views::reverse(subrange);
97+
assert(result.begin().base() == buf);
98+
assert(result.end().base() == buf + 3);
99+
}
100+
{
101+
// std::move into views::reverse
102+
BidirRange view(buf, buf + 3);
103+
ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
104+
std::same_as<Subrange> auto result = std::views::reverse(std::move(subrange));
105+
assert(result.begin().base() == buf);
106+
assert(result.end().base() == buf + 3);
107+
}
108+
{
109+
// with a const subrange
110+
BidirRange view(buf, buf + 3);
111+
ReverseSubrange const subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
112+
std::same_as<Subrange> auto result = std::views::reverse(subrange);
113+
assert(result.begin().base() == buf);
114+
assert(result.end().base() == buf + 3);
115+
}
116+
}
117+
118+
// Otherwise, views::reverse(x) is equivalent to ranges::reverse_view{x}
119+
{
120+
BidirRange view(buf, buf + 3);
121+
std::same_as<std::ranges::reverse_view<BidirRange>> auto result = std::views::reverse(view);
122+
assert(result.begin().base().base() == buf + 3);
123+
assert(result.end().base().base() == buf);
124+
}
125+
126+
// Test that std::views::reverse is a range adaptor
127+
{
128+
// Test `v | views::reverse`
129+
{
130+
BidirRange view(buf, buf + 3);
131+
std::same_as<std::ranges::reverse_view<BidirRange>> auto result = view | std::views::reverse;
132+
assert(result.begin().base().base() == buf + 3);
133+
assert(result.end().base().base() == buf);
134+
}
135+
136+
// Test `adaptor | views::reverse`
137+
{
138+
BidirRange view(buf, buf + 3);
139+
auto f = [](int i) { return i; };
140+
auto const partial = std::views::transform(f) | std::views::reverse;
141+
using Result = std::ranges::reverse_view<std::ranges::transform_view<BidirRange, decltype(f)>>;
142+
std::same_as<Result> auto result = partial(view);
143+
assert(result.begin().base().base().base() == buf + 3);
144+
assert(result.end().base().base().base() == buf);
145+
}
146+
147+
// Test `views::reverse | adaptor`
148+
{
149+
BidirRange view(buf, buf + 3);
150+
auto f = [](int i) { return i; };
151+
auto const partial = std::views::reverse | std::views::transform(f);
152+
using Result = std::ranges::transform_view<std::ranges::reverse_view<BidirRange>, decltype(f)>;
153+
std::same_as<Result> auto result = partial(view);
154+
assert(result.begin().base().base().base() == buf + 3);
155+
assert(result.end().base().base().base() == buf);
156+
}
157+
158+
// Check SFINAE friendliness
159+
{
160+
struct NotABidirRange { };
161+
static_assert(!std::is_invocable_v<decltype(std::views::reverse)>);
162+
static_assert(!std::is_invocable_v<decltype(std::views::reverse), NotABidirRange>);
163+
static_assert( CanBePiped<BidirRange, decltype(std::views::reverse)>);
164+
static_assert( CanBePiped<BidirRange&, decltype(std::views::reverse)>);
165+
static_assert(!CanBePiped<NotABidirRange, decltype(std::views::reverse)>);
166+
}
167+
}
168+
169+
{
170+
static_assert(std::same_as<decltype(std::views::reverse), decltype(std::ranges::views::reverse)>);
171+
}
172+
173+
return true;
174+
}
175+
176+
int main(int, char**) {
177+
test();
178+
static_assert(test());
179+
180+
return 0;
181+
}

0 commit comments

Comments
 (0)