323 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// <algorithm>10 11// UNSUPPORTED: c++03, c++11, c++14, c++1712 13// template<input_iterator I, sentinel_for<I> S, class Proj = identity,14// indirect_unary_predicate<projected<I, Proj>> Pred>15// constexpr iter_difference_t<I>16// ranges::count_if(I first, S last, Pred pred, Proj proj = {});17// template<input_range R, class Proj = identity,18// indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>19// constexpr range_difference_t<R>20// ranges::count_if(R&& r, Pred pred, Proj proj = {});21 22#include <algorithm>23#include <array>24#include <cassert>25#include <ranges>26 27#include "almost_satisfies_types.h"28#include "test_iterators.h"29 30struct Predicate {31 bool operator()(int);32};33 34template <class It, class Sent = It>35concept HasCountIfIt = requires(It it, Sent sent) { std::ranges::count_if(it, sent, Predicate{}); };36static_assert(HasCountIfIt<int*>);37static_assert(!HasCountIfIt<InputIteratorNotDerivedFrom>);38static_assert(!HasCountIfIt<InputIteratorNotIndirectlyReadable>);39static_assert(!HasCountIfIt<InputIteratorNotInputOrOutputIterator>);40static_assert(!HasCountIfIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);41static_assert(!HasCountIfIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);42 43static_assert(!HasCountIfIt<int*, int>);44static_assert(!HasCountIfIt<int, int*>);45 46template <class Pred>47concept HasCountIfPred = requires(int* it, Pred pred) {std::ranges::count_if(it, it, pred); };48 49static_assert(!HasCountIfPred<IndirectUnaryPredicateNotCopyConstructible>);50static_assert(!HasCountIfPred<IndirectUnaryPredicateNotPredicate>);51 52template <class R>53concept HasCountIfR = requires(R r) { std::ranges::count_if(r, Predicate{}); };54static_assert(HasCountIfR<std::array<int, 0>>);55static_assert(!HasCountIfR<int>);56static_assert(!HasCountIfR<InputRangeNotDerivedFrom>);57static_assert(!HasCountIfR<InputRangeNotIndirectlyReadable>);58static_assert(!HasCountIfR<InputRangeNotInputOrOutputIterator>);59static_assert(!HasCountIfR<InputRangeNotSentinelSemiregular>);60static_assert(!HasCountIfR<InputRangeNotSentinelEqualityComparableWith>);61 62template <class It, class Sent = It>63constexpr void test_iterators() {64 {65 // simple test66 {67 int a[] = {1, 2, 3, 4};68 std::same_as<std::ptrdiff_t> auto ret =69 std::ranges::count_if(It(a), Sent(It(a + 4)), [](int x) { return x == 4; });70 assert(ret == 1);71 }72 {73 int a[] = {1, 2, 3, 4};74 auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));75 std::same_as<std::ptrdiff_t> auto ret =76 std::ranges::count_if(range, [](int x) { return x == 4; });77 assert(ret == 1);78 }79 }80 81 {82 // check that an empty range works83 {84 std::array<int, 0> a = {};85 auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int) { return true; });86 assert(ret == 0);87 }88 {89 std::array<int, 0> a = {};90 auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));91 auto ret = std::ranges::count_if(range, [](int) { return true; });92 assert(ret == 0);93 }94 }95 96 {97 // check that a range with a single element works98 {99 std::array a = {2};100 auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int i) { return i == 2; });101 assert(ret == 1);102 }103 {104 std::array a = {2};105 auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));106 auto ret = std::ranges::count_if(range, [](int i) { return i == 2; });107 assert(ret == 1);108 }109 }110 111 {112 // check that 0 is returned with no match113 {114 int a[] = {1, 1, 1};115 auto ret = std::ranges::count_if(It(a), Sent(It(a + 3)), [](int) { return false; });116 assert(ret == 0);117 }118 {119 int a[] = {1, 1, 1};120 auto range = std::ranges::subrange(It(a), Sent(It(a + 3)));121 auto ret = std::ranges::count_if(range, [](int){ return false; });122 assert(ret == 0);123 }124 }125 126 {127 // check that more than one element is counted128 {129 std::array a = {3, 3, 4, 3, 3};130 auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int i) { return i == 3; });131 assert(ret == 4);132 }133 {134 std::array a = {3, 3, 4, 3, 3};135 auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));136 auto ret = std::ranges::count_if(range, [](int i) { return i == 3; });137 assert(ret == 4);138 }139 }140 141 {142 // check that all elements are counted143 {144 std::array a = {5, 5, 5, 5};145 auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int) { return true; });146 assert(ret == 4);147 }148 {149 std::array a = {5, 5, 5, 5};150 auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));151 auto ret = std::ranges::count_if(range, [](int) { return true; });152 assert(ret == 4);153 }154 }155}156 157constexpr bool test() {158 test_iterators<int*>();159 test_iterators<const int*>();160 test_iterators<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();161 test_iterators<bidirectional_iterator<int*>>();162 test_iterators<forward_iterator<int*>>();163 test_iterators<random_access_iterator<int*>>();164 test_iterators<contiguous_iterator<int*>>();165 166 {167 // check that projections are used properly and that they are called with the iterator directly168 {169 int a[] = {1, 2, 3, 4};170 auto ret = std::ranges::count_if(a, a + 4, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });171 assert(ret == 1);172 }173 {174 int a[] = {1, 2, 3, 4};175 auto ret = std::ranges::count_if(a, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });176 assert(ret == 1);177 }178 }179 180 {181 // check that std::invoke is used182 {183 struct S {184 int comp;185 int other;186 };187 S a[] = { {0, 0}, {0, 2}, {0, 1} };188 auto ret = std::ranges::count_if(a, [](int i){ return i == 0; }, &S::comp);189 assert(ret == 3);190 }191 {192 struct S {193 int comp;194 int other;195 };196 S a[] = { {0, 0}, {0, 2}, {0, 1} };197 auto ret = std::ranges::count_if(a, a + 3, [](int i) { return i == 0; }, &S::comp);198 assert(ret == 3);199 }200 }201 202 {203 // check projection and predicate invocation count204 {205 int a[] = {1, 2, 3, 4};206 int predicate_count = 0;207 int projection_count = 0;208 auto ret = std::ranges::count_if(a, a + 4,209 [&](int i) { ++predicate_count; return i == 2; },210 [&](int i) { ++projection_count; return i; });211 assert(ret == 1);212 assert(predicate_count == 4);213 assert(projection_count == 4);214 }215 {216 int a[] = {1, 2, 3, 4};217 int predicate_count = 0;218 int projection_count = 0;219 auto ret = std::ranges::count_if(a,220 [&](int i) { ++predicate_count; return i == 2; },221 [&](int i) { ++projection_count; return i; });222 assert(ret == 1);223 assert(predicate_count == 4);224 assert(projection_count == 4);225 }226 }227 228 {229 // check that an immobile type works230 struct NonMovable {231 NonMovable(const NonMovable&) = delete;232 NonMovable(NonMovable&&) = delete;233 constexpr NonMovable(int i_) : i(i_) {}234 int i;235 236 bool operator==(const NonMovable&) const = default;237 };238 {239 NonMovable a[] = {9, 8, 4, 3};240 auto ret = std::ranges::count_if(a, a + 4, [](const NonMovable& i) { return i == NonMovable(8); });241 assert(ret == 1);242 }243 {244 NonMovable a[] = {9, 8, 4, 3};245 auto ret = std::ranges::count_if(a, [](const NonMovable& i) { return i == NonMovable(8); });246 assert(ret == 1);247 }248 }249 250 {251 // check that difference_type is used252 struct DiffTypeIterator {253 using difference_type = signed char;254 using value_type = int;255 256 int* it = nullptr;257 258 constexpr DiffTypeIterator() = default;259 constexpr DiffTypeIterator(int* i) : it(i) {}260 261 constexpr int& operator*() const { return *it; }262 constexpr DiffTypeIterator& operator++() { ++it; return *this; }263 constexpr void operator++(int) { ++it; }264 265 bool operator==(const DiffTypeIterator&) const = default;266 };267 268 {269 int a[] = {5, 5, 4, 3, 2, 1};270 std::same_as<signed char> auto ret =271 std::ranges::count_if(DiffTypeIterator(a), DiffTypeIterator(a + 6), [](int& i) { return i == 4; });272 assert(ret == 1);273 }274 {275 int a[] = {5, 5, 4, 3, 2, 1};276 auto range = std::ranges::subrange(DiffTypeIterator(a), DiffTypeIterator(a + 6));277 std::same_as<signed char> auto ret = std::ranges::count_if(range, [](int& i) { return i == 4; });278 assert(ret == 1);279 }280 }281 282 {283 // check that the predicate can take the argument by lvalue ref284 {285 int a[] = {9, 8, 4, 3};286 auto ret = std::ranges::count_if(a, a + 4, [](int& i) { return i == 8; });287 assert(ret == 1);288 }289 {290 int a[] = {9, 8, 4, 3};291 auto ret = std::ranges::count_if(a, [](int& i) { return i == 8; });292 assert(ret == 1);293 }294 }295 296 {297 // check that the predicate isn't made const298 struct MutablePredicate {299 constexpr bool operator()(int i) & { return i == 8; }300 constexpr bool operator()(int i) && { return i == 8; }301 };302 {303 int a[] = {9, 8, 4, 3};304 auto ret = std::ranges::count_if(a, a + 4, MutablePredicate{});305 assert(ret == 1);306 }307 {308 int a[] = {9, 8, 4, 3};309 auto ret = std::ranges::count_if(a, MutablePredicate{});310 assert(ret == 1);311 }312 }313 314 return true;315}316 317int main(int, char**) {318 test();319 static_assert(test());320 321 return 0;322}323