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> 32 constexpr T operator()(T x) const { return x; } 33 }; 34 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 145 int main(int, char**) { 146 test(); 147 static_assert(test()); 148 149 return 0; 150 } 151