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