Skip to content

Commit 5b539d2

Browse files
committed
Test [range.join.with.overview]
1 parent 634d9b4 commit 5b539d2

File tree

2 files changed

+410
-0
lines changed

2 files changed

+410
-0
lines changed
Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
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+
// UNSUPPORTED: !c++experimental
11+
12+
// <ranges>
13+
14+
// std::views::join_with_view
15+
16+
#include <ranges>
17+
18+
#include <span>
19+
#include <string_view>
20+
#include <utility>
21+
22+
#include "test_iterators.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 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+
36+
private:
37+
std::string_view* begin_;
38+
std::string_view* end_;
39+
};
40+
41+
struct 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+
51+
struct NonCopyablePattern : Pattern {
52+
NonCopyablePattern(const NonCopyablePattern&) = delete;
53+
};
54+
55+
template <typename View>
56+
constexpr 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+
68+
constexpr int absoluteValue(int x) { return x < 0 ? -x : x; }
69+
70+
template <class T>
71+
constexpr const T&& asConstRvalue(T&& t) {
72+
return static_cast<const T&&>(t);
73+
}
74+
75+
constexpr 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+
187+
constexpr 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+
300+
constexpr 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+
355+
int main(int, char**) {
356+
test();
357+
#if __cpp_lib_variant >= 202106
358+
static_assert(test());
359+
#endif
360+
361+
return 0;
362+
}

0 commit comments

Comments
 (0)