xref: /llvm-project/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp (revision ba2236d3000645d3127f972aa7ac1844c47e299c)
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 
11 // std::views::drop
12 
13 #include <ranges>
14 
15 #include <array>
16 #include <cassert>
17 #include <concepts>
18 #include <span>
19 #include <string_view>
20 #include <utility>
21 
22 #include "test_iterators.h"
23 #include "test_range.h"
24 
25 struct SizedView : std::ranges::view_base {
26   int* begin_ = nullptr;
27   int* end_ = nullptr;
SizedViewSizedView28   constexpr SizedView(int* begin, int* end) : begin_(begin), end_(end) {}
29 
beginSizedView30   constexpr auto begin() const { return forward_iterator<int*>(begin_); }
endSizedView31   constexpr auto end() const { return sized_sentinel<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
32 };
33 static_assert(std::ranges::forward_range<SizedView>);
34 static_assert(std::ranges::sized_range<SizedView>);
35 static_assert(std::ranges::view<SizedView>);
36 
37 struct SizedViewWithUnsizedSentinel : std::ranges::view_base {
38   using iterator = random_access_iterator<int*>;
39   using sentinel = sentinel_wrapper<random_access_iterator<int*>>;
40 
41   int* begin_ = nullptr;
42   int* end_ = nullptr;
SizedViewWithUnsizedSentinelSizedViewWithUnsizedSentinel43   constexpr SizedViewWithUnsizedSentinel(int* begin, int* end) : begin_(begin), end_(end) {}
44 
beginSizedViewWithUnsizedSentinel45   constexpr auto begin() const { return iterator(begin_); }
endSizedViewWithUnsizedSentinel46   constexpr auto end() const { return sentinel(iterator(end_)); }
sizeSizedViewWithUnsizedSentinel47   constexpr std::size_t size() const { return end_ - begin_; }
48 };
49 static_assert(std::ranges::random_access_range<SizedViewWithUnsizedSentinel>);
50 static_assert(std::ranges::sized_range<SizedViewWithUnsizedSentinel>);
51 static_assert(!std::sized_sentinel_for<SizedViewWithUnsizedSentinel::sentinel, SizedViewWithUnsizedSentinel::iterator>);
52 static_assert(std::ranges::view<SizedViewWithUnsizedSentinel>);
53 
54 template <class T>
test_small_range(const T & input)55 constexpr void test_small_range(const T& input) {
56   constexpr int N = 100;
57   auto size = std::ranges::size(input);
58   assert(size < N);
59 
60   auto result = input | std::views::drop(N);
61   assert(result.empty());
62 }
63 
test()64 constexpr bool test() {
65   constexpr int N = 8;
66   int buf[N] = {1, 2, 3, 4, 5, 6, 7, 8};
67 
68   // Test that `std::views::drop` is a range adaptor.
69   {
70     using SomeView = SizedView;
71 
72     // Test `view | views::drop`
73     {
74       SomeView view(buf, buf + N);
75       std::same_as<std::ranges::drop_view<SomeView>> decltype(auto) result = view | std::views::drop(3);
76       assert(result.base().begin_ == buf);
77       assert(result.base().end_ == buf + N);
78       assert(base(result.begin()) == buf + 3);
79       assert(base(base(result.end())) == buf + N);
80       assert(result.size() == 5);
81     }
82 
83     // Test `adaptor | views::drop`
84     {
85       SomeView view(buf, buf + N);
86       auto f = [](int i) { return i; };
87       auto const partial = std::views::transform(f) | std::views::drop(3);
88 
89       using Result = std::ranges::drop_view<std::ranges::transform_view<SomeView, decltype(f)>>;
90       std::same_as<Result> decltype(auto) result = partial(view);
91       assert(result.base().base().begin_ == buf);
92       assert(result.base().base().end_ == buf + N);
93       assert(base(result.begin().base()) == buf + 3);
94       assert(base(base(result.end().base())) == buf + N);
95       assert(result.size() == 5);
96     }
97 
98     // Test `views::drop | adaptor`
99     {
100       SomeView view(buf, buf + N);
101       auto f = [](int i) { return i; };
102       auto const partial = std::views::drop(3) | std::views::transform(f);
103 
104       using Result = std::ranges::transform_view<std::ranges::drop_view<SomeView>, decltype(f)>;
105       std::same_as<Result> decltype(auto) result = partial(view);
106       assert(result.base().base().begin_ == buf);
107       assert(result.base().base().end_ == buf + N);
108       assert(base(result.begin().base()) == buf + 3);
109       assert(base(base(result.end().base())) == buf + N);
110       assert(result.size() == 5);
111     }
112 
113     // Check SFINAE friendliness
114     {
115       struct NotAView { };
116       static_assert(!std::is_invocable_v<decltype(std::views::drop)>);
117       static_assert(!std::is_invocable_v<decltype(std::views::drop), NotAView, int>);
118       static_assert( CanBePiped<SomeView&,   decltype(std::views::drop(3))>);
119       static_assert( CanBePiped<int(&)[10],  decltype(std::views::drop(3))>);
120       static_assert(!CanBePiped<int(&&)[10], decltype(std::views::drop(3))>);
121       static_assert(!CanBePiped<NotAView,    decltype(std::views::drop(3))>);
122 
123       static_assert(!CanBePiped<SomeView&,   decltype(std::views::drop(/*n=*/NotAView{}))>);
124     }
125   }
126 
127   {
128     static_assert(std::same_as<decltype(std::views::drop), decltype(std::ranges::views::drop)>);
129   }
130 
131   // `views::drop(empty_view, n)` returns an `empty_view`.
132   {
133     using Result = std::ranges::empty_view<int>;
134     [[maybe_unused]] std::same_as<Result> decltype(auto) result = std::views::empty<int> | std::views::drop(3);
135   }
136 
137   // `views::drop(span, n)` returns a `span`.
138   {
139     std::span<int> s(buf);
140     std::same_as<decltype(s)> decltype(auto) result = s | std::views::drop(5);
141     assert(result.size() == 3);
142   }
143 
144   // `views::drop(span, n)` returns a `span` with a dynamic extent, regardless of the input `span`.
145   {
146     std::span<int, 8> s(buf);
147     std::same_as<std::span<int, std::dynamic_extent>> decltype(auto) result = s | std::views::drop(3);
148     assert(result.size() == 5);
149   }
150 
151   // `views::drop(string_view, n)` returns a `string_view`.
152   {
153     {
154       std::string_view sv = "abcdef";
155       std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::drop(2);
156       assert(result.size() == 4);
157     }
158 
159     {
160       std::u32string_view sv = U"abcdef";
161       std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::drop(2);
162       assert(result.size() == 4);
163     }
164   }
165 
166   // `views::drop(iota_view, n)` returns an `iota_view`.
167   {
168     auto iota = std::views::iota(1, 8);
169     // The second template argument of the resulting `iota_view` is different because it has to be able to hold
170     // the `range_difference_t` of the input `iota_view`.
171     using Result = std::ranges::iota_view<int, int>;
172     std::same_as<Result> decltype(auto) result = iota | std::views::drop(3);
173     assert(result.size() == 4);
174     assert(*result.begin() == 4);
175     assert(*std::ranges::next(result.begin(), 3) == 7);
176   }
177 
178   // `views::drop(subrange, n)` returns a `subrange` when `subrange::StoreSize == false`.
179   {
180     auto subrange = std::ranges::subrange(buf, buf + N);
181     LIBCPP_STATIC_ASSERT(!decltype(subrange)::_StoreSize);
182 
183     using Result = std::ranges::subrange<int*>;
184     std::same_as<Result> decltype(auto) result = subrange | std::views::drop(3);
185     assert(result.size() == 5);
186   }
187 
188   // `views::drop(subrange, n)` returns a `subrange` when `subrange::StoreSize == true`.
189   {
190     using View = SizedViewWithUnsizedSentinel;
191     View view(buf, buf + N);
192 
193     using Subrange = std::ranges::subrange<View::iterator, View::sentinel, std::ranges::subrange_kind::sized>;
194     auto subrange = Subrange(view.begin(), view.end(), std::ranges::distance(view.begin(), view.end()));
195     LIBCPP_STATIC_ASSERT(decltype(subrange)::_StoreSize);
196 
197     std::same_as<Subrange> decltype(auto) result = subrange | std::views::drop(3);
198     assert(result.size() == 5);
199   }
200 
201   // `views::drop(subrange, n)` doesn't return a `subrange` if it's not a random access range.
202   {
203     SizedView v(buf, buf + N);
204     auto subrange = std::ranges::subrange(v.begin(), v.end());
205 
206     using Result = std::ranges::drop_view<std::ranges::subrange<forward_iterator<int*>,
207         sized_sentinel<forward_iterator<int*>>>>;
208     std::same_as<Result> decltype(auto) result = subrange | std::views::drop(3);
209     assert(result.size() == 5);
210   }
211 
212   // When the size of the input range `s` is shorter than `n`, an `empty_view` is returned.
213   {
214     test_small_range(std::span(buf));
215     test_small_range(std::string_view("abcdef"));
216     test_small_range(std::ranges::subrange(buf, buf + N));
217     test_small_range(std::views::iota(1, 8));
218   }
219 
220 #if TEST_STD_VER >= 23
221   // `views::drop(repeat_view, n)` returns a `repeat_view` when `repeat_view` models `sized_range`.
222   {
223     auto repeat                                = std::ranges::repeat_view<int, int>(1, 8);
224     using Result                               = std::ranges::repeat_view<int, int>;
225     std::same_as<Result> decltype(auto) result = repeat | std::views::drop(3);
226     static_assert(std::ranges::sized_range<Result>);
227     assert(result.size() == 5);
228     assert(*result.begin() == 1);
229   }
230 
231   // `views::drop(repeat_view, n)` returns a `repeat_view` when `repeat_view` doesn't model `sized_range`.
232   {
233     auto repeat                                = std::ranges::repeat_view<int>(1);
234     using Result                               = std::ranges::repeat_view<int, std::unreachable_sentinel_t>;
235     std::same_as<Result> decltype(auto) result = repeat | std::views::drop(3);
236     static_assert(!std::ranges::sized_range<Result>);
237     static_assert(std::same_as<std::unreachable_sentinel_t, decltype(result.end())>);
238   }
239 #endif
240 
241   // Test that it's possible to call `std::views::drop` with any single argument as long as the resulting closure is
242   // never invoked. There is no good use case for it, but it's valid.
243   {
244     struct X { };
245     [[maybe_unused]] auto partial = std::views::drop(X{});
246   }
247 
248   return true;
249 }
250 
main(int,char **)251 int main(int, char**) {
252   test();
253   static_assert(test());
254 
255   return 0;
256 }
257