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::transform
13
14 #include <ranges>
15
16 #include <cassert>
17 #include <concepts>
18 #include <type_traits>
19 #include <utility>
20
21 #include "test_macros.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 NonCopyableFunction {
30 NonCopyableFunction(NonCopyableFunction const&) = delete;
31 template <class T>
operator ()NonCopyableFunction32 constexpr T operator()(T x) const { return x; }
33 };
34
test()35 constexpr bool test() {
36 int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
37
38 // Test `views::transform(f)(v)`
39 {
40 {
41 using Result = std::ranges::transform_view<MoveOnlyView, PlusOne>;
42 std::same_as<Result> auto result = std::views::transform(PlusOne{})(MoveOnlyView{buff});
43 assert(result.begin().base() == buff);
44 assert(result[0] == 1);
45 assert(result[1] == 2);
46 assert(result[2] == 3);
47 }
48 {
49 auto const partial = std::views::transform(PlusOne{});
50 using Result = std::ranges::transform_view<MoveOnlyView, PlusOne>;
51 std::same_as<Result> auto result = partial(MoveOnlyView{buff});
52 assert(result.begin().base() == buff);
53 assert(result[0] == 1);
54 assert(result[1] == 2);
55 assert(result[2] == 3);
56 }
57 }
58
59 // Test `v | views::transform(f)`
60 {
61 {
62 using Result = std::ranges::transform_view<MoveOnlyView, PlusOne>;
63 std::same_as<Result> auto result = MoveOnlyView{buff} | std::views::transform(PlusOne{});
64 assert(result.begin().base() == buff);
65 assert(result[0] == 1);
66 assert(result[1] == 2);
67 assert(result[2] == 3);
68 }
69 {
70 auto const partial = std::views::transform(PlusOne{});
71 using Result = std::ranges::transform_view<MoveOnlyView, PlusOne>;
72 std::same_as<Result> auto result = MoveOnlyView{buff} | partial;
73 assert(result.begin().base() == buff);
74 assert(result[0] == 1);
75 assert(result[1] == 2);
76 assert(result[2] == 3);
77 }
78 }
79
80 // Test `views::transform(v, f)`
81 {
82 using Result = std::ranges::transform_view<MoveOnlyView, PlusOne>;
83 std::same_as<Result> auto result = std::views::transform(MoveOnlyView{buff}, PlusOne{});
84 assert(result.begin().base() == buff);
85 assert(result[0] == 1);
86 assert(result[1] == 2);
87 assert(result[2] == 3);
88 }
89
90 // Test that one can call std::views::transform with arbitrary stuff, as long as we
91 // don't try to actually complete the call by passing it a range.
92 //
93 // That makes no sense and we can't do anything with the result, but it's valid.
94 {
95 struct X { };
96 auto partial = std::views::transform(X{});
97 (void)partial;
98 }
99
100 // Test `adaptor | views::transform(f)`
101 {
102 {
103 using Result = std::ranges::transform_view<std::ranges::transform_view<MoveOnlyView, PlusOne>, TimesTwo>;
104 std::same_as<Result> auto result = MoveOnlyView{buff} | std::views::transform(PlusOne{}) | std::views::transform(TimesTwo{});
105 assert(result.begin().base().base() == buff);
106 assert(result[0] == 2);
107 assert(result[1] == 4);
108 assert(result[2] == 6);
109 }
110 {
111 auto const partial = std::views::transform(PlusOne{}) | std::views::transform(TimesTwo{});
112 using Result = std::ranges::transform_view<std::ranges::transform_view<MoveOnlyView, PlusOne>, TimesTwo>;
113 std::same_as<Result> auto result = MoveOnlyView{buff} | partial;
114 assert(result.begin().base().base() == buff);
115 assert(result[0] == 2);
116 assert(result[1] == 4);
117 assert(result[2] == 6);
118 }
119 }
120
121 // Test SFINAE friendliness
122 {
123 struct NotAView { };
124 struct NotInvocable { };
125
126 static_assert(!CanBePiped<MoveOnlyView, decltype(std::views::transform)>);
127 static_assert( CanBePiped<MoveOnlyView, decltype(std::views::transform(PlusOne{}))>);
128 static_assert(!CanBePiped<NotAView, decltype(std::views::transform(PlusOne{}))>);
129 static_assert(!CanBePiped<MoveOnlyView, decltype(std::views::transform(NotInvocable{}))>);
130
131 static_assert(!std::is_invocable_v<decltype(std::views::transform)>);
132 static_assert(!std::is_invocable_v<decltype(std::views::transform), PlusOne, MoveOnlyView>);
133 static_assert( std::is_invocable_v<decltype(std::views::transform), MoveOnlyView, PlusOne>);
134 static_assert(!std::is_invocable_v<decltype(std::views::transform), MoveOnlyView, PlusOne, PlusOne>);
135 static_assert(!std::is_invocable_v<decltype(std::views::transform), NonCopyableFunction>);
136 }
137
138 {
139 static_assert(std::is_same_v<decltype(std::ranges::views::transform), decltype(std::views::transform)>);
140 }
141
142 return true;
143 }
144
main(int,char **)145 int main(int, char**) {
146 test();
147 static_assert(test());
148
149 return 0;
150 }
151