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
25struct SizedView : std::ranges::view_base {
26 int* begin_ = nullptr;
27 int* end_ = nullptr;
28 constexpr SizedView(int* begin, int* end) : begin_(begin), end_(end) {}
29
30 constexpr auto begin() const { return forward_iterator<int*>(begin_); }
31 constexpr auto end() const { return sized_sentinel<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
32};
33static_assert(std::ranges::forward_range<SizedView>);
34static_assert(std::ranges::sized_range<SizedView>);
35static_assert(std::ranges::view<SizedView>);
36
37struct 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;
43 constexpr SizedViewWithUnsizedSentinel(int* begin, int* end) : begin_(begin), end_(end) {}
44
45 constexpr auto begin() const { return iterator(begin_); }
46 constexpr auto end() const { return sentinel(iterator(end_)); }
47 constexpr std::size_t size() const { return end_ - begin_; }
48};
49static_assert(std::ranges::random_access_range<SizedViewWithUnsizedSentinel>);
50static_assert(std::ranges::sized_range<SizedViewWithUnsizedSentinel>);
51static_assert(!std::sized_sentinel_for<SizedViewWithUnsizedSentinel::sentinel, SizedViewWithUnsizedSentinel::iterator>);
52static_assert(std::ranges::view<SizedViewWithUnsizedSentinel>);
53
54template <class T>
55constexpr 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
64constexpr 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(input: 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
251int main(int, char**) {
252 test();
253 static_assert(test());
254
255 return 0;
256}
257

source code of libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp