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