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// REQUIRES: std-at-least-c++23
10
11// <ranges>
12
13// std::views::join_with_view
14
15#include <ranges>
16
17#include <memory>
18#include <span>
19#include <string_view>
20#include <utility>
21
22#include "test_iterators.h"
23
24template <class View, class T>
25concept CanBePiped = requires(View&& view, T&& t) {
26 { std::forward<View>(view) | std::forward<T>(t) };
27};
28
29struct Range : std::ranges::view_base {
30 using Iterator = forward_iterator<std::string_view*>;
31 using Sentinel = sentinel_wrapper<Iterator>;
32 constexpr explicit Range(std::string_view* b, std::string_view* e) : begin_(b), end_(e) {}
33 constexpr Iterator begin() const { return Iterator(begin_); }
34 constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
35
36private:
37 std::string_view* begin_;
38 std::string_view* end_;
39};
40
41struct Pattern : std::ranges::view_base {
42 using Iterator = forward_iterator<const char*>;
43 using Sentinel = sentinel_wrapper<Iterator>;
44 static constexpr std::string_view pat{", "};
45
46 constexpr Pattern() = default;
47 constexpr Iterator begin() const { return Iterator(pat.data()); }
48 constexpr Sentinel end() const { return Sentinel(Iterator(pat.data() + pat.size())); }
49};
50
51struct NonCopyablePattern : Pattern {
52 NonCopyablePattern(const NonCopyablePattern&) = delete;
53};
54
55template <typename View>
56constexpr void compareViews(View v, std::string_view list) {
57 auto b1 = v.begin();
58 auto e1 = v.end();
59 auto b2 = list.begin();
60 auto e2 = list.end();
61 for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
62 assert(*b1 == *b2);
63 }
64 assert(b1 == e1);
65 assert(b2 == e2);
66}
67
68constexpr int absoluteValue(int x) { return x < 0 ? -x : x; }
69
70template <class T>
71constexpr const T&& asConstRvalue(T&& t) {
72 return static_cast<const T&&>(t);
73}
74
75constexpr void test_adaptor_with_pattern(std::span<std::string_view> buff) {
76 // Test `views::join_with(pattern)(v)`
77 {
78 using Result = std::ranges::join_with_view<Range, Pattern>;
79 const Range range(buff.data(), buff.data() + buff.size());
80 Pattern pattern;
81
82 {
83 // 'views::join_with(pattern)' - &&
84 std::same_as<Result> decltype(auto) result = std::views::join_with(pattern)(range);
85 compareViews(result, "abcd, ef, ghij, kl");
86 }
87 {
88 // 'views::join_with(pattern)' - const&&
89 std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(pattern))(range);
90 compareViews(result, "abcd, ef, ghij, kl");
91 }
92 {
93 // 'views::join_with(pattern)' - &
94 auto partial = std::views::join_with(pattern);
95 std::same_as<Result> decltype(auto) result = partial(range);
96 compareViews(result, "abcd, ef, ghij, kl");
97 }
98 {
99 // 'views::join_with(pattern)' - const&
100 auto const partial = std::views::join_with(pattern);
101 std::same_as<Result> decltype(auto) result = partial(range);
102 compareViews(result, "abcd, ef, ghij, kl");
103 }
104 }
105
106 // Test `v | views::join_with(pattern)`
107 {
108 using Result = std::ranges::join_with_view<Range, Pattern>;
109 const Range range(buff.data(), buff.data() + buff.size());
110 Pattern pattern;
111
112 {
113 // 'views::join_with(pattern)' - &&
114 std::same_as<Result> decltype(auto) result = range | std::views::join_with(pattern);
115 compareViews(result, "abcd, ef, ghij, kl");
116 }
117 {
118 // 'views::join_with(pattern)' - const&&
119 std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(pattern));
120 compareViews(result, "abcd, ef, ghij, kl");
121 }
122 {
123 // 'views::join_with(pattern)' - &
124 auto partial = std::views::join_with(pattern);
125 std::same_as<Result> decltype(auto) result = range | partial;
126 compareViews(result, "abcd, ef, ghij, kl");
127 }
128 {
129 // 'views::join_with(pattern)' - const&
130 auto const partial = std::views::join_with(pattern);
131 std::same_as<Result> decltype(auto) result = range | partial;
132 compareViews(result, "abcd, ef, ghij, kl");
133 }
134 }
135
136 // Test `views::join_with(v, pattern)` range adaptor object
137 {
138 using Result = std::ranges::join_with_view<Range, Pattern>;
139 const Range range(buff.data(), buff.data() + buff.size());
140 Pattern pattern;
141
142 {
143 // 'views::join_with' - &&
144 auto range_adaptor = std::views::join_with;
145 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);
146 compareViews(result, "abcd, ef, ghij, kl");
147 }
148 {
149 // 'views::join_with' - const&&
150 const auto range_adaptor = std::views::join_with;
151 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);
152 compareViews(result, "abcd, ef, ghij, kl");
153 }
154 {
155 // 'views::join_with' - &
156 auto range_adaptor = std::views::join_with;
157 std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);
158 compareViews(result, "abcd, ef, ghij, kl");
159 }
160 {
161 // 'views::join_with' - const&
162 const auto range_adaptor = std::views::join_with;
163 std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);
164 compareViews(result, "abcd, ef, ghij, kl");
165 }
166 }
167
168 // Test `adaptor | views::join_with(pattern)`
169 {
170 auto pred = [](std::string_view s) { return s.size() >= 3; };
171 using Result = std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, Pattern>;
172 const Range range(buff.data(), buff.data() + buff.size());
173 Pattern pattern;
174
175 {
176 std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(pattern);
177 compareViews(result, "abcd, ghij");
178 }
179 {
180 const auto partial = std::views::filter(pred) | std::views::join_with(pattern);
181 std::same_as<Result> decltype(auto) result = range | partial;
182 compareViews(result, "abcd, ghij");
183 }
184 }
185}
186
187constexpr void test_adaptor_with_single_element(std::span<std::string_view> buff) {
188 // Test `views::join_with(element)(v)`
189 {
190 using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
191 const Range range(buff.data(), buff.data() + buff.size());
192 const char element = '.';
193
194 {
195 // 'views::join_with(element)' - &&
196 std::same_as<Result> decltype(auto) result = std::views::join_with(element)(range);
197 compareViews(result, "abcd.ef.ghij.kl");
198 }
199 {
200 // 'views::join_with(element)' - const&&
201 std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(element))(range);
202 compareViews(result, "abcd.ef.ghij.kl");
203 }
204 {
205 // 'views::join_with(element)' - &
206 auto partial = std::views::join_with(element);
207 std::same_as<Result> decltype(auto) result = partial(range);
208 compareViews(result, "abcd.ef.ghij.kl");
209 }
210 {
211 // 'views::join_with(element)' - const&
212 const auto partial = std::views::join_with(element);
213 std::same_as<Result> decltype(auto) result = partial(range);
214 compareViews(result, "abcd.ef.ghij.kl");
215 }
216 }
217
218 // Test `v | views::join_with(element)`
219 {
220 using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
221 const Range range(buff.data(), buff.data() + buff.size());
222 const char element = '.';
223
224 {
225 // 'views::join_with(element)' - &&
226 std::same_as<Result> decltype(auto) result = range | std::views::join_with(element);
227 compareViews(result, "abcd.ef.ghij.kl");
228 }
229 {
230 // 'views::join_with(element)' - const&&
231 std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(element));
232 compareViews(result, "abcd.ef.ghij.kl");
233 }
234 {
235 // 'views::join_with(element)' - &
236 auto partial = std::views::join_with(element);
237 std::same_as<Result> decltype(auto) result = range | partial;
238 compareViews(result, "abcd.ef.ghij.kl");
239 }
240 {
241 // 'views::join_with(element)' - const&
242 const auto partial = std::views::join_with(element);
243 std::same_as<Result> decltype(auto) result = range | partial;
244 compareViews(result, "abcd.ef.ghij.kl");
245 }
246 }
247
248 // Test `views::join_with(v, element)` range adaptor object
249 {
250 using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
251 const Range range(buff.data(), buff.data() + buff.size());
252 const char element = '.';
253
254 {
255 // 'views::join_with' - &&
256 auto range_adaptor = std::views::join_with;
257 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);
258 compareViews(result, "abcd.ef.ghij.kl");
259 }
260 {
261 // 'views::join_with' - const&&
262 const auto range_adaptor = std::views::join_with;
263 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);
264 compareViews(result, "abcd.ef.ghij.kl");
265 }
266 {
267 // 'views::join_with' - &
268 auto range_adaptor = std::views::join_with;
269 std::same_as<Result> decltype(auto) result = range_adaptor(range, element);
270 compareViews(result, "abcd.ef.ghij.kl");
271 }
272 {
273 // 'views::join_with' - const&
274 const auto range_adaptor = std::views::join_with;
275 std::same_as<Result> decltype(auto) result = range_adaptor(range, element);
276 compareViews(result, "abcd.ef.ghij.kl");
277 }
278 }
279
280 // Test `adaptor | views::join_with(element)`
281 {
282 auto pred = [](std::string_view s) { return s.size() >= 3; };
283 using Result =
284 std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, std::ranges::single_view<char>>;
285 const Range range(buff.data(), buff.data() + buff.size());
286 const char element = '.';
287
288 {
289 std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(element);
290 compareViews(result, "abcd.ghij");
291 }
292 {
293 const auto partial = std::views::filter(pred) | std::views::join_with(element);
294 std::same_as<Result> decltype(auto) result = range | partial;
295 compareViews(result, "abcd.ghij");
296 }
297 }
298}
299
300constexpr bool test() {
301 std::string_view buff[] = {"abcd", "ef", "ghij", "kl"};
302
303 // Test range adaptor object
304 {
305 using RangeAdaptorObject = decltype(std::views::join_with);
306 static_assert(std::is_const_v<RangeAdaptorObject>);
307
308 // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular
309 static_assert(std::semiregular<std::remove_const<RangeAdaptorObject>>);
310 }
311
312 test_adaptor_with_pattern(buff);
313 test_adaptor_with_single_element(buff);
314
315 // Test that one can call std::views::join_with with arbitrary stuff, as long as we
316 // don't try to actually complete the call by passing it a range.
317 //
318 // That makes no sense and we can't do anything with the result, but it's valid.
319 {
320 long array[3] = {1, 2, 3};
321 [[maybe_unused]] auto partial = std::views::join_with(std::move(array));
322 }
323
324 // Test SFINAE friendliness
325 {
326 struct NotAView {};
327
328 static_assert(!CanBePiped<Range, decltype(std::views::join_with)>);
329 static_assert(CanBePiped<Range, decltype(std::views::join_with(Pattern{}))>);
330 static_assert(CanBePiped<Range, decltype(std::views::join_with('.'))>);
331 static_assert(!CanBePiped<NotAView, decltype(std::views::join_with(Pattern{}))>);
332 static_assert(!CanBePiped<NotAView, decltype(std::views::join_with('.'))>);
333 static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with(Pattern{}))>);
334 static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with('.'))>);
335 static_assert(!CanBePiped<Range, decltype(std::views::join_with(NotAView{}))>);
336
337 static_assert(!std::is_invocable_v<decltype(std::views::join_with)>);
338 static_assert(!std::is_invocable_v<decltype(std::views::join_with), Pattern, Range>);
339 static_assert(!std::is_invocable_v<decltype(std::views::join_with), char, Range>);
340 static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, Pattern>);
341 static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, char>);
342 static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, Pattern, Pattern>);
343 static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, char, char>);
344 static_assert(!std::is_invocable_v<decltype(std::views::join_with), NonCopyablePattern>);
345 }
346
347 {
348 static_assert(std::is_same_v<decltype(std::ranges::views::join_with), decltype(std::views::join_with)>);
349 assert(std::addressof(std::ranges::views::join_with) == std::addressof(std::views::join_with));
350 }
351
352 return true;
353}
354
355int main(int, char**) {
356 test();
357 static_assert(test());
358
359 return 0;
360}
361

source code of libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp