[libc++] Implement ranges::filter_view

Differential Revision: https://reviews.llvm.org/D109086
This commit is contained in:
Louis Dionne 2021-07-14 17:01:25 -04:00
parent 27e8c50a4c
commit 2b424f4ea8
32 changed files with 2683 additions and 2 deletions

View File

@ -151,7 +151,7 @@ Section,Description,Dependencies,Assignee,Complete
`[range.iota] <https://wg21.link/range.iota>`_,`iota_view <https://llvm.org/D107396>`_,[range.all],Zoe Carver,✅
`[range.all] <https://wg21.link/range.all>`_,`view::all <https://llvm.org/D102028>`_,"[range.subrange], [range.view.ref]",Zoe Carver,✅
`[range.ref.view] <https://wg21.link/range.ref.view>`_,`ref_view <https://llvm.org/D102020>`_,[view.interface],Zoe Carver,✅
`[range.filter] <https://wg21.link/range.filter>`_,`filter_view <https://llvm.org/D109086>`_,[range.all],Louis Dionne,Under review
`[range.filter] <https://wg21.link/range.filter>`_,`filter_view <https://llvm.org/D109086>`_,[range.all],Louis Dionne,
`[range.transform] <https://wg21.link/range.transform>`_,`transform_view <https://llvm.org/D103056>`_,[range.all],Zoe Carver,✅
`[range.take] <https://wg21.link/range.take>`_,`take_view <https://llvm.org/D106507>`_,[range.all],Zoe Carver,✅
`[range.join] <https://wg21.link/range.join>`_,`join_view <https://llvm.org/D107671>`_,[range.all],Zoe Carver,✅

1 Section Description Dependencies Assignee Complete
151
152
153
154
155
156
157

View File

@ -371,6 +371,7 @@ set(files
__ranges/empty_view.h
__ranges/enable_borrowed_range.h
__ranges/enable_view.h
__ranges/filter_view.h
__ranges/iota_view.h
__ranges/join_view.h
__ranges/lazy_split_view.h

View File

@ -0,0 +1,259 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// 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___RANGES_FILTER_VIEW_H
#define _LIBCPP___RANGES_FILTER_VIEW_H
#include <__algorithm/ranges_find_if.h>
#include <__config>
#include <__debug>
#include <__functional/bind_back.h>
#include <__functional/invoke.h>
#include <__functional/reference_wrapper.h>
#include <__iterator/concepts.h>
#include <__iterator/iter_move.h>
#include <__iterator/iter_swap.h>
#include <__iterator/iterator_traits.h>
#include <__memory/addressof.h>
#include <__ranges/access.h>
#include <__ranges/all.h>
#include <__ranges/concepts.h>
#include <__ranges/copyable_box.h>
#include <__ranges/non_propagating_cache.h>
#include <__ranges/range_adaptor.h>
#include <__ranges/view_interface.h>
#include <__utility/forward.h>
#include <__utility/in_place.h>
#include <__utility/move.h>
#include <concepts>
#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
namespace ranges {
template<input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
requires view<_View> && is_object_v<_Pred>
class filter_view : public view_interface<filter_view<_View, _Pred>> {
_LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
_LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_;
// We cache the result of begin() to allow providing an amortized O(1) begin() whenever
// the underlying range is at least a forward_range.
static constexpr bool _UseCache = forward_range<_View>;
using _Cache = _If<_UseCache, __non_propagating_cache<iterator_t<_View>>, __empty_cache>;
_LIBCPP_NO_UNIQUE_ADDRESS _Cache __cached_begin_ = _Cache();
class __iterator;
class __sentinel;
public:
_LIBCPP_HIDE_FROM_ABI
filter_view() requires default_initializable<_View> && default_initializable<_Pred> = default;
_LIBCPP_HIDE_FROM_ABI
constexpr filter_view(_View __base, _Pred __pred)
: __base_(std::move(__base)), __pred_(in_place, std::move(__pred))
{ }
template<class _Vp = _View>
_LIBCPP_HIDE_FROM_ABI
constexpr _View base() const& requires copy_constructible<_Vp> { return __base_; }
_LIBCPP_HIDE_FROM_ABI
constexpr _View base() && { return std::move(__base_); }
_LIBCPP_HIDE_FROM_ABI
constexpr _Pred const& pred() const { return *__pred_; }
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator begin() {
_LIBCPP_ASSERT(__pred_.__has_value(), "Trying to call begin() on a filter_view that does not have a valid predicate.");
if constexpr (_UseCache) {
if (!__cached_begin_.__has_value()) {
__cached_begin_.__emplace(ranges::find_if(__base_, std::ref(*__pred_)));
}
return {*this, *__cached_begin_};
} else {
return {*this, ranges::find_if(__base_, std::ref(*__pred_))};
}
}
_LIBCPP_HIDE_FROM_ABI
constexpr auto end() {
if constexpr (common_range<_View>)
return __iterator{*this, ranges::end(__base_)};
else
return __sentinel{*this};
}
};
template<class _Range, class _Pred>
filter_view(_Range&&, _Pred) -> filter_view<views::all_t<_Range>, _Pred>;
template<class _View>
struct __filter_iterator_category { };
template<forward_range _View>
struct __filter_iterator_category<_View> {
using _Cat = typename iterator_traits<iterator_t<_View>>::iterator_category;
using iterator_category =
_If<derived_from<_Cat, bidirectional_iterator_tag>, bidirectional_iterator_tag,
_If<derived_from<_Cat, forward_iterator_tag>, forward_iterator_tag,
/* else */ _Cat
>>;
};
template<input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
requires view<_View> && is_object_v<_Pred>
class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
public:
_LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
_LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_ = nullptr;
using iterator_concept =
_If<bidirectional_range<_View>, bidirectional_iterator_tag,
_If<forward_range<_View>, forward_iterator_tag,
/* else */ input_iterator_tag
>>;
// using iterator_category = inherited;
using value_type = range_value_t<_View>;
using difference_type = range_difference_t<_View>;
_LIBCPP_HIDE_FROM_ABI
__iterator() requires default_initializable<iterator_t<_View>> = default;
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
: __current_(std::move(__current)), __parent_(std::addressof(__parent))
{ }
_LIBCPP_HIDE_FROM_ABI
constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
_LIBCPP_HIDE_FROM_ABI
constexpr iterator_t<_View> base() && { return std::move(__current_); }
_LIBCPP_HIDE_FROM_ABI
constexpr range_reference_t<_View> operator*() const { return *__current_; }
_LIBCPP_HIDE_FROM_ABI
constexpr iterator_t<_View> operator->() const
requires __has_arrow<iterator_t<_View>> && copyable<iterator_t<_View>>
{
return __current_;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator& operator++() {
__current_ = ranges::find_if(std::move(++__current_), ranges::end(__parent_->__base_),
std::ref(*__parent_->__pred_));
return *this;
}
_LIBCPP_HIDE_FROM_ABI
constexpr void operator++(int) { ++*this; }
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator operator++(int) requires forward_range<_View> {
auto __tmp = *this;
++*this;
return __tmp;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator& operator--() requires bidirectional_range<_View> {
do {
--__current_;
} while (!std::invoke(*__parent_->__pred_, *__current_));
return *this;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator operator--(int) requires bidirectional_range<_View> {
auto tmp = *this;
--*this;
return tmp;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
requires equality_comparable<iterator_t<_View>>
{
return __x.__current_ == __y.__current_;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr range_rvalue_reference_t<_View> iter_move(__iterator const& __it)
noexcept(noexcept(ranges::iter_move(__it.__current_)))
{
return ranges::iter_move(__it.__current_);
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr void iter_swap(__iterator const& __x, __iterator const& __y)
noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
requires indirectly_swappable<iterator_t<_View>>
{
return ranges::iter_swap(__x.__current_, __y.__current_);
}
};
template<input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
requires view<_View> && is_object_v<_Pred>
class filter_view<_View, _Pred>::__sentinel {
public:
sentinel_t<_View> __end_ = sentinel_t<_View>();
_LIBCPP_HIDE_FROM_ABI
__sentinel() = default;
_LIBCPP_HIDE_FROM_ABI
constexpr explicit __sentinel(filter_view& __parent)
: __end_(ranges::end(__parent.__base_))
{ }
_LIBCPP_HIDE_FROM_ABI
constexpr sentinel_t<_View> base() const { return __end_; }
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
return __x.__current_ == __y.__end_;
}
};
namespace views {
namespace __filter {
struct __fn {
template<class _Range, class _Pred>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
constexpr auto operator()(_Range&& __range, _Pred&& __pred) const
noexcept(noexcept(filter_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))))
-> decltype( filter_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)))
{ return filter_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)); }
template<class _Pred>
requires constructible_from<decay_t<_Pred>, _Pred>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
constexpr auto operator()(_Pred&& __pred) const
noexcept(is_nothrow_constructible_v<decay_t<_Pred>, _Pred>)
{ return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pred>(__pred))); }
};
} // namespace __filter
inline namespace __cpo {
inline constexpr auto filter = __filter::__fn{};
} // namespace __cpo
} // namespace views
} // namespace ranges
#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___RANGES_FILTER_VIEW_H

View File

@ -849,6 +849,7 @@ module std [system] {
module empty_view { private header "__ranges/empty_view.h" }
module enable_borrowed_range { private header "__ranges/enable_borrowed_range.h" }
module enable_view { private header "__ranges/enable_view.h" }
module filter_view { private header "__ranges/filter_view.h" }
module iota_view { private header "__ranges/iota_view.h" }
module join_view { private header "__ranges/join_view.h" }
module lazy_split_view { private header "__ranges/lazy_split_view.h" }

View File

@ -150,6 +150,15 @@ namespace std::ranges {
template<class T>
inline constexpr bool enable_borrowed_range<owning_view<T>> = enable_borrowed_range<T>;
// [range.filter], filter view
template<input_range V, indirect_unary_predicate<iterator_t<V>> Pred>
requires view<V> && is_object_v<Pred>
class filter_view;
namespace views {
inline constexpr unspecified filter = unspecified;
}
// [range.drop], drop view
template<view V>
class drop_view;
@ -266,6 +275,7 @@ namespace std {
#include <__ranges/empty_view.h>
#include <__ranges/enable_borrowed_range.h>
#include <__ranges/enable_view.h>
#include <__ranges/filter_view.h>
#include <__ranges/iota_view.h>
#include <__ranges/join_view.h>
#include <__ranges/lazy_split_view.h>

View File

@ -402,6 +402,7 @@ END-SCRIPT
#include <__ranges/empty_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty_view.h'}}
#include <__ranges/enable_borrowed_range.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/enable_borrowed_range.h'}}
#include <__ranges/enable_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/enable_view.h'}}
#include <__ranges/filter_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/filter_view.h'}}
#include <__ranges/iota_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/iota_view.h'}}
#include <__ranges/join_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/join_view.h'}}
#include <__ranges/lazy_split_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/lazy_split_view.h'}}

View File

@ -89,7 +89,7 @@ static_assert(test(std::views::counted, a, 10));
//static_assert(test(std::views::drop, a, 10));
//static_assert(test(std::views::drop_while, a, [](int x){ return x < 10; }));
//static_assert(test(std::views::elements<0>, pairs));
//static_assert(test(std::views::filter, a, [](int x){ return x < 10; }));
static_assert(test(std::views::filter, a, [](int x){ return x < 10; }));
//static_assert(test(std::views::join, arrays));
static_assert(test(std::views::lazy_split, a, 4));
static_assert(test(std::views::reverse, a));

View File

@ -0,0 +1,170 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// std::views::filter
#include <ranges>
#include <cassert>
#include <concepts>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include "test_iterators.h"
template <class View, class T>
concept CanBePiped = requires (View&& view, T&& t) {
{ std::forward<View>(view) | std::forward<T>(t) };
};
struct NonCopyablePredicate {
NonCopyablePredicate(NonCopyablePredicate const&) = delete;
template <class T>
constexpr bool operator()(T x) const { return x % 2 == 0; }
};
struct Range : std::ranges::view_base {
using Iterator = forward_iterator<int*>;
using Sentinel = sentinel_wrapper<Iterator>;
constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
constexpr Iterator begin() const { return Iterator(begin_); }
constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
private:
int* begin_;
int* end_;
};
struct Pred {
constexpr bool operator()(int i) const { return i % 2 == 0; }
};
template <typename View>
constexpr void compareViews(View v, std::initializer_list<int> list) {
auto b1 = v.begin();
auto e1 = v.end();
auto b2 = list.begin();
auto e2 = list.end();
for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
assert(*b1 == *b2);
}
assert(b1 == e1);
assert(b2 == e2);
}
constexpr bool test() {
int buff[] = {0, 1, 2, 3, 4, 5, 6, 7};
// Test `views::filter(pred)(v)`
{
using Result = std::ranges::filter_view<Range, Pred>;
Range const range(buff, buff + 8);
Pred pred;
{
std::same_as<Result> decltype(auto) result = std::views::filter(pred)(range);
compareViews(result, {0, 2, 4, 6});
}
{
auto const partial = std::views::filter(pred);
std::same_as<Result> decltype(auto) result = partial(range);
compareViews(result, {0, 2, 4, 6});
}
}
// Test `v | views::filter(pred)`
{
using Result = std::ranges::filter_view<Range, Pred>;
Range const range(buff, buff + 8);
Pred pred;
{
std::same_as<Result> decltype(auto) result = range | std::views::filter(pred);
compareViews(result, {0, 2, 4, 6});
}
{
auto const partial = std::views::filter(pred);
std::same_as<Result> decltype(auto) result = range | partial;
compareViews(result, {0, 2, 4, 6});
}
}
// Test `views::filter(v, pred)`
{
using Result = std::ranges::filter_view<Range, Pred>;
Range const range(buff, buff + 8);
Pred pred;
std::same_as<Result> decltype(auto) result = std::views::filter(range, pred);
compareViews(result, {0, 2, 4, 6});
}
// Test that one can call std::views::filter with arbitrary stuff, as long as we
// don't try to actually complete the call by passing it a range.
//
// That makes no sense and we can't do anything with the result, but it's valid.
{
struct X { };
[[maybe_unused]] auto partial = std::views::filter(X{});
}
// Test `adaptor | views::filter(pred)`
{
Range const range(buff, buff + 8);
{
auto pred1 = [](int i) { return i % 2 == 0; };
auto pred2 = [](int i) { return i % 3 == 0; };
using Result = std::ranges::filter_view<std::ranges::filter_view<Range, decltype(pred1)>, decltype(pred2)>;
std::same_as<Result> decltype(auto) result = range | std::views::filter(pred1) | std::views::filter(pred2);
compareViews(result, {0, 6});
}
{
auto pred1 = [](int i) { return i % 2 == 0; };
auto pred2 = [](int i) { return i % 3 == 0; };
using Result = std::ranges::filter_view<std::ranges::filter_view<Range, decltype(pred1)>, decltype(pred2)>;
auto const partial = std::views::filter(pred1) | std::views::filter(pred2);
std::same_as<Result> decltype(auto) result = range | partial;
compareViews(result, {0, 6});
}
}
// Test SFINAE friendliness
{
struct NotAView { };
struct NotInvocable { };
static_assert(!CanBePiped<Range, decltype(std::views::filter)>);
static_assert( CanBePiped<Range, decltype(std::views::filter(Pred{}))>);
static_assert(!CanBePiped<NotAView, decltype(std::views::filter(Pred{}))>);
static_assert(!CanBePiped<Range, decltype(std::views::filter(NotInvocable{}))>);
static_assert(!std::is_invocable_v<decltype(std::views::filter)>);
static_assert(!std::is_invocable_v<decltype(std::views::filter), Pred, Range>);
static_assert( std::is_invocable_v<decltype(std::views::filter), Range, Pred>);
static_assert(!std::is_invocable_v<decltype(std::views::filter), Range, Pred, Pred>);
static_assert(!std::is_invocable_v<decltype(std::views::filter), NonCopyablePredicate>);
}
{
static_assert(std::is_same_v<decltype(std::ranges::views::filter), decltype(std::views::filter)>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,92 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr View base() const& requires copy_constructible<View>;
// constexpr View base() &&;
#include <ranges>
#include <cassert>
#include <concepts>
#include <utility>
struct Range : std::ranges::view_base {
constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) { }
constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) { }
Range& operator=(Range const&) = default;
Range& operator=(Range&&) = default;
constexpr int* begin() const { return begin_; }
constexpr int* end() const { return end_; }
int* begin_;
int* end_;
bool wasCopyInitialized = false;
bool wasMoveInitialized = false;
};
struct Pred {
bool operator()(int) const;
};
struct NoCopyRange : std::ranges::view_base {
explicit NoCopyRange(int*, int*);
NoCopyRange(NoCopyRange const&) = delete;
NoCopyRange(NoCopyRange&&) = default;
NoCopyRange& operator=(NoCopyRange const&) = default;
NoCopyRange& operator=(NoCopyRange&&) = default;
int* begin() const;
int* end() const;
};
template <typename T>
concept can_call_base_on = requires(T t) { std::forward<T>(t).base(); };
constexpr bool test() {
int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
// Check the const& overload
{
Range range(buff, buff + 8);
std::ranges::filter_view<Range, Pred> const view(range, Pred{});
std::same_as<Range> decltype(auto) result = view.base();
assert(result.wasCopyInitialized);
assert(result.begin() == buff);
assert(result.end() == buff + 8);
}
// Check the && overload
{
Range range(buff, buff + 8);
std::ranges::filter_view<Range, Pred> view(range, Pred{});
std::same_as<Range> decltype(auto) result = std::move(view).base();
assert(result.wasMoveInitialized);
assert(result.begin() == buff);
assert(result.end() == buff + 8);
}
// Ensure the const& overload is not considered when the base is not copy-constructible
{
static_assert(!can_call_base_on<std::ranges::filter_view<NoCopyRange, Pred> const&>);
static_assert(!can_call_base_on<std::ranges::filter_view<NoCopyRange, Pred>&>);
static_assert( can_call_base_on<std::ranges::filter_view<NoCopyRange, Pred>&&>);
static_assert( can_call_base_on<std::ranges::filter_view<NoCopyRange, Pred>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,201 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator begin();
#include <ranges>
#include <cassert>
#include "test_iterators.h"
#include "types.h"
struct Range : std::ranges::view_base {
using Iterator = forward_iterator<int*>;
using Sentinel = sentinel_wrapper<Iterator>;
constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
constexpr Iterator begin() const { return Iterator(begin_); }
constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
private:
int* begin_;
int* end_;
};
// A range that isn't a forward_range, used to test filter_view
// when we don't cache the result of begin()
struct InputRange : std::ranges::view_base {
using Iterator = cpp17_input_iterator<int*>;
using Sentinel = sentinel_wrapper<Iterator>;
constexpr explicit InputRange(int* b, int* e) : begin_(b), end_(e) { }
constexpr Iterator begin() const { return Iterator(begin_); }
constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
private:
int* begin_;
int* end_;
};
struct TrackingPred : TrackInitialization {
using TrackInitialization::TrackInitialization;
constexpr bool operator()(int i) const { return i % 2 == 0; }
};
template <typename Range>
constexpr void general_tests() {
int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
// Check the return type of `.begin()`
{
Range range(buff, buff + 1);
auto pred = [](int) { return true; };
std::ranges::filter_view view(range, pred);
using FilterIterator = std::ranges::iterator_t<decltype(view)>;
ASSERT_SAME_TYPE(FilterIterator, decltype(view.begin()));
}
// begin() over an empty range
{
Range range(buff, buff);
auto pred = [](int) { return true; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff);
assert(it == view.end());
}
// begin() over a 1-element range
{
{
Range range(buff, buff + 1);
auto pred = [](int i) { return i == 1; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff);
}
{
Range range(buff, buff + 1);
auto pred = [](int) { return false; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff + 1);
assert(it == view.end());
}
}
// begin() over a 2-element range
{
{
Range range(buff, buff + 2);
auto pred = [](int i) { return i == 1; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff);
}
{
Range range(buff, buff + 2);
auto pred = [](int i) { return i == 2; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff + 1);
}
{
Range range(buff, buff + 2);
auto pred = [](int) { return false; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff + 2);
assert(it == view.end());
}
}
// begin() over a N-element range
{
for (int k = 1; k != 8; ++k) {
Range range(buff, buff + 8);
auto pred = [=](int i) { return i == k; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff + (k - 1));
}
{
Range range(buff, buff + 8);
auto pred = [](int) { return false; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff + 8);
assert(it == view.end());
}
}
// Make sure we do not make a copy of the predicate when we call begin()
// (we should be passing it to ranges::find_if using std::ref)
{
bool moved = false, copied = false;
Range range(buff, buff + 2);
std::ranges::filter_view view(range, TrackingPred(&moved, &copied));
moved = false;
copied = false;
[[maybe_unused]] auto it = view.begin();
assert(!moved);
assert(!copied);
}
// Test with a non-const predicate
{
Range range(buff, buff + 8);
auto pred = [](int i) mutable { return i % 2 == 0; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff + 1);
}
// Test with a predicate that takes by non-const reference
{
Range range(buff, buff + 8);
auto pred = [](int& i) { return i % 2 == 0; };
std::ranges::filter_view view(range, pred);
auto it = view.begin();
assert(base(it.base()) == buff + 1);
}
}
template <typename ForwardRange>
constexpr void cache_tests() {
int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
// Make sure that we cache the result of begin() on subsequent calls
// (only applies to forward_ranges)
ForwardRange range(buff, buff + 8);
int called = 0;
auto pred = [&](int i) { ++called; return i == 3; };
std::ranges::filter_view view(range, pred);
assert(called == 0);
for (int k = 0; k != 3; ++k) {
auto it = view.begin();
assert(base(it.base()) == buff + 2);
assert(called == 3);
}
}
constexpr bool test() {
general_tests<Range>();
general_tests<InputRange>(); // test when we don't cache the result
cache_tests<Range>();
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,110 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// Check constraints on the type itself.
//
// template<input_range View, indirect_unary_predicate<iterator_t<View>> Pred>
// requires view<View> && is_object_v<Pred>
// class filter_view;
#include <ranges>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <type_traits>
template <class View, class Pred>
concept can_form_filter_view = requires {
typename std::ranges::filter_view<View, Pred>;
};
// filter_view is not valid when the view is not an input_range
namespace test1 {
struct View : std::ranges::view_base {
struct NotInputIterator {
NotInputIterator& operator++();
void operator++(int);
int& operator*() const;
using difference_type = std::ptrdiff_t;
friend bool operator==(NotInputIterator const&, NotInputIterator const&);
};
NotInputIterator begin() const;
NotInputIterator end() const;
};
struct Pred { bool operator()(int) const; };
static_assert(!std::ranges::input_range<View>);
static_assert( std::indirect_unary_predicate<Pred, int*>);
static_assert( std::ranges::view<View>);
static_assert( std::is_object_v<Pred>);
static_assert(!can_form_filter_view<View, Pred>);
}
// filter_view is not valid when the predicate is not indirect_unary_predicate
namespace test2 {
struct View : std::ranges::view_base {
int* begin() const;
int* end() const;
};
struct Pred { };
static_assert( std::ranges::input_range<View>);
static_assert(!std::indirect_unary_predicate<Pred, int*>);
static_assert( std::ranges::view<View>);
static_assert( std::is_object_v<Pred>);
static_assert(!can_form_filter_view<View, Pred>);
}
// filter_view is not valid when the view is not a view
namespace test3 {
struct View {
int* begin() const;
int* end() const;
};
struct Pred { bool operator()(int) const; };
static_assert( std::ranges::input_range<View>);
static_assert( std::indirect_unary_predicate<Pred, int*>);
static_assert(!std::ranges::view<View>);
static_assert( std::is_object_v<Pred>);
static_assert(!can_form_filter_view<View, Pred>);
}
// filter_view is not valid when the predicate is not an object type
namespace test4 {
struct View : std::ranges::view_base {
int* begin() const;
int* end() const;
};
using Pred = bool(&)(int);
static_assert( std::ranges::input_range<View>);
static_assert( std::indirect_unary_predicate<Pred, int*>);
static_assert( std::ranges::view<View>);
static_assert(!std::is_object_v<Pred>);
static_assert(!can_form_filter_view<View, Pred>);
}
// filter_view is valid when all the constraints are satisfied (test the test)
namespace test5 {
struct View : std::ranges::view_base {
int* begin() const;
int* end() const;
};
struct Pred { bool operator()(int) const; };
static_assert( std::ranges::input_range<View>);
static_assert( std::indirect_unary_predicate<Pred, int*>);
static_assert( std::ranges::view<View>);
static_assert( std::is_object_v<Pred>);
static_assert( can_form_filter_view<View, Pred>);
}

View File

@ -0,0 +1,64 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// template <class Range, class Pred>
// filter_view(Range&&, Pred) -> filter_view<views::all_t<Range>, Pred>;
#include <ranges>
#include <cassert>
#include <type_traits>
#include "test_iterators.h"
struct View : std::ranges::view_base {
View() = default;
forward_iterator<int*> begin() const;
sentinel_wrapper<forward_iterator<int*>> end() const;
};
static_assert(std::ranges::view<View>);
// A range that is not a view
struct Range {
Range() = default;
forward_iterator<int*> begin() const;
sentinel_wrapper<forward_iterator<int*>> end() const;
};
static_assert(std::ranges::range<Range> && !std::ranges::view<Range>);
struct Pred {
constexpr bool operator()(int i) const { return i % 2 == 0; }
};
constexpr bool test() {
{
View v;
Pred pred;
std::ranges::filter_view view(v, pred);
static_assert(std::is_same_v<decltype(view), std::ranges::filter_view<View, Pred>>);
}
// Test with a range that isn't a view, to make sure we properly use views::all_t in the implementation.
{
Range r;
Pred pred;
std::ranges::filter_view view(r, pred);
static_assert(std::is_same_v<decltype(view), std::ranges::filter_view<std::ranges::ref_view<Range>, Pred>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,108 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// filter_view() requires std::default_initializable<View> &&
// std::default_initializable<Pred> = default;
#include <ranges>
#include <cassert>
#include <type_traits>
constexpr int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
struct DefaultConstructibleView : std::ranges::view_base {
constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 8) { }
constexpr int const* begin() const { return begin_; }
constexpr int const* end() const { return end_; }
private:
int const* begin_;
int const* end_;
};
struct DefaultConstructiblePredicate {
DefaultConstructiblePredicate() = default;
constexpr bool operator()(int i) const { return i % 2 == 0; }
};
struct NoDefaultView : std::ranges::view_base {
NoDefaultView() = delete;
int* begin() const;
int* end() const;
};
struct NoDefaultPredicate {
NoDefaultPredicate() = delete;
constexpr bool operator()(int) const;
};
struct NoexceptView : std::ranges::view_base {
NoexceptView() noexcept;
int const* begin() const;
int const* end() const;
};
struct NoexceptPredicate {
NoexceptPredicate() noexcept;
bool operator()(int) const;
};
constexpr bool test() {
{
using View = std::ranges::filter_view<DefaultConstructibleView, DefaultConstructiblePredicate>;
View view;
auto it = view.begin(), end = view.end();
assert(*it++ == 2);
assert(*it++ == 4);
assert(*it++ == 6);
assert(*it++ == 8);
assert(it == end);
}
{
using View = std::ranges::filter_view<DefaultConstructibleView, DefaultConstructiblePredicate>;
View view = {};
auto it = view.begin(), end = view.end();
assert(*it++ == 2);
assert(*it++ == 4);
assert(*it++ == 6);
assert(*it++ == 8);
assert(it == end);
}
// Check cases where the default constructor isn't provided
{
static_assert(!std::is_default_constructible_v<std::ranges::filter_view<NoDefaultView, DefaultConstructiblePredicate>>);
static_assert(!std::is_default_constructible_v<std::ranges::filter_view<DefaultConstructibleView, NoDefaultPredicate>>);
static_assert(!std::is_default_constructible_v<std::ranges::filter_view<NoDefaultView, NoDefaultPredicate>>);
}
// Check noexcept-ness
{
{
using View = std::ranges::filter_view<DefaultConstructibleView, DefaultConstructiblePredicate>;
static_assert(!noexcept(View()));
}
{
using View = std::ranges::filter_view<NoexceptView, NoexceptPredicate>;
static_assert(noexcept(View()));
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,102 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr filter_view(View, Pred);
#include <ranges>
#include <cassert>
#include <utility>
#include "types.h"
struct Range : std::ranges::view_base {
constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
constexpr int* begin() const { return begin_; }
constexpr int* end() const { return end_; }
private:
int* begin_;
int* end_;
};
struct Pred {
constexpr bool operator()(int i) const { return i % 2 != 0; }
};
struct TrackingPred : TrackInitialization {
using TrackInitialization::TrackInitialization;
constexpr bool operator()(int) const;
};
struct TrackingRange : TrackInitialization, std::ranges::view_base {
using TrackInitialization::TrackInitialization;
int* begin() const;
int* end() const;
};
constexpr bool test() {
int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
// Test explicit syntax
{
Range range(buff, buff + 8);
Pred pred;
std::ranges::filter_view<Range, Pred> view(range, pred);
auto it = view.begin(), end = view.end();
assert(*it++ == 1);
assert(*it++ == 3);
assert(*it++ == 5);
assert(*it++ == 7);
assert(it == end);
}
// Test implicit syntax
{
Range range(buff, buff + 8);
Pred pred;
std::ranges::filter_view<Range, Pred> view = {range, pred};
auto it = view.begin(), end = view.end();
assert(*it++ == 1);
assert(*it++ == 3);
assert(*it++ == 5);
assert(*it++ == 7);
assert(it == end);
}
// Make sure we move the view
{
bool moved = false, copied = false;
TrackingRange range(&moved, &copied);
Pred pred;
[[maybe_unused]] std::ranges::filter_view<TrackingRange, Pred> view(std::move(range), pred);
assert(moved);
assert(!copied);
}
// Make sure we move the predicate
{
bool moved = false, copied = false;
Range range(buff, buff + 8);
TrackingPred pred(&moved, &copied);
[[maybe_unused]] std::ranges::filter_view<Range, TrackingPred> view(range, std::move(pred));
assert(moved);
assert(!copied);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,115 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr auto end();
#include <ranges>
#include <cassert>
#include <concepts>
#include <type_traits>
#include "test_iterators.h"
struct Range : std::ranges::view_base {
using Iterator = forward_iterator<int*>;
using Sentinel = sentinel_wrapper<Iterator>;
constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
constexpr Iterator begin() const { return Iterator(begin_); }
constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
private:
int* begin_;
int* end_;
};
struct CommonRange : std::ranges::view_base {
using Iterator = forward_iterator<int*>;
constexpr explicit CommonRange(int* b, int* e) : begin_(b), end_(e) { }
constexpr Iterator begin() const { return Iterator(begin_); }
constexpr Iterator end() const { return Iterator(end_); }
private:
int* begin_;
int* end_;
};
constexpr bool test() {
int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
// Check the return type of `.end()`
{
Range range(buff, buff + 1);
auto pred = [](int) { return true; };
std::ranges::filter_view view(range, pred);
using FilterSentinel = std::ranges::sentinel_t<decltype(view)>;
ASSERT_SAME_TYPE(FilterSentinel, decltype(view.end()));
}
// end() on an empty range
{
Range range(buff, buff);
auto pred = [](int) { return true; };
std::ranges::filter_view view(range, pred);
auto end = view.end();
assert(base(base(end.base())) == buff);
}
// end() on a 1-element range
{
Range range(buff, buff + 1);
auto pred = [](int) { return true; };
std::ranges::filter_view view(range, pred);
auto end = view.end();
assert(base(base(end.base())) == buff + 1);
static_assert(!std::is_same_v<decltype(end), decltype(view.begin())>);
}
// end() on a 2-element range
{
Range range(buff, buff + 2);
auto pred = [](int) { return true; };
std::ranges::filter_view view(range, pred);
auto end = view.end();
assert(base(base(end.base())) == buff + 2);
static_assert(!std::is_same_v<decltype(end), decltype(view.begin())>);
}
// end() on a N-element range
{
for (int k = 1; k != 8; ++k) {
Range range(buff, buff + 8);
auto pred = [=](int i) { return i == k; };
std::ranges::filter_view view(range, pred);
auto end = view.end();
assert(base(base(end.base())) == buff + 8);
static_assert(!std::is_same_v<decltype(end), decltype(view.begin())>);
}
}
// end() on a common_range
{
CommonRange range(buff, buff + 8);
auto pred = [](int i) { return i % 2 == 0; };
std::ranges::filter_view view(range, pred);
auto end = view.end();
assert(base(end.base()) == buff + 8);
static_assert(std::is_same_v<decltype(end), decltype(view.begin())>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,137 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator_t<V> operator->() const
// requires has-arrow<iterator_t<V>> && copyable<iterator_t<V>>
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <cstddef>
#include <utility>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"
struct Point {
int x;
int y;
};
template <class T>
concept has_arrow = requires (T t) {
{ t->x };
};
static_assert(has_arrow<Point*>); // test the test
struct WithArrowOperator {
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = Point;
constexpr explicit WithArrowOperator(Point* p) : p_(p) { }
constexpr Point& operator*() const { return *p_; }
constexpr Point* operator->() const { return p_; } // has arrow
constexpr WithArrowOperator& operator++() { ++p_; return *this; }
constexpr WithArrowOperator operator++(int) { return WithArrowOperator(p_++); }
friend constexpr Point* base(WithArrowOperator const& i) { return i.p_; }
Point* p_;
};
static_assert(std::input_iterator<WithArrowOperator>);
struct WithNonCopyableIterator : std::ranges::view_base {
struct iterator {
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = Point;
iterator(iterator const&) = delete; // not copyable
iterator(iterator&&);
iterator& operator=(iterator&&);
Point& operator*() const;
iterator operator->() const;
iterator& operator++();
iterator operator++(int);
// We need this to use Point* as a sentinel type below. sentinel_wrapper
// can't be used because this iterator is not copyable.
friend bool operator==(iterator const&, Point*);
};
iterator begin() const;
Point* end() const;
};
static_assert(std::ranges::input_range<WithNonCopyableIterator>);
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
std::array<Point, 5> array{{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}};
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
for (std::ptrdiff_t n = 0; n != 5; ++n) {
FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
FilterIterator const iter(view, Iterator(array.begin() + n));
std::same_as<Iterator> decltype(auto) result = iter.operator->();
assert(base(result) == array.begin() + n);
assert(iter->x == n);
assert(iter->y == n);
}
}
constexpr bool tests() {
test<WithArrowOperator>();
test<Point*>();
test<Point const*>();
test<contiguous_iterator<Point*>>();
test<contiguous_iterator<Point const*>>();
// Make sure filter_view::iterator doesn't have operator-> if the
// underlying iterator doesn't have one.
{
auto check_no_arrow = []<class It> {
using View = minimal_view<It, sentinel_wrapper<It>>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
static_assert(!has_arrow<FilterIterator>);
};
check_no_arrow.operator()<cpp17_input_iterator<Point*>>();
check_no_arrow.operator()<cpp20_input_iterator<Point*>>();
check_no_arrow.operator()<forward_iterator<Point*>>();
check_no_arrow.operator()<bidirectional_iterator<Point*>>();
check_no_arrow.operator()<random_access_iterator<Point*>>();
check_no_arrow.operator()<int*>();
}
// Make sure filter_view::iterator doesn't have operator-> if the
// underlying iterator is not copyable.
{
using FilterView = std::ranges::filter_view<WithNonCopyableIterator, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
static_assert(!has_arrow<FilterIterator>);
}
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,73 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator_t<V> const& base() const& noexcept;
// constexpr iterator_t<V> base() &&;
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <utility>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
std::array<int, 5> array{0, 1, 2, 3, 4};
FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
// Test the const& version
{
FilterIterator const iter(view, Iterator(array.begin()));
Iterator const& result = iter.base();
ASSERT_SAME_TYPE(Iterator const&, decltype(iter.base()));
ASSERT_NOEXCEPT(iter.base());
assert(base(result) == array.begin());
}
// Test the && version
{
FilterIterator iter(view, Iterator(array.begin()));
Iterator result = std::move(iter).base();
ASSERT_SAME_TYPE(Iterator, decltype(std::move(iter).base()));
assert(base(result) == array.begin());
}
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>>();
test<cpp20_input_iterator<int*>>();
test<forward_iterator<int*>>();
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
test<int const*>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,90 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr bool operator==(iterator const&, iterator const&)
// requires equality_comparable<iterator_t<V>>
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <utility>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"
template <class T>
concept has_equal = requires (T const& x, T const& y) { { x == y }; };
template <class Iterator>
constexpr void test() {
using Sentinel = sentinel_wrapper<Iterator>;
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
{
std::array<int, 5> array{0, 1, 2, 3, 4};
FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
FilterIterator it1 = view.begin();
FilterIterator it2 = view.begin();
std::same_as<bool> decltype(auto) result = (it1 == it2);
assert(result);
++it1;
assert(!(it1 == it2));
}
{
std::array<int, 5> array{0, 1, 2, 3, 4};
FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
assert(!(view.begin() == view.end()));
}
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>>();
test<forward_iterator<int*>>();
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
test<cpp17_input_iterator<int const*>>();
test<forward_iterator<int const*>>();
test<bidirectional_iterator<int const*>>();
test<random_access_iterator<int const*>>();
test<contiguous_iterator<int const*>>();
test<int const*>();
// Make sure `operator==` isn't provided for non comparable iterators
{
using Iterator = cpp20_input_iterator<int*>;
using Sentinel = sentinel_wrapper<Iterator>;
using FilterView = std::ranges::filter_view<minimal_view<Iterator, Sentinel>, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
static_assert(!has_equal<FilterIterator>);
}
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,57 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// std::ranges::filter_view<V>::<iterator>() requires default_initializable<iterator_t<V>> = default;
#include <ranges>
#include <cassert>
#include <type_traits>
#include "test_iterators.h"
#include "../types.h"
template <class Iterator, bool IsNoexcept>
constexpr void test_default_constructible() {
// Make sure the iterator is default constructible when the underlying iterator is.
using View = minimal_view<Iterator, sentinel_wrapper<Iterator>>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
FilterIterator iter1{};
FilterIterator iter2;
assert(iter1 == iter2);
static_assert(noexcept(FilterIterator()) == IsNoexcept);
}
template <class Iterator>
constexpr void test_not_default_constructible() {
// Make sure the iterator is *not* default constructible when the underlying iterator isn't.
using View = minimal_view<Iterator, sentinel_wrapper<Iterator>>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
static_assert(!std::is_default_constructible_v<FilterIterator>);
}
constexpr bool tests() {
test_not_default_constructible<cpp17_input_iterator<int*>>();
test_not_default_constructible<cpp20_input_iterator<int*>>();
test_default_constructible<forward_iterator<int*>, /* noexcept */ false>();
test_default_constructible<bidirectional_iterator<int*>, /* noexcept */ false>();
test_default_constructible<random_access_iterator<int*>, /* noexcept */ false>();
test_default_constructible<contiguous_iterator<int*>, /* noexcept */ false>();
test_default_constructible<int*, /* noexcept */ true>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,52 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
#include <ranges>
#include <array>
#include <cassert>
#include <utility>
#include "test_iterators.h"
#include "../types.h"
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
View view(Iterator(array.begin()), Sentinel(Iterator(array.end())));
Iterator iter = view.begin();
FilterView filter_view(std::move(view), AlwaysTrue{});
FilterIterator filter_iter(filter_view, std::move(iter));
assert(base(filter_iter.base()) == array.begin());
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>>();
test<cpp20_input_iterator<int*>>();
test<forward_iterator<int*>>();
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,143 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator& operator--() requires bidirectional_range<V>;
// constexpr iterator operator--(int) requires bidirectional_range<V>;
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <iterator>
#include <utility>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"
struct EqualTo {
int x;
constexpr bool operator()(int y) const { return x == y; }
};
template <class T>
concept has_pre_decrement = requires (T t) { { --t }; };
template <class T>
concept has_post_decrement = requires (T t) { { t-- }; };
template <class Iterator>
using FilterIteratorFor = std::ranges::iterator_t<
std::ranges::filter_view<minimal_view<Iterator, sentinel_wrapper<Iterator>>, EqualTo>
>;
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, EqualTo>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
// Test with a single satisfied value
{
std::array<int, 5> array{0, 1, 2, 3, 4};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = std::ranges::next(view.begin(), view.end());
assert(base(it.base()) == array.end()); // test the test
FilterIterator& result = --it;
ASSERT_SAME_TYPE(FilterIterator&, decltype(--it));
assert(&result == &it);
assert(base(result.base()) == array.begin() + 1);
}
// Test with more than one satisfied value
{
std::array<int, 6> array{0, 1, 2, 3, 1, 4};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = std::ranges::next(view.begin(), view.end());
assert(base(it.base()) == array.end()); // test the test
FilterIterator& result = --it;
assert(&result == &it);
assert(base(result.base()) == array.begin() + 4);
--it;
assert(base(it.base()) == array.begin() + 1);
}
// Test going forward and then backward on the same iterator
{
std::array<int, 10> array{0, 1, 2, 3, 1, 1, 4, 5, 1, 6};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = view.begin();
++it;
--it; assert(base(it.base()) == array.begin() + 1);
++it; ++it;
--it; assert(base(it.base()) == array.begin() + 4);
++it; ++it;
--it; assert(base(it.base()) == array.begin() + 5);
++it; ++it;
--it; assert(base(it.base()) == array.begin() + 8);
}
// Test post-decrement
{
std::array<int, 6> array{0, 1, 2, 3, 1, 4};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = std::ranges::next(view.begin(), view.end());
assert(base(it.base()) == array.end()); // test the test
FilterIterator result = it--;
ASSERT_SAME_TYPE(FilterIterator, decltype(it--));
assert(base(result.base()) == array.end());
assert(base(it.base()) == array.begin() + 4);
result = it--;
assert(base(result.base()) == array.begin() + 4);
assert(base(it.base()) == array.begin() + 1);
}
}
constexpr bool tests() {
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
test<bidirectional_iterator<int const*>>();
test<random_access_iterator<int const*>>();
test<contiguous_iterator<int const*>>();
test<int const*>();
// Make sure `operator--` isn't provided for non bidirectional ranges
{
static_assert(!has_pre_decrement<FilterIteratorFor<cpp17_input_iterator<int*>>>);
static_assert(!has_pre_decrement<FilterIteratorFor<cpp20_input_iterator<int*>>>);
static_assert(!has_pre_decrement<FilterIteratorFor<forward_iterator<int*>>>);
static_assert(!has_post_decrement<FilterIteratorFor<cpp17_input_iterator<int*>>>);
static_assert(!has_post_decrement<FilterIteratorFor<cpp20_input_iterator<int*>>>);
static_assert(!has_post_decrement<FilterIteratorFor<forward_iterator<int*>>>);
}
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,70 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr range_reference_t<V> operator*() const;
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <cstddef>
#include <utility>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"
template <class Iterator, class ValueType = int, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
std::array array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
for (std::size_t n = 0; n != array.size(); ++n) {
FilterIterator const iter(view, Iterator(array.begin() + n));
ValueType& result = *iter;
ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
assert(&result == array.begin() + n);
}
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>>();
test<cpp20_input_iterator<int*>>();
test<forward_iterator<int*>>();
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
test<cpp17_input_iterator<int const*>, int const>();
test<cpp20_input_iterator<int const*>, int const>();
test<forward_iterator<int const*>, int const>();
test<bidirectional_iterator<int const*>, int const>();
test<random_access_iterator<int const*>, int const>();
test<contiguous_iterator<int const*>, int const>();
test<int const*, int const>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,184 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator& operator++();
// constexpr void operator++(int);
// constexpr iterator operator++(int) requires forward_range<V>;
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <type_traits>
#include <utility>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"
struct EqualTo {
int x;
constexpr bool operator()(int y) const { return x == y; }
};
struct TrackingPred : TrackInitialization {
using TrackInitialization::TrackInitialization;
constexpr bool operator()(int i) const { return i == 1; }
};
template <class Iterator, bool IsForwardRange, bool IsConst>
constexpr void test() {
using Sentinel = sentinel_wrapper<Iterator>;
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, EqualTo>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
// Increment an iterator when it won't find another satisfied value after begin()
{
std::array<int, 5> array{0, 1, 2, 3, 4};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = view.begin();
FilterIterator& result = ++it;
ASSERT_SAME_TYPE(FilterIterator&, decltype(++it));
assert(&result == &it);
assert(base(result.base()) == array.end());
}
// Increment the iterator and it finds another value after begin()
{
std::array<int, 5> array{99, 1, 99, 1, 99};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = view.begin();
++it;
assert(base(it.base()) == array.begin() + 3);
}
// Increment advances all the way to the end of the range
{
std::array<int, 5> array{99, 1, 99, 99, 1};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = view.begin();
++it;
assert(base(it.base()) == array.begin() + 4);
}
// Increment an iterator multiple times
{
std::array<int, 10> array{0, 1, 2, 3, 1, 1, 4, 5, 1, 6};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = view.begin();
assert(base(it.base()) == array.begin() + 1);
++it; assert(base(it.base()) == array.begin() + 4);
++it; assert(base(it.base()) == array.begin() + 5);
++it; assert(base(it.base()) == array.begin() + 8);
++it; assert(base(it.base()) == array.end());
}
// Test with a predicate that takes by non-const reference
if constexpr (!IsConst) {
std::array<int, 4> array{99, 1, 99, 1};
View v{Iterator(array.begin()), Sentinel(Iterator(array.end()))};
auto pred = [](int& x) { return x == 1; };
auto view = std::ranges::filter_view(std::move(v), pred);
auto it = view.begin();
assert(base(it.base()) == array.begin() + 1);
++it;
assert(base(it.base()) == array.begin() + 3);
}
// Make sure we do not make a copy of the predicate when we increment
// (we should be passing it to ranges::find_if using std::ref)
{
bool moved = false, copied = false;
std::array<int, 3> array{1, 1, 1};
View v{Iterator(array.begin()), Sentinel(Iterator(array.end()))};
auto view = std::ranges::filter_view(std::move(v), TrackingPred(&moved, &copied));
moved = false;
copied = false;
auto it = view.begin();
++it;
it++;
assert(!moved);
assert(!copied);
}
// Check post-increment for input ranges
if constexpr (!IsForwardRange) {
std::array<int, 10> array{0, 1, 2, 3, 1, 1, 4, 5, 1, 6};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = view.begin();
assert(base(it.base()) == array.begin() + 1);
it++; assert(base(it.base()) == array.begin() + 4);
it++; assert(base(it.base()) == array.begin() + 5);
it++; assert(base(it.base()) == array.begin() + 8);
it++; assert(base(it.base()) == array.end());
static_assert(std::is_same_v<decltype(it++), void>);
}
// Check post-increment for forward ranges
if constexpr (IsForwardRange) {
std::array<int, 10> array{0, 1, 2, 3, 1, 1, 4, 5, 1, 6};
FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
FilterIterator it = view.begin();
FilterIterator result = it++;
ASSERT_SAME_TYPE(FilterIterator, decltype(it++));
assert(base(result.base()) == array.begin() + 1);
assert(base(it.base()) == array.begin() + 4);
result = it++;
assert(base(result.base()) == array.begin() + 4);
assert(base(it.base()) == array.begin() + 5);
result = it++;
assert(base(result.base()) == array.begin() + 5);
assert(base(it.base()) == array.begin() + 8);
result = it++;
assert(base(result.base()) == array.begin() + 8);
assert(base(it.base()) == array.end());
}
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>, /* IsForwardRange */ false, /* IsConst */ false>();
test<cpp20_input_iterator<int*>, /* IsForwardRange */ false, /* IsConst */ false>();
test<forward_iterator<int*>, /* IsForwardRange */ true, /* IsConst */ false>();
test<bidirectional_iterator<int*>, /* IsForwardRange */ true, /* IsConst */ false>();
test<random_access_iterator<int*>, /* IsForwardRange */ true, /* IsConst */ false>();
test<contiguous_iterator<int*>, /* IsForwardRange */ true, /* IsConst */ false>();
test<int*, /* IsForwardRange */ true, /* IsConst */ false>();
test<cpp17_input_iterator<int const*>, /* IsForwardRange */ false, /* IsConst */ true>();
test<cpp20_input_iterator<int const*>, /* IsForwardRange */ false, /* IsConst */ true>();
test<forward_iterator<int const*>, /* IsForwardRange */ true, /* IsConst */ true>();
test<bidirectional_iterator<int const*>, /* IsForwardRange */ true, /* IsConst */ true>();
test<random_access_iterator<int const*>, /* IsForwardRange */ true, /* IsConst */ true>();
test<contiguous_iterator<int const*>, /* IsForwardRange */ true, /* IsConst */ true>();
test<int const*, /* IsForwardRange */ true, /* IsConst */ true>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,64 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr range_rvalue_reference_t<V> iter_move(iterator const& i)
// noexcept(noexcept(ranges::iter_move(i.current_)));
#include <ranges>
#include <array>
#include <cassert>
#include <utility>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"
template <class Iterator, bool HasNoexceptIterMove>
constexpr void test() {
using Sentinel = sentinel_wrapper<Iterator>;
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
{
std::array<int, 5> array{0, 1, 2, 3, 4};
FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
FilterIterator const it = view.begin();
int&& result = iter_move(it);
static_assert(noexcept(iter_move(it)) == HasNoexceptIterMove);
assert(&result == array.begin());
}
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>, /* noexcept */ false>();
test<cpp20_input_iterator<int*>, /* noexcept */ false>();
test<forward_iterator<int*>, /* noexcept */ false>();
test<bidirectional_iterator<int*>, /* noexcept */ false>();
test<random_access_iterator<int*>, /* noexcept */ false>();
test<contiguous_iterator<int*>, /* noexcept */ false>();
test<int*, /* noexcept */ true>();
test<NoexceptIterMoveInputIterator<true>, /* noexcept */ true>();
test<NoexceptIterMoveInputIterator<false>, /* noexcept */ false>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,91 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr void iter_swap(iterator const& x, iterator const& y)
// noexcept(noexcept(ranges::iter_swap(x.current_, y.current_)))
// requires(indirectly_swappable<iterator_t<V>>);
#include <ranges>
#include <array>
#include <cassert>
#include <iterator>
#include <utility>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"
template <class It>
concept has_iter_swap = requires (It it) {
std::ranges::iter_swap(it, it);
};
struct IsEven {
constexpr bool operator()(int x) const { return x % 2 == 0; }
};
template <class Iterator, bool IsNoexcept>
constexpr void test() {
using Sentinel = sentinel_wrapper<Iterator>;
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, IsEven>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
{
std::array<int, 5> array{1, 2, 1, 4, 1};
FilterView view = make_filter_view(array.begin(), array.end(), IsEven{});
FilterIterator const it1 = view.begin();
FilterIterator const it2 = std::ranges::next(view.begin());
static_assert(std::is_same_v<decltype(iter_swap(it1, it2)), void>);
static_assert(noexcept(iter_swap(it1, it2)) == IsNoexcept);
assert(*it1 == 2 && *it2 == 4); // test the test
iter_swap(it1, it2);
assert(*it1 == 4);
assert(*it2 == 2);
}
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>, /* noexcept */ false>();
test<cpp20_input_iterator<int*>, /* noexcept */ false>();
test<forward_iterator<int*>, /* noexcept */ false>();
test<bidirectional_iterator<int*>, /* noexcept */ false>();
test<random_access_iterator<int*>, /* noexcept */ false>();
test<contiguous_iterator<int*>, /* noexcept */ false>();
test<int*, /* noexcept */ true>();
test<NoexceptIterSwapInputIterator<true>, /* noexcept */ true>();
test<NoexceptIterSwapInputIterator<false>, /* noexcept */ false>();
// Test that iter_swap requires the underlying iterator to be iter_swappable
{
using Iterator = int const*;
using View = minimal_view<Iterator, Iterator>;
using FilterView = std::ranges::filter_view<View, IsEven>;
using FilterIterator = std::ranges::iterator_t<FilterView>;
static_assert(!std::indirectly_swappable<Iterator>);
static_assert(!has_iter_swap<FilterIterator>);
}
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,91 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// std::filter_view::<iterator>::difference_type
// std::filter_view::<iterator>::value_type
// std::filter_view::<iterator>::iterator_category
// std::filter_view::<iterator>::iterator_concept
#include <ranges>
#include <type_traits>
#include "test_iterators.h"
#include "../types.h"
template <typename T>
concept HasIteratorCategory = requires {
typename T::iterator_category;
};
template <class Iterator>
using FilterViewFor = std::ranges::filter_view<
minimal_view<Iterator, sentinel_wrapper<Iterator>>,
AlwaysTrue
>;
template <class Iterator>
using FilterIteratorFor = std::ranges::iterator_t<FilterViewFor<Iterator>>;
struct ForwardIteratorWithInputCategory {
using difference_type = int;
using value_type = int;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
ForwardIteratorWithInputCategory();
ForwardIteratorWithInputCategory& operator++();
ForwardIteratorWithInputCategory operator++(int);
int& operator*() const;
friend bool operator==(ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory);
};
static_assert(std::forward_iterator<ForwardIteratorWithInputCategory>);
void f() {
// Check that value_type is range_value_t and difference_type is range_difference_t
{
auto test = []<class Iterator> {
using FilterView = FilterViewFor<Iterator>;
using FilterIterator = FilterIteratorFor<Iterator>;
static_assert(std::is_same_v<typename FilterIterator::value_type, std::ranges::range_value_t<FilterView>>);
static_assert(std::is_same_v<typename FilterIterator::difference_type, std::ranges::range_difference_t<FilterView>>);
};
test.operator()<cpp17_input_iterator<int*>>();
test.operator()<cpp20_input_iterator<int*>>();
test.operator()<forward_iterator<int*>>();
test.operator()<bidirectional_iterator<int*>>();
test.operator()<random_access_iterator<int*>>();
test.operator()<contiguous_iterator<int*>>();
test.operator()<int*>();
}
// Check iterator_concept for various categories of ranges
{
static_assert(std::is_same_v<FilterIteratorFor<cpp17_input_iterator<int*>>::iterator_concept, std::input_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<cpp20_input_iterator<int*>>::iterator_concept, std::input_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<ForwardIteratorWithInputCategory>::iterator_concept, std::forward_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<forward_iterator<int*>>::iterator_concept, std::forward_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<bidirectional_iterator<int*>>::iterator_concept, std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<random_access_iterator<int*>>::iterator_concept, std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<contiguous_iterator<int*>>::iterator_concept, std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<int*>::iterator_concept, std::bidirectional_iterator_tag>);
}
// Check iterator_category for various categories of ranges
{
static_assert(!HasIteratorCategory<FilterIteratorFor<cpp17_input_iterator<int*>>>);
static_assert(!HasIteratorCategory<FilterIteratorFor<cpp20_input_iterator<int*>>>);
static_assert(std::is_same_v<FilterIteratorFor<ForwardIteratorWithInputCategory>::iterator_category, std::input_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<forward_iterator<int*>>::iterator_category, std::forward_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<bidirectional_iterator<int*>>::iterator_category, std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<random_access_iterator<int*>>::iterator_category, std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<contiguous_iterator<int*>>::iterator_category, std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<FilterIteratorFor<int*>::iterator_category, std::bidirectional_iterator_tag>);
}
}

View File

@ -0,0 +1,65 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// Older Clangs don't properly deduce decltype(auto) with a concept constraint
// XFAIL: clang-11, clang-12
// XFAIL: apple-clang-12, apple-clang-13.0
// constexpr Pred const& pred() const;
#include <ranges>
#include <cassert>
#include <concepts>
struct Range : std::ranges::view_base {
int* begin() const;
int* end() const;
};
struct Pred {
bool operator()(int) const;
int value;
};
constexpr bool test() {
{
Pred pred{42};
std::ranges::filter_view<Range, Pred> const view(Range{}, pred);
std::same_as<Pred const&> decltype(auto) result = view.pred();
assert(result.value == 42);
// Make sure we're really holding a reference to something inside the view
std::same_as<Pred const&> decltype(auto) result2 = view.pred();
assert(&result == &result2);
}
// Same, but calling on a non-const view
{
Pred pred{42};
std::ranges::filter_view<Range, Pred> view(Range{}, pred);
std::same_as<Pred const&> decltype(auto) result = view.pred();
assert(result.value == 42);
// Make sure we're really holding a reference to something inside the view
std::same_as<Pred const&> decltype(auto) result2 = view.pred();
assert(&result == &result2);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,57 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr sentinel_t<V> base() const;
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <utility>
#include "test_iterators.h"
#include "../types.h"
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterSentinel = std::ranges::sentinel_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
std::array<int, 5> array{0, 1, 2, 3, 4};
FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
FilterSentinel const sent = view.end();
std::same_as<Sentinel> decltype(auto) result = sent.base();
assert(base(base(result)) == array.end());
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>>();
test<cpp20_input_iterator<int*>>();
test<forward_iterator<int*>>();
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,62 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr bool operator==(iterator const&, sentinel const&);
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <utility>
#include "test_iterators.h"
#include "../types.h"
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using View = minimal_view<Iterator, Sentinel>;
std::array<int, 5> array{0, 1, 2, 3, 4};
{
View v(Iterator(array.begin()), Sentinel(Iterator(array.end())));
std::ranges::filter_view view(std::move(v), AlwaysTrue{});
auto const it = view.begin();
auto const sent = view.end();
std::same_as<bool> decltype(auto) result = (it == sent);
assert(!result);
}
{
View v(Iterator(array.begin()), Sentinel(Iterator(array.end())));
std::ranges::filter_view view(std::move(v), [](auto) { return false; });
auto const it = view.begin();
auto const sent = view.end();
std::same_as<bool> decltype(auto) result = (it == sent);
assert(result);
}
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>>();
test<cpp20_input_iterator<int*>>();
test<forward_iterator<int*>>();
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,46 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// filter_view<V>::<sentinel>() = default;
#include <ranges>
#include <cassert>
#include "test_iterators.h"
#include "../types.h"
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterSentinel = std::ranges::sentinel_t<FilterView>;
FilterSentinel sent1{};
FilterSentinel sent2;
assert(base(base(sent1.base())) == base(base(sent2.base())));
static_assert(noexcept(FilterSentinel()));
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>>();
test<cpp20_input_iterator<int*>>();
test<forward_iterator<int*>>();
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr explicit sentinel(filter_view&);
#include <ranges>
#include <array>
#include <cassert>
#include <type_traits>
#include <utility>
#include "test_iterators.h"
#include "../types.h"
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using View = minimal_view<Iterator, Sentinel>;
using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
using FilterSentinel = std::ranges::sentinel_t<FilterView>;
auto make_filter_view = [](auto begin, auto end, auto pred) {
View view{Iterator(begin), Sentinel(Iterator(end))};
return FilterView(std::move(view), pred);
};
std::array<int, 5> array{0, 1, 2, 3, 4};
FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
FilterSentinel sent(view);
assert(base(base(sent.base())) == base(base(view.end().base())));
static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
!std::is_convertible_v<FilterView&, FilterSentinel>);
}
constexpr bool tests() {
test<cpp17_input_iterator<int*>>();
test<cpp20_input_iterator<int*>>();
test<forward_iterator<int*>>();
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -0,0 +1,104 @@
//===----------------------------------------------------------------------===//
//
// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_FILTER_TYPES_H
#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_FILTER_TYPES_H
#include <ranges>
#include <utility>
struct TrackInitialization {
constexpr explicit TrackInitialization(bool* moved, bool* copied) : moved_(moved), copied_(copied) { }
constexpr TrackInitialization(TrackInitialization const& other) : moved_(other.moved_), copied_(other.copied_) {
*copied_ = true;
}
constexpr TrackInitialization(TrackInitialization&& other) : moved_(other.moved_), copied_(other.copied_) {
*moved_ = true;
}
TrackInitialization& operator=(TrackInitialization const&) = default;
TrackInitialization& operator=(TrackInitialization&&) = default;
bool* moved_;
bool* copied_;
};
struct AlwaysTrue {
template <typename T>
constexpr bool operator()(T const&) const { return true; }
};
template <class Iterator, class Sentinel>
struct minimal_view : std::ranges::view_base {
constexpr explicit minimal_view(Iterator it, Sentinel sent)
: it_(base(std::move(it)))
, sent_(base(std::move(sent)))
{ }
minimal_view(minimal_view&&) = default;
minimal_view& operator=(minimal_view&&) = default;
constexpr Iterator begin() const { return Iterator(it_); }
constexpr Sentinel end() const { return Sentinel(sent_); }
private:
decltype(base(std::declval<Iterator>())) it_;
decltype(base(std::declval<Sentinel>())) sent_;
};
template <bool IsNoexcept>
class NoexceptIterMoveInputIterator {
int *it_;
public:
using iterator_category = std::input_iterator_tag;
using value_type = int;
using difference_type = typename std::iterator_traits<int *>::difference_type;
using pointer = int*;
using reference = int&;
NoexceptIterMoveInputIterator() = default;
explicit constexpr NoexceptIterMoveInputIterator(int *it) : it_(it) {}
friend constexpr decltype(auto) iter_move(const NoexceptIterMoveInputIterator& it) noexcept(IsNoexcept) {
return std::ranges::iter_move(it.it_);
}
friend constexpr int* base(const NoexceptIterMoveInputIterator& i) { return i.it_; }
constexpr reference operator*() const { return *it_; }
constexpr NoexceptIterMoveInputIterator& operator++() {++it_; return *this;}
constexpr NoexceptIterMoveInputIterator operator++(int)
{ NoexceptIterMoveInputIterator tmp(*this); ++(*this); return tmp; }
};
template <bool IsNoexcept>
class NoexceptIterSwapInputIterator {
int *it_;
public:
using iterator_category = std::input_iterator_tag;
using value_type = int;
using difference_type = typename std::iterator_traits<int *>::difference_type;
using pointer = int*;
using reference = int&;
NoexceptIterSwapInputIterator() = default;
explicit constexpr NoexceptIterSwapInputIterator(int *it) : it_(it) {}
friend constexpr void iter_swap(const NoexceptIterSwapInputIterator& a, const NoexceptIterSwapInputIterator& b) noexcept(IsNoexcept) {
return std::ranges::iter_swap(a.it_, b.it_);
}
friend constexpr int* base(const NoexceptIterSwapInputIterator& i) { return i.it_; }
constexpr reference operator*() const { return *it_; }
constexpr NoexceptIterSwapInputIterator& operator++() {++it_; return *this;}
constexpr NoexceptIterSwapInputIterator operator++(int)
{ NoexceptIterSwapInputIterator tmp(*this); ++(*this); return tmp; }
};
#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_FILTER_TYPES_H