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