183 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// constexpr auto begin();12 13#include <array>14#include <cassert>15#include <ranges>16#include <type_traits>17#include <utility>18 19#include "test_iterators.h"20 21struct View : std::ranges::view_base {22 int* begin() const;23 int* end() const;24};25 26// Test that begin is not const27template <class T>28concept HasBegin = requires(T t) { t.begin(); };29 30struct Pred {31 constexpr bool operator()(int i) const { return i < 3; }32};33 34static_assert(HasBegin<std::ranges::drop_while_view<View, Pred>>);35static_assert(!HasBegin<const std::ranges::drop_while_view<View, Pred>>);36 37constexpr auto always = [](auto v) { return [v](auto&&...) { return v; }; };38 39template <class Iter>40constexpr void testOne() {41 using Sent = sentinel_wrapper<Iter>;42 using Range = std::ranges::subrange<Iter, Sent>;43 constexpr auto make_subrange = []<std::size_t N>(int(&buffer)[N]) {44 return Range{Iter{buffer}, Sent{Iter{buffer + N}}};45 };46 47 // empty48 {49 std::array<int, 0> a;50 Range range{Iter{a.data()}, Sent{Iter{a.data() + a.size()}}};51 std::ranges::drop_while_view dwv{std::move(range), always(false)};52 std::same_as<Iter> decltype(auto) it = dwv.begin();53 assert(base(it) == a.data() + a.size());54 }55 56 // 1 element not dropped57 {58 int buffer[] = {1};59 auto range = make_subrange(buffer);60 std::ranges::drop_while_view dwv{std::move(range), always(false)};61 std::same_as<Iter> decltype(auto) it = dwv.begin();62 assert(base(it) == buffer);63 }64 65 // 1 element dropped66 {67 int buffer[] = {1};68 auto range = make_subrange(buffer);69 std::ranges::drop_while_view dwv{std::move(range), always(true)};70 std::same_as<Iter> decltype(auto) it = dwv.begin();71 assert(base(it) == buffer + 1);72 }73 74 // multiple elements. no element dropped75 {76 int buffer[] = {1, 2, 3, 4, 5};77 auto range = make_subrange(buffer);78 std::ranges::drop_while_view dwv{std::move(range), always(false)};79 std::same_as<Iter> decltype(auto) it = dwv.begin();80 assert(base(it) == buffer);81 }82 83 // multiple elements. all elements dropped84 {85 int buffer[] = {1, 2, 3, 4, 5};86 auto range = make_subrange(buffer);87 std::ranges::drop_while_view dwv{std::move(range), always(true)};88 std::same_as<Iter> decltype(auto) it = dwv.begin();89 assert(base(it) == buffer + 5);90 }91 92 // multiple elements. some elements dropped93 {94 int buffer[] = {1, 2, 3, 2, 1};95 auto range = make_subrange(buffer);96 std::ranges::drop_while_view dwv{std::move(range), [](int i) { return i < 3; }};97 std::same_as<Iter> decltype(auto) it = dwv.begin();98 assert(base(it) == buffer + 2);99 }100 101 // Make sure we do not make a copy of the predicate when we call begin()102 {103 struct TrackingPred {104 constexpr explicit TrackingPred(bool* moved, bool* copied) : moved_(moved), copied_(copied) {}105 constexpr TrackingPred(TrackingPred const& other) : moved_(other.moved_), copied_(other.copied_) {106 *copied_ = true;107 }108 constexpr TrackingPred(TrackingPred&& other) : moved_(other.moved_), copied_(other.copied_) { *moved_ = true; }109 TrackingPred& operator=(TrackingPred const&) = default;110 TrackingPred& operator=(TrackingPred&&) = default;111 112 constexpr bool operator()(int i) const { return i < 3; }113 bool* moved_;114 bool* copied_;115 };116 117 int buffer[] = {1, 2, 3, 2, 1};118 bool moved = false, copied = false;119 auto range = make_subrange(buffer);120 std::ranges::drop_while_view dwv{std::move(range), TrackingPred(&moved, &copied)};121 moved = false;122 copied = false;123 [[maybe_unused]] auto it = dwv.begin();124 assert(!moved);125 assert(!copied);126 }127 128 // Test with a non-const predicate129 {130 int buffer[] = {1, 2, 3, 2, 1};131 auto range = make_subrange(buffer);132 std::ranges::drop_while_view dwv{std::move(range), [](int i) mutable { return i < 3; }};133 std::same_as<Iter> decltype(auto) it = dwv.begin();134 assert(base(it) == buffer + 2);135 }136 137 // Test with a predicate that takes by non-const reference138 {139 int buffer[] = {1, 2, 3, 2, 1};140 auto range = make_subrange(buffer);141 std::ranges::drop_while_view dwv{std::move(range), [](int& i) { return i < 3; }};142 std::same_as<Iter> decltype(auto) it = dwv.begin();143 assert(base(it) == buffer + 2);144 }145 146 if constexpr (std::forward_iterator<Iter>) {147 // Make sure that we cache the result of begin() on subsequent calls148 {149 int buffer[] = {1, 2, 3, 2, 1};150 auto range = make_subrange(buffer);151 152 int called = 0;153 auto pred = [&](int i) {154 ++called;155 return i < 3;156 };157 std::ranges::drop_while_view dwv{range, pred};158 for (auto i = 0; i < 10; ++i) {159 std::same_as<Iter> decltype(auto) it = dwv.begin();160 assert(base(it) == buffer + 2);161 assert(called == 3);162 }163 }164 }165}166 167constexpr bool test() {168 testOne<cpp17_input_iterator<int*>>();169 testOne<cpp20_input_iterator<int*>>();170 testOne<forward_iterator<int*>>();171 testOne<bidirectional_iterator<int*>>();172 testOne<random_access_iterator<int*>>();173 testOne<contiguous_iterator<int*>>();174 testOne<int*>();175 return true;176}177 178int main(int, char**) {179 test();180 static_assert(test());181 return 0;182}183