xref: /llvm-project/libcxx/test/std/ranges/range.adaptors/range.chunk.by/adaptor.pass.cpp (revision ba2236d3000645d3127f972aa7ac1844c47e299c)
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, c++20
10 
11 // <ranges>
12 
13 // std::views::chunk_by
14 
15 #include <ranges>
16 
17 #include <algorithm>
18 #include <cassert>
19 #include <concepts>
20 #include <initializer_list>
21 #include <type_traits>
22 #include <utility>
23 
24 #include "test_iterators.h"
25 #include "test_range.h"
26 
27 struct Pred {
operator ()Pred28   constexpr bool operator()(int x, int y) const { return x != -y; }
29 };
30 
31 struct NonCopyablePredicate : Pred {
32   NonCopyablePredicate(NonCopyablePredicate const&) = delete;
33 };
34 
35 struct Range : std::ranges::view_base {
36   using Iterator = forward_iterator<int*>;
37   using Sentinel = sentinel_wrapper<Iterator>;
RangeRange38   constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
beginRange39   constexpr Iterator begin() const { return Iterator(begin_); }
endRange40   constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
41 
42 private:
43   int* begin_;
44   int* end_;
45 };
46 
47 template <typename View>
compareViews(View v,std::initializer_list<std::initializer_list<int>> list)48 constexpr void compareViews(View v, std::initializer_list<std::initializer_list<int>> list) {
49   auto b1 = v.begin();
50   auto e1 = v.end();
51   auto b2 = list.begin();
52   auto e2 = list.end();
53   for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
54     bool eq = std::ranges::equal(*b1, *b2, [](int x, int y) {
55       assert(x == y);
56       return true;
57     });
58     assert(eq);
59   }
60   assert(b1 == e1);
61   assert(b2 == e2);
62 }
63 
absoluteValue(int x)64 constexpr int absoluteValue(int x) { return x < 0 ? -x : x; }
65 
66 template <class T>
asConstRvalue(T && t)67 constexpr const T&& asConstRvalue(T&& t) {
68   return static_cast<T const&&>(t);
69 }
70 
test()71 constexpr bool test() {
72   int buff[] = {-4, -3, -2, -1, 1, 2, 3, 4};
73 
74   // Test range adaptor object
75   {
76     using RangeAdaptorObject = decltype(std::views::chunk_by);
77     static_assert(std::is_const_v<RangeAdaptorObject>);
78 
79     // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular
80     static_assert(std::semiregular<std::remove_const<RangeAdaptorObject>>);
81   }
82 
83   // Test `views::chunk_by(pred)(v)`
84   {
85     using Result = std::ranges::chunk_by_view<Range, Pred>;
86     Range const range(buff, buff + 8);
87     Pred pred;
88 
89     {
90       // 'views::chunk_by(pred)' - &&
91       std::same_as<Result> decltype(auto) result = std::views::chunk_by(pred)(range);
92       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
93     }
94     {
95       // 'views::chunk_by(pred)' - const&&
96       std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::chunk_by(pred))(range);
97       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
98     }
99     {
100       // 'views::chunk_by(pred)' - &
101       auto partial                               = std::views::chunk_by(pred);
102       std::same_as<Result> decltype(auto) result = partial(range);
103       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
104     }
105     {
106       // 'views::chunk_by(pred)' - const&
107       auto const partial                         = std::views::chunk_by(pred);
108       std::same_as<Result> decltype(auto) result = partial(range);
109       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
110     }
111   }
112 
113   // Test `v | views::chunk_by(pred)`
114   {
115     using Result = std::ranges::chunk_by_view<Range, Pred>;
116     Range const range(buff, buff + 8);
117     Pred pred;
118 
119     {
120       // 'views::chunk_by(pred)' - &&
121       std::same_as<Result> decltype(auto) result = range | std::views::chunk_by(pred);
122       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
123     }
124     {
125       // 'views::chunk_by(pred)' - const&&
126       std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::chunk_by(pred));
127       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
128     }
129     {
130       // 'views::chunk_by(pred)' - &
131       auto partial                               = std::views::chunk_by(pred);
132       std::same_as<Result> decltype(auto) result = range | partial;
133       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
134     }
135     {
136       // 'views::chunk_by(pred)' - const&
137       auto const partial                         = std::views::chunk_by(pred);
138       std::same_as<Result> decltype(auto) result = range | partial;
139       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
140     }
141   }
142 
143   // Test `views::chunk_by(v, pred)` range adaptor object
144   {
145     using Result = std::ranges::chunk_by_view<Range, Pred>;
146     Range const range(buff, buff + 8);
147     Pred pred;
148 
149     {
150       // 'views::chunk_by' - &&
151       auto range_adaptor                         = std::views::chunk_by;
152       std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pred);
153       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
154     }
155     {
156       // 'views::chunk_by' - const&&
157       auto const range_adaptor                   = std::views::chunk_by;
158       std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pred);
159       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
160     }
161     {
162       // 'views::chunk_by' - &
163       auto range_adaptor                         = std::views::chunk_by;
164       std::same_as<Result> decltype(auto) result = range_adaptor(range, pred);
165       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
166     }
167     {
168       // 'views::chunk_by' - const&
169       auto const range_adaptor                   = std::views::chunk_by;
170       std::same_as<Result> decltype(auto) result = range_adaptor(range, pred);
171       compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
172     }
173   }
174 
175   // Test that one can call std::views::chunk_by with arbitrary stuff, as long as we
176   // don't try to actually complete the call by passing it a range.
177   //
178   // That makes no sense and we can't do anything with the result, but it's valid.
179   {
180     int array[3]                  = {1, 2, 3};
181     [[maybe_unused]] auto partial = std::views::chunk_by(std::move(array));
182   }
183 
184   // Test `adaptor | views::chunk_by(pred)`
185   {
186     Range const range(buff, buff + 8);
187 
188     {
189       auto pred1 = [](int i) { return absoluteValue(i) < 3; };
190       Pred pred2;
191       using Result = std::ranges::chunk_by_view<std::ranges::filter_view<Range, decltype(pred1)>, Pred>;
192       std::same_as<Result> decltype(auto) result = range | std::views::filter(pred1) | std::views::chunk_by(pred2);
193       compareViews(result, {{-2, -1}, {1, 2}});
194     }
195     {
196       auto pred1 = [](int i) { return absoluteValue(i) < 3; };
197       Pred pred2;
198       using Result       = std::ranges::chunk_by_view<std::ranges::filter_view<Range, decltype(pred1)>, Pred>;
199       auto const partial = std::views::filter(pred1) | std::views::chunk_by(pred2);
200       std::same_as<Result> decltype(auto) result = range | partial;
201       compareViews(result, {{-2, -1}, {1, 2}});
202     }
203   }
204 
205   // Test SFINAE friendliness
206   {
207     struct NotAView {};
208     struct NotInvocable {};
209 
210     static_assert(!CanBePiped<Range, decltype(std::views::chunk_by)>);
211     static_assert(CanBePiped<Range, decltype(std::views::chunk_by(Pred{}))>);
212     static_assert(!CanBePiped<NotAView, decltype(std::views::chunk_by(Pred{}))>);
213     static_assert(!CanBePiped<std::initializer_list<int>, decltype(std::views::chunk_by(Pred{}))>);
214     static_assert(!CanBePiped<Range, decltype(std::views::chunk_by(NotInvocable{}))>);
215 
216     static_assert(!std::is_invocable_v<decltype(std::views::chunk_by)>);
217     static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), Pred, Range>);
218     static_assert(std::is_invocable_v<decltype(std::views::chunk_by), Range, Pred>);
219     static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), Range, Pred, Pred>);
220     static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), NonCopyablePredicate>);
221   }
222 
223   { static_assert(std::is_same_v<decltype(std::ranges::views::chunk_by), decltype(std::views::chunk_by)>); }
224 
225   return true;
226 }
227 
main(int,char **)228 int main(int, char**) {
229   test();
230   static_assert(test());
231 
232   return 0;
233 }
234