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