diff options
author | Nikolas Klauser <nikolasklauser@berlin.de> | 2022-04-07 13:02:02 +0200 |
---|---|---|
committer | Nikolas Klauser <nikolasklauser@berlin.de> | 2022-04-07 15:18:14 +0200 |
commit | 1306b1025c507ec8c406a975558007e3e94e5cbd (patch) | |
tree | b5e4c84fb7047fafdd103002dd8708196a581eda | |
parent | da6b6b3c8201473a37499baab6594ef5b087f8dc (diff) | |
download | llvm-1306b1025c507ec8c406a975558007e3e94e5cbd.zip llvm-1306b1025c507ec8c406a975558007e3e94e5cbd.tar.gz llvm-1306b1025c507ec8c406a975558007e3e94e5cbd.tar.bz2 |
[libc++][ranges] Implement ranges::count{, _if}
Reviewed By: var-const, Mordante, ldionne, #libc
Spies: tcanens, libcxx-commits, mgorny
Differential Revision: https://reviews.llvm.org/D121523
12 files changed, 758 insertions, 10 deletions
diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv index 33401e8f..68cd1a0 100644 --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -21,8 +21,8 @@ Search,minmax,Not assigned,n/a,Not started Search,min_element,Nikolas Klauser,`D117025 <https://llvm.org/D117025>`_,✅ Search,max_element,Nikolas Klauser,`D117523 <https://llvm.org/D117523>`_,✅ Search,minmax_element,Not assigned,n/a,Not started -Search,count,Not assigned,n/a,Not started -Search,count_if,Not assigned,n/a,Not started +Search,count,Nikolas Klauser, `D121523 <https://llvm.org/D121523>`_,✅ +Search,count_if,Nikolas Klauser, `D121523 <https://llvm.org/D121523>`_,✅ Search,search,Not assigned,n/a,Not started Search,search_n,Not assigned,n/a,Not started Search,find_end,Not assigned,n/a,Not started diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 6d37f91..be544cf 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -66,6 +66,8 @@ set(files __algorithm/pop_heap.h __algorithm/prev_permutation.h __algorithm/push_heap.h + __algorithm/ranges_count.h + __algorithm/ranges_count_if.h __algorithm/ranges_find.h __algorithm/ranges_find_if.h __algorithm/ranges_find_if_not.h diff --git a/libcxx/include/__algorithm/ranges_count.h b/libcxx/include/__algorithm/ranges_count.h new file mode 100644 index 0000000..3cbcdc9 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_count.h @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_COUNT_H +#define _LIBCPP___ALGORITHM_RANGES_COUNT_H + +#include <__algorithm/ranges_count_if.h> +#include <__config> +#include <__functional/identity.h> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/projected.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { +namespace __count { +struct __fn { + template <input_iterator _Iter, sentinel_for<_Iter> _Sent, class _Type, class _Proj = identity> + requires indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const _Type*> + _LIBCPP_HIDE_FROM_ABI constexpr + iter_difference_t<_Iter> operator()(_Iter __first, _Sent __last, const _Type& __value, _Proj __proj = {}) const { + auto __pred = [&](auto&& __e) { return __e == __value; }; + return ranges::__count_if_impl(std::move(__first), std::move(__last), __pred, __proj); + } + + template <input_range _Range, class _Type, class _Proj = identity> + requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const _Type*> + _LIBCPP_HIDE_FROM_ABI constexpr + range_difference_t<_Range> operator()(_Range&& __r, const _Type& __value, _Proj __proj = {}) const { + auto __pred = [&](auto&& __e) { return __e == __value; }; + return ranges::__count_if_impl(ranges::begin(__r), ranges::end(__r), __pred, __proj); + } +}; +} // namespace __count + +inline namespace __cpo { + inline constexpr auto count = __count::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_COUNT_H diff --git a/libcxx/include/__algorithm/ranges_count_if.h b/libcxx/include/__algorithm/ranges_count_if.h new file mode 100644 index 0000000..9080631 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_count_if.h @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_COUNT_IF_H +#define _LIBCPP___ALGORITHM_RANGES_COUNT_IF_H + +#include <__config> +#include <__functional/identity.h> +#include <__functional/invoke.h> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/projected.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { +template <class _Iter, class _Sent, class _Proj, class _Pred> +_LIBCPP_HIDE_FROM_ABI constexpr +iter_difference_t<_Iter> __count_if_impl(_Iter __first, _Sent __last, + _Pred& __pred, _Proj& __proj) { + iter_difference_t<_Iter> __counter(0); + for (; __first != __last; ++__first) { + if (std::invoke(__pred, std::invoke(__proj, *__first))) + ++__counter; + } + return __counter; +} + +namespace __count_if { +struct __fn { + template <input_iterator _Iter, sentinel_for<_Iter> _Sent, class _Proj = identity, + indirect_unary_predicate<projected<_Iter, _Proj>> _Predicate> + _LIBCPP_HIDE_FROM_ABI constexpr + iter_difference_t<_Iter> operator()(_Iter __first, _Sent __last, _Predicate __pred, _Proj __proj = {}) const { + return ranges::__count_if_impl(std::move(__first), std::move(__last), __pred, __proj); + } + + template <input_range _Range, class _Proj = identity, + indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Predicate> + _LIBCPP_HIDE_FROM_ABI constexpr + range_difference_t<_Range> operator()(_Range&& __r, _Predicate __pred, _Proj __proj = {}) const { + return ranges::__count_if_impl(ranges::begin(__r), ranges::end(__r), __pred, __proj); + } +}; +} // namespace __count_if + +inline namespace __cpo { + inline constexpr auto count_if = __count_if::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_COUNT_IF_H diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index d86cfe7..401e918 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -145,6 +145,26 @@ namespace ranges { constexpr ranges::binary_transform_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>, O> transform(R1&& r1, R2&& r2, O result, F binary_op, Proj1 proj1 = {}, Proj2 proj2 = {}); // since C++20 + + template<input_iterator I, sentinel_for<I> S, class T, class Proj = identity> + requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*> + constexpr iter_difference_t<I> + count(I first, S last, const T& value, Proj proj = {}); // since C++20 + + template<input_range R, class T, class Proj = identity> + requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*> + constexpr range_difference_t<R> + count(R&& r, const T& value, Proj proj = {}); // since C++20 + + template<input_iterator I, sentinel_for<I> S, class Proj = identity, + indirect_unary_predicate<projected<I, Proj>> Pred> + constexpr iter_difference_t<I> + count_if(I first, S last, Pred pred, Proj proj = {}); // since C++20 + + template<input_range R, class Proj = identity, + indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred> + constexpr range_difference_t<R> + count_if(R&& r, Pred pred, Proj proj = {}); // since C++20 } constexpr bool // constexpr in C++20 @@ -862,6 +882,8 @@ template <class BidirectionalIterator, class Compare> #include <__algorithm/pop_heap.h> #include <__algorithm/prev_permutation.h> #include <__algorithm/push_heap.h> +#include <__algorithm/ranges_count.h> +#include <__algorithm/ranges_count_if.h> #include <__algorithm/ranges_find.h> #include <__algorithm/ranges_find_if.h> #include <__algorithm/ranges_find_if_not.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index ef37387..cf74a07 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -294,6 +294,8 @@ module std [system] { module pop_heap { private header "__algorithm/pop_heap.h" } module prev_permutation { private header "__algorithm/prev_permutation.h" } module push_heap { private header "__algorithm/push_heap.h" } + module ranges_count { private header "__algorithm/ranges_count.h" } + module ranges_count_if { private header "__algorithm/ranges_count_if.h" } module ranges_find { private header "__algorithm/ranges_find.h" } module ranges_find_if { private header "__algorithm/ranges_find_if.h" } module ranges_find_if_not { private header "__algorithm/ranges_find_if_not.h" } diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp index 2ce0a77..35f7c36 100644 --- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp +++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp @@ -99,8 +99,8 @@ constexpr bool all_the_algorithms() //(void)std::ranges::binary_search(first, last, value, Less(&copies)); assert(copies == 0); //(void)std::ranges::binary_search(a, value, Less(&copies)); assert(copies == 0); //(void)std::ranges::clamp(value, value, value, Less(&copies)); assert(copies == 0); - //(void)std::ranges::count_if(first, last, UnaryTrue(&copies)); assert(copies == 0); - //(void)std::ranges::count_if(a, UnaryTrue(&copies)); assert(copies == 0); + (void)std::ranges::count_if(first, last, UnaryTrue(&copies)); assert(copies == 0); + (void)std::ranges::count_if(a, UnaryTrue(&copies)); assert(copies == 0); //(void)std::ranges::copy_if(first, last, first2, UnaryTrue(&copies)); assert(copies == 0); //(void)std::ranges::copy_if(a, first2, UnaryTrue(&copies)); assert(copies == 0); #if TEST_STD_VER > 20 diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp index e0b2764..e593216 100644 --- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp +++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp @@ -81,10 +81,10 @@ constexpr bool all_the_algorithms() //(void)std::ranges::binary_search(first, last, value, Less(), Proj(&copies)); assert(copies == 0); //(void)std::ranges::binary_search(a, value, Less(), Proj(&copies)); assert(copies == 0); //(void)std::ranges::clamp(T(), T(), T(), Less(), Proj(&copies)); assert(copies == 0); - //(void)std::ranges::count(first, last, value, Proj(&copies)); assert(copies == 0); - //(void)std::ranges::count(a, value, Proj(&copies)); assert(copies == 0); - //(void)std::ranges::count_if(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); - //(void)std::ranges::count_if(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); + (void)std::ranges::count(first, last, value, Proj(&copies)); assert(copies == 0); + (void)std::ranges::count(a, value, Proj(&copies)); assert(copies == 0); + (void)std::ranges::count_if(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); + (void)std::ranges::count_if(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); //(void)std::ranges::copy_if(first, last, first2, UnaryTrue(), Proj(&copies)); assert(copies == 0); //(void)std::ranges::copy_if(a, first2, UnaryTrue(), Proj(&copies)); assert(copies == 0); #if TEST_STD_VER > 20 diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp index 96c8014..f7117dd 100644 --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -103,6 +103,8 @@ END-SCRIPT #include <__algorithm/pop_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pop_heap.h'}} #include <__algorithm/prev_permutation.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/prev_permutation.h'}} #include <__algorithm/push_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/push_heap.h'}} +#include <__algorithm/ranges_count.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_count.h'}} +#include <__algorithm/ranges_count_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_count_if.h'}} #include <__algorithm/ranges_find.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_find.h'}} #include <__algorithm/ranges_find_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_find_if.h'}} #include <__algorithm/ranges_find_if_not.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_find_if_not.h'}} diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count.pass.cpp new file mode 100644 index 0000000..ae03c28 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count.pass.cpp @@ -0,0 +1,265 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// <algorithm> + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template<input_iterator I, sentinel_for<I> S, class T, class Proj = identity> +// requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*> +// constexpr iter_difference_t<I> +// ranges::count(I first, S last, const T& value, Proj proj = {}); +// template<input_range R, class T, class Proj = identity> +// requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*> +// constexpr range_difference_t<R> +// ranges::count(R&& r, const T& value, Proj proj = {}); + +#include <algorithm> +#include <array> +#include <cassert> +#include <ranges> + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +struct NotEqualityComparable { + bool operator==(NotEqualityComparable&&) const; + bool operator==(NotEqualityComparable&) const; + bool operator==(const NotEqualityComparable&&) const; +}; + +template <class It, class Sent = It> +concept HasCountIt = requires(It it, Sent sent) { std::ranges::count(it, sent, *it); }; +static_assert(HasCountIt<int*>); +static_assert(!HasCountIt<NotEqualityComparable*>); +static_assert(!HasCountIt<InputIteratorNotDerivedFrom>); +static_assert(!HasCountIt<InputIteratorNotIndirectlyReadable>); +static_assert(!HasCountIt<InputIteratorNotInputOrOutputIterator>); +static_assert(!HasCountIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>); +static_assert(!HasCountIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>); + +static_assert(!HasCountIt<int*, int>); +static_assert(!HasCountIt<int, int*>); + +template <class Range, class ValT> +concept HasCountR = requires(Range r) { std::ranges::count(r, ValT{}); }; +static_assert(HasCountR<std::array<int, 1>, int>); +static_assert(!HasCountR<int, int>); +static_assert(!HasCountR<std::array<NotEqualityComparable, 1>, NotEqualityComparable>); +static_assert(!HasCountR<InputRangeNotDerivedFrom, int>); +static_assert(!HasCountR<InputRangeNotIndirectlyReadable, int>); +static_assert(!HasCountR<InputRangeNotInputOrOutputIterator, int>); +static_assert(!HasCountR<InputRangeNotSentinelSemiregular, int>); +static_assert(!HasCountR<InputRangeNotSentinelEqualityComparableWith, int>); + +template <class It, class Sent = It> +constexpr void test_iterators() { + { + // simple test + { + int a[] = {1, 2, 3, 4}; + std::same_as<std::ptrdiff_t> auto ret = std::ranges::count(It(a), Sent(It(a + 4)), 3); + assert(ret == 1); + } + { + int a[] = {1, 2, 3, 4}; + auto range = std::ranges::subrange(It(a), Sent(It(a + 4))); + std::same_as<std::ptrdiff_t> auto ret = std::ranges::count(range, 3); + assert(ret == 1); + } + } + + { + // check that an empty range works + { + std::array<int, 0> a = {}; + auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 1); + assert(ret == 0); + } + { + std::array<int, 0> a = {}; + auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size()))); + auto ret = std::ranges::count(range, 1); + assert(ret == 0); + } + } + + { + // check that a range with a single element works + { + std::array a = {2}; + auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 2); + assert(ret == 1); + } + { + std::array a = {2}; + auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size()))); + auto ret = std::ranges::count(range, 2); + assert(ret == 1); + } + } + + { + // check that 0 is returned with no match + { + std::array a = {1, 1, 1}; + auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 0); + assert(ret == 0); + } + { + std::array a = {1, 1, 1}; + auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size()))); + auto ret = std::ranges::count(range, 0); + assert(ret == 0); + } + } + + { + // check that more than one element is counted + { + std::array a = {3, 3, 4, 3, 3}; + auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 3); + assert(ret == 4); + } + { + std::array a = {3, 3, 4, 3, 3}; + auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size()))); + auto ret = std::ranges::count(range, 3); + assert(ret == 4); + } + } + + { + // check that all elements are counted + { + std::array a = {5, 5, 5, 5}; + auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 5); + assert(ret == 4); + } + { + std::array a = {5, 5, 5, 5}; + auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size()))); + auto ret = std::ranges::count(range, 5); + assert(ret == 4); + } + } +} + +constexpr bool test() { + test_iterators<int*>(); + test_iterators<const int*>(); + test_iterators<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>(); + test_iterators<bidirectional_iterator<int*>>(); + test_iterators<forward_iterator<int*>>(); + test_iterators<random_access_iterator<int*>>(); + test_iterators<contiguous_iterator<int*>>(); + + { + // check that projections are used properly and that they are called with the iterator directly + { + int a[] = {1, 2, 3, 4}; + auto ret = std::ranges::count(a, a + 4, a + 3, [](int& i) { return &i; }); + assert(ret == 1); + } + { + int a[] = {1, 2, 3, 4}; + auto ret = std::ranges::count(a, a + 3, [](int& i) { return &i; }); + assert(ret == 1); + } + } + + { + // check that std::invoke is used + struct S { int i; }; + S a[] = { S{1}, S{3}, S{2} }; + std::same_as<std::ptrdiff_t> auto ret = std::ranges::count(a, 4, &S::i); + assert(ret == 0); + } + + { + // count invocations of the projection + { + int a[] = {1, 2, 3, 4}; + int projection_count = 0; + auto ret = std::ranges::count(a, a + 4, 2, [&](int i) { ++projection_count; return i; }); + assert(ret == 1); + assert(projection_count == 4); + } + { + int a[] = {1, 2, 3, 4}; + int projection_count = 0; + auto ret = std::ranges::count(a, 2, [&](int i) { ++projection_count; return i; }); + assert(ret == 1); + assert(projection_count == 4); + } + } + + { + // check that an immobile type works + struct NonMovable { + NonMovable(const NonMovable&) = delete; + NonMovable(NonMovable&&) = delete; + constexpr NonMovable(int i_) : i(i_) {} + int i; + + bool operator==(const NonMovable&) const = default; + }; + { + NonMovable a[] = {9, 8, 4, 3}; + auto ret = std::ranges::count(a, a + 4, NonMovable(8)); + assert(ret == 1); + } + { + NonMovable a[] = {9, 8, 4, 3}; + auto ret = std::ranges::count(a, NonMovable(8)); + assert(ret == 1); + } + } + + { + // check that difference_type is used + struct DiffTypeIterator { + using difference_type = signed char; + using value_type = int; + + int* it = nullptr; + + constexpr DiffTypeIterator() = default; + constexpr DiffTypeIterator(int* i) : it(i) {} + + constexpr int& operator*() const { return *it; } + constexpr DiffTypeIterator& operator++() { ++it; return *this; } + constexpr void operator++(int) { ++it; } + + bool operator==(const DiffTypeIterator&) const = default; + }; + + { + int a[] = {5, 5, 4, 3, 2, 1}; + std::same_as<signed char> decltype(auto) ret = + std::ranges::count(DiffTypeIterator(a), DiffTypeIterator(a + 6), 4); + assert(ret == 1); + } + { + int a[] = {5, 5, 4, 3, 2, 1}; + auto range = std::ranges::subrange(DiffTypeIterator(a), DiffTypeIterator(a + 6)); + std::same_as<signed char> decltype(auto) ret = std::ranges::count(range, 4); + assert(ret == 1); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count_if.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count_if.pass.cpp new file mode 100644 index 0000000..5e76245 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count_if.pass.cpp @@ -0,0 +1,323 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// <algorithm> + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template<input_iterator I, sentinel_for<I> S, class Proj = identity, +// indirect_unary_predicate<projected<I, Proj>> Pred> +// constexpr iter_difference_t<I> +// ranges::count_if(I first, S last, Pred pred, Proj proj = {}); +// template<input_range R, class Proj = identity, +// indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred> +// constexpr range_difference_t<R> +// ranges::count_if(R&& r, Pred pred, Proj proj = {}); + +#include <algorithm> +#include <array> +#include <cassert> +#include <ranges> + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +struct Predicate { + bool operator()(int); +}; + +template <class It, class Sent = It> +concept HasCountIfIt = requires(It it, Sent sent) { std::ranges::count_if(it, sent, Predicate{}); }; +static_assert(HasCountIfIt<int*>); +static_assert(!HasCountIfIt<InputIteratorNotDerivedFrom>); +static_assert(!HasCountIfIt<InputIteratorNotIndirectlyReadable>); +static_assert(!HasCountIfIt<InputIteratorNotInputOrOutputIterator>); +static_assert(!HasCountIfIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>); +static_assert(!HasCountIfIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>); + +static_assert(!HasCountIfIt<int*, int>); +static_assert(!HasCountIfIt<int, int*>); + +template <class Pred> +concept HasCountIfPred = requires(int* it, Pred pred) {std::ranges::count_if(it, it, pred); }; + +static_assert(!HasCountIfPred<IndirectUnaryPredicateNotCopyConstructible>); +static_assert(!HasCountIfPred<IndirectUnaryPredicateNotPredicate>); + +template <class R> +concept HasCountIfR = requires(R r) { std::ranges::count_if(r, Predicate{}); }; +static_assert(HasCountIfR<std::array<int, 0>>); +static_assert(!HasCountIfR<int>); +static_assert(!HasCountIfR<InputRangeNotDerivedFrom>); +static_assert(!HasCountIfR<InputRangeNotIndirectlyReadable>); +static_assert(!HasCountIfR<InputRangeNotInputOrOutputIterator>); +static_assert(!HasCountIfR<InputRangeNotSentinelSemiregular>); +static_assert(!HasCountIfR<InputRangeNotSentinelEqualityComparableWith>); + +template <class It, class Sent = It> +constexpr void test_iterators() { + { + // simple test + { + int a[] = {1, 2, 3, 4}; + std::same_as<std::ptrdiff_t> auto ret = + std::ranges::count_if(It(a), Sent(It(a + 4)), [](int x) { return x == 4; }); + assert(ret == 1); + } + { + int a[] = {1, 2, 3, 4}; + auto range = std::ranges::subrange(It(a), Sent(It(a + 4))); + std::same_as<std::ptrdiff_t> auto ret = + std::ranges::count_if(range, [](int x) { return x == 4; }); + assert(ret == 1); + } + } + + { + // check that an empty range works + { + std::array<int, 0> a = {}; + auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int) { return true; }); + assert(ret == 0); + } + { + std::array<int, 0> a = {}; + auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size()))); + auto ret = std::ranges::count_if(range, [](int) { return true; }); + assert(ret == 0); + } + } + + { + // check that a range with a single element works + { + std::array a = {2}; + auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int i) { return i == 2; }); + assert(ret == 1); + } + { + std::array a = {2}; + auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size()))); + auto ret = std::ranges::count_if(range, [](int i) { return i == 2; }); + assert(ret == 1); + } + } + + { + // check that 0 is returned with no match + { + int a[] = {1, 1, 1}; + auto ret = std::ranges::count_if(It(a), Sent(It(a + 3)), [](int) { return false; }); + assert(ret == 0); + } + { + int a[] = {1, 1, 1}; + auto range = std::ranges::subrange(It(a), Sent(It(a + 3))); + auto ret = std::ranges::count_if(range, [](int){ return false; }); + assert(ret == 0); + } + } + + { + // check that more than one element is counted + { + std::array a = {3, 3, 4, 3, 3}; + auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int i) { return i == 3; }); + assert(ret == 4); + } + { + std::array a = {3, 3, 4, 3, 3}; + auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size()))); + auto ret = std::ranges::count_if(range, [](int i) { return i == 3; }); + assert(ret == 4); + } + } + + { + // check that all elements are counted + { + std::array a = {5, 5, 5, 5}; + auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int) { return true; }); + assert(ret == 4); + } + { + std::array a = {5, 5, 5, 5}; + auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size()))); + auto ret = std::ranges::count_if(range, [](int) { return true; }); + assert(ret == 4); + } + } +} + +constexpr bool test() { + test_iterators<int*>(); + test_iterators<const int*>(); + test_iterators<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>(); + test_iterators<bidirectional_iterator<int*>>(); + test_iterators<forward_iterator<int*>>(); + test_iterators<random_access_iterator<int*>>(); + test_iterators<contiguous_iterator<int*>>(); + + { + // check that projections are used properly and that they are called with the iterator directly + { + int a[] = {1, 2, 3, 4}; + auto ret = std::ranges::count_if(a, a + 4, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; }); + assert(ret == 1); + } + { + int a[] = {1, 2, 3, 4}; + auto ret = std::ranges::count_if(a, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; }); + assert(ret == 1); + } + } + + { + // check that std::invoke is used + { + struct S { + int comp; + int other; + }; + S a[] = { {0, 0}, {0, 2}, {0, 1} }; + auto ret = std::ranges::count_if(a, [](int i){ return i == 0; }, &S::comp); + assert(ret == 3); + } + { + struct S { + int comp; + int other; + }; + S a[] = { {0, 0}, {0, 2}, {0, 1} }; + auto ret = std::ranges::count_if(a, a + 3, [](int i) { return i == 0; }, &S::comp); + assert(ret == 3); + } + } + + { + // check projection and predicate invocation count + { + int a[] = {1, 2, 3, 4}; + int predicate_count = 0; + int projection_count = 0; + auto ret = std::ranges::count_if(a, a + 4, + [&](int i) { ++predicate_count; return i == 2; }, + [&](int i) { ++projection_count; return i; }); + assert(ret == 1); + assert(predicate_count == 4); + assert(projection_count == 4); + } + { + int a[] = {1, 2, 3, 4}; + int predicate_count = 0; + int projection_count = 0; + auto ret = std::ranges::count_if(a, + [&](int i) { ++predicate_count; return i == 2; }, + [&](int i) { ++projection_count; return i; }); + assert(ret == 1); + assert(predicate_count == 4); + assert(projection_count == 4); + } + } + + { + // check that an immobile type works + struct NonMovable { + NonMovable(const NonMovable&) = delete; + NonMovable(NonMovable&&) = delete; + constexpr NonMovable(int i_) : i(i_) {} + int i; + + bool operator==(const NonMovable&) const = default; + }; + { + NonMovable a[] = {9, 8, 4, 3}; + auto ret = std::ranges::count_if(a, a + 4, [](const NonMovable& i) { return i == NonMovable(8); }); + assert(ret == 1); + } + { + NonMovable a[] = {9, 8, 4, 3}; + auto ret = std::ranges::count_if(a, [](const NonMovable& i) { return i == NonMovable(8); }); + assert(ret == 1); + } + } + + { + // check that difference_type is used + struct DiffTypeIterator { + using difference_type = signed char; + using value_type = int; + + int* it = nullptr; + + constexpr DiffTypeIterator() = default; + constexpr DiffTypeIterator(int* i) : it(i) {} + + constexpr int& operator*() const { return *it; } + constexpr DiffTypeIterator& operator++() { ++it; return *this; } + constexpr void operator++(int) { ++it; } + + bool operator==(const DiffTypeIterator&) const = default; + }; + + { + int a[] = {5, 5, 4, 3, 2, 1}; + std::same_as<signed char> auto ret = + std::ranges::count_if(DiffTypeIterator(a), DiffTypeIterator(a + 6), [](int& i) { return i == 4; }); + assert(ret == 1); + } + { + int a[] = {5, 5, 4, 3, 2, 1}; + auto range = std::ranges::subrange(DiffTypeIterator(a), DiffTypeIterator(a + 6)); + std::same_as<signed char> auto ret = std::ranges::count_if(range, [](int& i) { return i == 4; }); + assert(ret == 1); + } + } + + { + // check that the predicate can take the argument by lvalue ref + { + int a[] = {9, 8, 4, 3}; + auto ret = std::ranges::count_if(a, a + 4, [](int& i) { return i == 8; }); + assert(ret == 1); + } + { + int a[] = {9, 8, 4, 3}; + auto ret = std::ranges::count_if(a, [](int& i) { return i == 8; }); + assert(ret == 1); + } + } + + { + // check that the predicate isn't made const + struct MutablePredicate { + constexpr bool operator()(int i) & { return i == 8; } + constexpr bool operator()(int i) && { return i == 8; } + }; + { + int a[] = {9, 8, 4, 3}; + auto ret = std::ranges::count_if(a, a + 4, MutablePredicate{}); + assert(ret == 1); + } + { + int a[] = {9, 8, 4, 3}; + auto ret = std::ranges::count_if(a, MutablePredicate{}); + assert(ret == 1); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp index 71b43b2..09cce4c 100644 --- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp +++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp @@ -69,8 +69,8 @@ auto odd = [](int x) { return x % 2 != 0; }; //static_assert(test(std::ranges::copy_backward, a, a)); //static_assert(test(std::ranges::copy_if, a, a, odd)); //static_assert(test(std::ranges::copy_n, a, 10, a)); -//static_assert(test(std::ranges::count, a, 42)); -//static_assert(test(std::ranges::count_if, a, odd)); +static_assert(test(std::ranges::count, a, 42)); +static_assert(test(std::ranges::count_if, a, odd)); //static_assert(test(std::ranges::ends_with, a, a)); //static_assert(test(std::ranges::equal, a, a)); //static_assert(test(std::ranges::equal_range, a, 42)); |