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