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::take
13 
14 #include <ranges>
15 
16 #include <cassert>
17 #include <concepts>
18 #include <span>
19 #include <string_view>
20 #include <utility>
21 #include "test_iterators.h"
22 
23 template <class View, class T>
24 concept CanBePiped = requires (View&& view, T&& t) {
25   { std::forward<View>(view) | std::forward<T>(t) };
26 };
27 
28 struct SizedView : std::ranges::view_base {
29   int* begin_ = nullptr;
30   int* end_ = nullptr;
SizedViewSizedView31   constexpr SizedView(int* begin, int* end) : begin_(begin), end_(end) {}
32 
beginSizedView33   constexpr auto begin() const { return forward_iterator<int*>(begin_); }
endSizedView34   constexpr auto end() const { return sized_sentinel<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
35 };
36 static_assert(std::ranges::forward_range<SizedView>);
37 static_assert(std::ranges::sized_range<SizedView>);
38 static_assert(std::ranges::view<SizedView>);
39 
40 template <class T>
test_small_range(const T & input)41 constexpr void test_small_range(const T& input) {
42   constexpr int N = 100;
43   auto size = std::ranges::size(input);
44 
45   auto result = input | std::views::take(N);
46   assert(size < N);
47   assert(result.size() == size);
48 }
49 
test()50 constexpr bool test() {
51   constexpr int N = 8;
52   int buf[N] = {1, 2, 3, 4, 5, 6, 7, 8};
53 
54   // Test that `std::views::take` is a range adaptor.
55   {
56     using SomeView = SizedView;
57 
58     // Test `view | views::take`
59     {
60       SomeView view(buf, buf + N);
61       std::same_as<std::ranges::take_view<SomeView>> decltype(auto) result = view | std::views::take(3);
62       assert(result.base().begin_ == buf);
63       assert(result.base().end_ == buf + N);
64       assert(result.size() == 3);
65     }
66 
67     // Test `adaptor | views::take`
68     {
69       SomeView view(buf, buf + N);
70       auto f = [](int i) { return i; };
71       auto const partial = std::views::transform(f) | std::views::take(3);
72 
73       using Result = std::ranges::take_view<std::ranges::transform_view<SomeView, decltype(f)>>;
74       std::same_as<Result> decltype(auto) result = partial(view);
75       assert(result.base().base().begin_ == buf);
76       assert(result.base().base().end_ == buf + N);
77       assert(result.size() == 3);
78     }
79 
80     // Test `views::take | adaptor`
81     {
82       SomeView view(buf, buf + N);
83       auto f = [](int i) { return i; };
84       auto const partial = std::views::take(3) | std::views::transform(f);
85 
86       using Result = std::ranges::transform_view<std::ranges::take_view<SomeView>, decltype(f)>;
87       std::same_as<Result> decltype(auto) result = partial(view);
88       assert(result.base().base().begin_ == buf);
89       assert(result.base().base().end_ == buf + N);
90       assert(result.size() == 3);
91     }
92 
93     // Check SFINAE friendliness
94     {
95       struct NotAView { };
96       static_assert(!std::is_invocable_v<decltype(std::views::take)>);
97       static_assert(!std::is_invocable_v<decltype(std::views::take), NotAView, int>);
98       static_assert( CanBePiped<SomeView&,   decltype(std::views::take(3))>);
99       static_assert( CanBePiped<int(&)[10],  decltype(std::views::take(3))>);
100       static_assert(!CanBePiped<int(&&)[10], decltype(std::views::take(3))>);
101       static_assert(!CanBePiped<NotAView,    decltype(std::views::take(3))>);
102 
103       static_assert(!CanBePiped<SomeView&,   decltype(std::views::take(/*n=*/NotAView{}))>);
104     }
105   }
106 
107   {
108     static_assert(std::same_as<decltype(std::views::take), decltype(std::ranges::views::take)>);
109   }
110 
111   // `views::take(empty_view, n)` returns an `empty_view`.
112   {
113     using Result = std::ranges::empty_view<int>;
114     [[maybe_unused]] std::same_as<Result> decltype(auto) result = std::views::empty<int> | std::views::take(3);
115   }
116 
117   // `views::take(span, n)` returns a `span`.
118   {
119     std::span<int> s(buf);
120     std::same_as<decltype(s)> decltype(auto) result = s | std::views::take(3);
121     assert(result.size() == 3);
122   }
123 
124   // `views::take(span, n)` returns a `span` with a dynamic extent, regardless of the input `span`.
125   {
126     std::span<int, 8> s(buf);
127     std::same_as<std::span<int, std::dynamic_extent>> decltype(auto) result = s | std::views::take(3);
128     assert(result.size() == 3);
129   }
130 
131   // `views::take(string_view, n)` returns a `string_view`.
132   {
133     {
134       std::string_view sv = "abcdef";
135       std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::take(3);
136       assert(result.size() == 3);
137     }
138 
139     {
140       std::u32string_view sv = U"abcdef";
141       std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::take(3);
142       assert(result.size() == 3);
143     }
144   }
145 
146   // `views::take(subrange, n)` returns a `subrange`.
147   {
148     auto subrange = std::ranges::subrange(buf, buf + N);
149     using Result = std::ranges::subrange<int*>;
150     std::same_as<Result> decltype(auto) result = subrange | std::views::take(3);
151     assert(result.size() == 3);
152   }
153 
154   // `views::take(subrange, n)` doesn't return a `subrange` if it's not a random access range.
155   {
156     SizedView v(buf, buf + N);
157     auto subrange = std::ranges::subrange(v.begin(), v.end());
158 
159     using Result = std::ranges::take_view<std::ranges::subrange<forward_iterator<int*>,
160         sized_sentinel<forward_iterator<int*>>>>;
161     std::same_as<Result> decltype(auto) result = subrange | std::views::take(3);
162     assert(result.size() == 3);
163   }
164 
165   // `views::take(subrange, n)` returns a `subrange` with all default template arguments.
166   {
167     std::ranges::subrange<int*, sized_sentinel<int*>, std::ranges::subrange_kind::sized> subrange;
168 
169     using Result = std::ranges::subrange<int*, int*, std::ranges::subrange_kind::sized>;
170     [[maybe_unused]] std::same_as<Result> decltype(auto) result = subrange | std::views::take(3);
171   }
172 
173   // `views::take(iota_view, n)` returns an `iota_view`.
174   {
175     auto iota = std::views::iota(1, 8);
176     // The second template argument of the resulting `iota_view` is different because it has to be able to hold
177     // the `range_difference_t` of the input `iota_view`.
178     using Result = std::ranges::iota_view<int, std::ranges::range_difference_t<decltype(iota)>>;
179     std::same_as<Result> decltype(auto) result = iota | std::views::take(3);
180     assert(result.size() == 3);
181   }
182 
183   // When the size of the input range `s` is shorter than `n`, only `s` elements are taken.
184   {
185     test_small_range(std::span(buf));
186     test_small_range(std::string_view("abcdef"));
187     test_small_range(std::ranges::subrange(buf, buf + N));
188     test_small_range(std::views::iota(1, 8));
189   }
190 
191   // Test that it's possible to call `std::views::take` with any single argument as long as the resulting closure is
192   // never invoked. There is no good use case for it, but it's valid.
193   {
194     struct X { };
195     [[maybe_unused]] auto partial = std::views::take(X{});
196   }
197 
198   return true;
199 }
200 
main(int,char **)201 int main(int, char**) {
202   test();
203   static_assert(test());
204 
205   return 0;
206 }
207