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