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