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::drop 13 14 #include <ranges> 15 16 #include <array> 17 #include <cassert> 18 #include <concepts> 19 #include <span> 20 #include <string_view> 21 #include <utility> 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 SizedView : std::ranges::view_base { 30 int* begin_ = nullptr; 31 int* end_ = nullptr; 32 constexpr SizedView(int* begin, int* end) : begin_(begin), end_(end) {} 33 34 constexpr auto begin() const { return forward_iterator<int*>(begin_); } 35 constexpr auto end() const { return sized_sentinel<forward_iterator<int*>>(forward_iterator<int*>(end_)); } 36 }; 37 static_assert(std::ranges::forward_range<SizedView>); 38 static_assert(std::ranges::sized_range<SizedView>); 39 static_assert(std::ranges::view<SizedView>); 40 41 struct SizedViewWithUnsizedSentinel : std::ranges::view_base { 42 using iterator = random_access_iterator<int*>; 43 using sentinel = sentinel_wrapper<random_access_iterator<int*>>; 44 45 int* begin_ = nullptr; 46 int* end_ = nullptr; 47 constexpr SizedViewWithUnsizedSentinel(int* begin, int* end) : begin_(begin), end_(end) {} 48 49 constexpr auto begin() const { return iterator(begin_); } 50 constexpr auto end() const { return sentinel(iterator(end_)); } 51 constexpr size_t size() const { return end_ - begin_; } 52 }; 53 static_assert(std::ranges::random_access_range<SizedViewWithUnsizedSentinel>); 54 static_assert(std::ranges::sized_range<SizedViewWithUnsizedSentinel>); 55 static_assert(!std::sized_sentinel_for<SizedViewWithUnsizedSentinel::sentinel, SizedViewWithUnsizedSentinel::iterator>); 56 static_assert(std::ranges::view<SizedViewWithUnsizedSentinel>); 57 58 template <class T> 59 constexpr void test_small_range(const T& input) { 60 constexpr int N = 100; 61 auto size = std::ranges::size(input); 62 assert(size < N); 63 64 auto result = input | std::views::drop(N); 65 assert(result.empty()); 66 } 67 68 constexpr bool test() { 69 constexpr int N = 8; 70 int buf[N] = {1, 2, 3, 4, 5, 6, 7, 8}; 71 72 // Test that `std::views::drop` is a range adaptor. 73 { 74 using SomeView = SizedView; 75 76 // Test `view | views::drop` 77 { 78 SomeView view(buf, buf + N); 79 std::same_as<std::ranges::drop_view<SomeView>> decltype(auto) result = view | std::views::drop(3); 80 assert(result.base().begin_ == buf); 81 assert(result.base().end_ == buf + N); 82 assert(base(result.begin()) == buf + 3); 83 assert(base(base(result.end())) == buf + N); 84 assert(result.size() == 5); 85 } 86 87 // Test `adaptor | views::drop` 88 { 89 SomeView view(buf, buf + N); 90 auto f = [](int i) { return i; }; 91 auto const partial = std::views::transform(f) | std::views::drop(3); 92 93 using Result = std::ranges::drop_view<std::ranges::transform_view<SomeView, decltype(f)>>; 94 std::same_as<Result> decltype(auto) result = partial(view); 95 assert(result.base().base().begin_ == buf); 96 assert(result.base().base().end_ == buf + N); 97 assert(base(result.begin().base()) == buf + 3); 98 assert(base(base(result.end().base())) == buf + N); 99 assert(result.size() == 5); 100 } 101 102 // Test `views::drop | adaptor` 103 { 104 SomeView view(buf, buf + N); 105 auto f = [](int i) { return i; }; 106 auto const partial = std::views::drop(3) | std::views::transform(f); 107 108 using Result = std::ranges::transform_view<std::ranges::drop_view<SomeView>, decltype(f)>; 109 std::same_as<Result> decltype(auto) result = partial(view); 110 assert(result.base().base().begin_ == buf); 111 assert(result.base().base().end_ == buf + N); 112 assert(base(result.begin().base()) == buf + 3); 113 assert(base(base(result.end().base())) == buf + N); 114 assert(result.size() == 5); 115 } 116 117 // Check SFINAE friendliness 118 { 119 struct NotAView { }; 120 static_assert(!std::is_invocable_v<decltype(std::views::drop)>); 121 static_assert(!std::is_invocable_v<decltype(std::views::drop), NotAView, int>); 122 static_assert( CanBePiped<SomeView&, decltype(std::views::drop(3))>); 123 static_assert( CanBePiped<int(&)[10], decltype(std::views::drop(3))>); 124 static_assert(!CanBePiped<int(&&)[10], decltype(std::views::drop(3))>); 125 static_assert(!CanBePiped<NotAView, decltype(std::views::drop(3))>); 126 127 static_assert(!CanBePiped<SomeView&, decltype(std::views::drop(/*n=*/NotAView{}))>); 128 } 129 } 130 131 { 132 static_assert(std::same_as<decltype(std::views::drop), decltype(std::ranges::views::drop)>); 133 } 134 135 // `views::drop(empty_view, n)` returns an `empty_view`. 136 { 137 using Result = std::ranges::empty_view<int>; 138 [[maybe_unused]] std::same_as<Result> decltype(auto) result = std::views::empty<int> | std::views::drop(3); 139 } 140 141 // `views::drop(span, n)` returns a `span`. 142 { 143 std::span<int> s(buf); 144 std::same_as<decltype(s)> decltype(auto) result = s | std::views::drop(5); 145 assert(result.size() == 3); 146 } 147 148 // `views::drop(span, n)` returns a `span` with a dynamic extent, regardless of the input `span`. 149 { 150 std::span<int, 8> s(buf); 151 std::same_as<std::span<int, std::dynamic_extent>> decltype(auto) result = s | std::views::drop(3); 152 assert(result.size() == 5); 153 } 154 155 // `views::drop(string_view, n)` returns a `string_view`. 156 { 157 { 158 std::string_view sv = "abcdef"; 159 std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::drop(2); 160 assert(result.size() == 4); 161 } 162 163 { 164 std::u32string_view sv = U"abcdef"; 165 std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::drop(2); 166 assert(result.size() == 4); 167 } 168 } 169 170 // `views::drop(iota_view, n)` returns an `iota_view`. 171 { 172 auto iota = std::views::iota(1, 8); 173 // The second template argument of the resulting `iota_view` is different because it has to be able to hold 174 // the `range_difference_t` of the input `iota_view`. 175 using Result = std::ranges::iota_view<int, int>; 176 std::same_as<Result> decltype(auto) result = iota | std::views::drop(3); 177 assert(result.size() == 4); 178 assert(*result.begin() == 4); 179 assert(*std::ranges::next(result.begin(), 3) == 7); 180 } 181 182 // `views::drop(subrange, n)` returns a `subrange` when `subrange::StoreSize == false`. 183 { 184 auto subrange = std::ranges::subrange(buf, buf + N); 185 LIBCPP_STATIC_ASSERT(!decltype(subrange)::_StoreSize); 186 187 using Result = std::ranges::subrange<int*>; 188 std::same_as<Result> decltype(auto) result = subrange | std::views::drop(3); 189 assert(result.size() == 5); 190 } 191 192 // `views::drop(subrange, n)` returns a `subrange` when `subrange::StoreSize == true`. 193 { 194 using View = SizedViewWithUnsizedSentinel; 195 View view(buf, buf + N); 196 197 using Subrange = std::ranges::subrange<View::iterator, View::sentinel, std::ranges::subrange_kind::sized>; 198 auto subrange = Subrange(view.begin(), view.end(), std::ranges::distance(view.begin(), view.end())); 199 LIBCPP_STATIC_ASSERT(decltype(subrange)::_StoreSize); 200 201 std::same_as<Subrange> decltype(auto) result = subrange | std::views::drop(3); 202 assert(result.size() == 5); 203 } 204 205 // `views::drop(subrange, n)` doesn't return a `subrange` if it's not a random access range. 206 { 207 SizedView v(buf, buf + N); 208 auto subrange = std::ranges::subrange(v.begin(), v.end()); 209 210 using Result = std::ranges::drop_view<std::ranges::subrange<forward_iterator<int*>, 211 sized_sentinel<forward_iterator<int*>>>>; 212 std::same_as<Result> decltype(auto) result = subrange | std::views::drop(3); 213 assert(result.size() == 5); 214 } 215 216 // When the size of the input range `s` is shorter than `n`, an `empty_view` is returned. 217 { 218 test_small_range(std::span(buf)); 219 test_small_range(std::string_view("abcdef")); 220 test_small_range(std::ranges::subrange(buf, buf + N)); 221 test_small_range(std::views::iota(1, 8)); 222 } 223 224 // Test that it's possible to call `std::views::drop` with any single argument as long as the resulting closure is 225 // never invoked. There is no good use case for it, but it's valid. 226 { 227 struct X { }; 228 [[maybe_unused]] auto partial = std::views::drop(X{}); 229 } 230 231 return true; 232 } 233 234 int main(int, char**) { 235 test(); 236 static_assert(test()); 237 238 return 0; 239 } 240