From f8b91a26b6ce52b5e5029ea59c41ebf2a3ca72c3 Mon Sep 17 00:00:00 2001 From: Hui Date: Wed, 29 May 2024 17:45:15 +0100 Subject: [PATCH] initial implementation --- any_view.md | 23 +- impl/any_view/any_view.hpp | 696 +++++++++++++++++- impl/any_view/test/example.cpp | 0 impl/any_view/test/iterator/bidirectional.cpp | 145 ++++ impl/any_view/test/iterator/forward.cpp | 135 ++++ impl/any_view/test/iterator/input.cpp | 83 +++ impl/any_view/test/iterator/input_common.cpp | 117 +++ impl/any_view/test/iterator/random_access.cpp | 225 ++++++ impl/any_view/test/view/bidirectional.cpp | 146 ++++ impl/any_view/test/view/contiguous.cpp | 166 +++++ impl/any_view/test/view/forward.cpp | 132 ++++ impl/any_view/test/view/input.cpp | 117 +++ impl/any_view/test/view/random_access.cpp | 165 +++++ 13 files changed, 2144 insertions(+), 6 deletions(-) delete mode 100644 impl/any_view/test/example.cpp create mode 100644 impl/any_view/test/iterator/bidirectional.cpp create mode 100644 impl/any_view/test/iterator/forward.cpp create mode 100644 impl/any_view/test/iterator/input.cpp create mode 100644 impl/any_view/test/iterator/input_common.cpp create mode 100644 impl/any_view/test/iterator/random_access.cpp create mode 100644 impl/any_view/test/view/bidirectional.cpp create mode 100644 impl/any_view/test/view/contiguous.cpp create mode 100644 impl/any_view/test/view/forward.cpp create mode 100644 impl/any_view/test/view/input.cpp create mode 100644 impl/any_view/test/view/random_access.cpp diff --git a/any_view.md b/any_view.md index 129a18d..2e78fa0 100644 --- a/any_view.md +++ b/any_view.md @@ -106,7 +106,6 @@ For `any_view`, it is much much more complex than that: - Is it an `input_range`, `forward_range`, `bidirectional_range`, `random_access_range`, or a `contiguous_range` ? - Is the range `copyable` ? -- Is the iterator `copyable` ? - Is it a `sized_range` ? - Is it a `borrowed_range` ? - Is it a `common_range` ? @@ -114,6 +113,10 @@ For `any_view`, it is much much more complex than that: - What is the `range_value_t` ? - What is the `range_rvalue_reference_t` ? - What is the `range_difference_t` ? +- Is the `range` const-iterable? +- Is the iterator `copyable` for `input_iterator`? +- Is the iterator equality comparable for `input_iterator`? +- sized_sentinel_for ? We can easily get combinatorial explosion of types if we follow the same approach of `std::function`. So let's look at the prior arts. @@ -169,13 +172,12 @@ enum class category forward = 3, bidirectional = 7, random_access = 15, - contiguous = 31 + contiguous = 31, mask = contiguous, sized = 32, borrowed = 64, common = 128, - move_only_iterator = 256, - move_only_view = 512 + move_only_view = 256 }; template , class RValueRef = add_rvalue_reference_t, class Diff = ptrdiff_t> -struct any_view; +class any_view; ``` +#### Considerations + +- `contiguous_range` is still useful to support even though we have already `std::span`. But `span` is non-owning. + +- move-only iterator is not a useful thing to be configured. We can always make the `input_iterator` move-only, as algorithms that deal with input ranges must already deal with the non-copyability. + +- move-only view is still useful. (see `std::function` vs `std::move_only_function`) + +- const-iteratable will make the design super complicated as all the types can be different between const and non-const. # Implementation Experience +- Reference implementation in the repo + # Wording ## Feature Test Macro diff --git a/impl/any_view/any_view.hpp b/impl/any_view/any_view.hpp index 25ee6a0..42d9484 100644 --- a/impl/any_view/any_view.hpp +++ b/impl/any_view/any_view.hpp @@ -1,6 +1,700 @@ #ifndef LIBCPP__RANGE_ANY_VIEW_HPP #define LIBCPP__RANGE_ANY_VIEW_HPP -namespace std::ranges {} +#include +#include +#include +#include +namespace std::ranges { + +inline namespace __any_view { + +enum class category { + none = 0, + input = 1, + forward = 3, + bidirectional = 7, + random_access = 15, + contiguous = 31, + category_mask = contiguous, + sized = 32, + borrowed = 64, + common = 128, + move_only_view = 256 +}; + +constexpr category operator&(category lhs, category rhs) noexcept { + return static_cast( + static_cast>(lhs) & + static_cast>(rhs)); +} + +constexpr category operator|(category lhs, category rhs) noexcept { + return static_cast( + static_cast>(lhs) | + static_cast>(rhs)); +} + +constexpr auto operator<=>(category lhs, category rhs) noexcept { + return static_cast>(lhs) <=> + static_cast>(rhs); +} + +} // namespace __any_view + +template , + class RValueRef = add_rvalue_reference_t, + class Diff = ptrdiff_t> +class any_view { + public: + // "requires" is not allowed to use with virtual functions. + // so it looks complicated + template + struct iterator_interface; + + struct basic_input_iterator_interface { + constexpr virtual ~basic_input_iterator_interface() = default; + + constexpr virtual std::unique_ptr + move() = 0; + + constexpr virtual Ref deref() const = 0; + constexpr virtual void increment() = 0; + + constexpr virtual RValueRef iter_move() const = 0; + }; + + struct common_input_iterator : basic_input_iterator_interface { + constexpr virtual std::unique_ptr clone() const = 0; + constexpr virtual bool equal(const common_input_iterator &) const = 0; + }; + + template <> + struct iterator_interface + : std::conditional_t<(Cat & category::common) != category::none, + common_input_iterator, + basic_input_iterator_interface> {}; + + template <> + struct iterator_interface + : basic_input_iterator_interface { + iterator_interface() = default; + constexpr virtual std::unique_ptr clone() const = 0; + + constexpr virtual bool equal(const iterator_interface &) const = 0; + }; + + template <> + struct iterator_interface + : iterator_interface { + constexpr virtual void decrement() = 0; + }; + + template <> + struct iterator_interface + : iterator_interface { + constexpr virtual void advance(Diff) = 0; + constexpr virtual Diff distance_to(const iterator_interface &) const = 0; + }; + + using any_iter_interface = iterator_interface; + template + struct iterator_impl : any_iter_interface { + constexpr explicit iterator_impl(Iter tt) : it(std::move(tt)) {} + + // input + constexpr std::unique_ptr move() { + return std::make_unique(std::move(it)); + } + + constexpr Ref deref() const { return *it; }; + constexpr void increment() { ++it; }; + + constexpr RValueRef iter_move() const { + return std::ranges::iter_move(it); + }; + + // note: the following functions should ideally be constrained + // but requires is not allowed in virtual functions + + // forward + + iterator_impl() = default; + + using common_interface = std::conditional_t< + std::is_base_of_v, + common_input_iterator, iterator_interface>; + + constexpr virtual std::unique_ptr clone() const { + if constexpr ((Cat & category::category_mask) >= category::forward || + (Cat & category::common) != category::none) { + return std::make_unique(it); + } else { + assert(false); + return nullptr; + } + } + + constexpr bool equal(const common_interface &other) const { + if constexpr ((Cat & category::category_mask) >= category::forward || + (Cat & category::common) != category::none) { + return it == static_cast(other).it; + } else { + assert(false); + return false; + } + } + + // bidi + + constexpr void decrement() { + if constexpr ((Cat & category::category_mask) >= + category::bidirectional) { + --it; + } else { + assert(false); + } + } + + // random access + + constexpr void advance(Diff diff) { + if constexpr ((Cat & category::category_mask) >= + category::random_access) { + it += diff; + } else { + assert(false); + } + } + + constexpr Diff distance_to(const any_iter_interface &other) const { + if constexpr ((Cat & category::category_mask) >= + category::random_access) { + return Diff(it - static_cast(other).it); + } else { + assert(false); + return Diff{}; + } + } + + Iter it; + }; + + struct singular_impl : any_iter_interface { + singular_impl() = default; + + constexpr std::unique_ptr move() { + return std::make_unique(); + } + + constexpr Ref deref() const { assert(false); }; + constexpr void increment() { assert(false); }; + + constexpr RValueRef iter_move() const { assert(false); }; + + using common_interface = std::conditional_t< + std::is_base_of_v, + common_input_iterator, iterator_interface>; + + constexpr virtual std::unique_ptr clone() const { + return std::make_unique(); + } + + constexpr bool equal(const common_interface &other) const { + return dynamic_cast(&other) != nullptr; + } + + // bidi + constexpr void decrement() { assert(false); } + + // random access + + constexpr void advance(Diff) { assert(false); } + constexpr Diff distance_to(const any_iter_interface &) const { + assert(false); + return Diff{}; + } + }; + + struct empty_iterator_category {}; + struct with_iterator_category { + private: + constexpr static auto get_category() { + constexpr auto cat_mask = Cat & category::category_mask; + if constexpr (!std::is_reference_v) { + return std::input_iterator_tag{}; + } else if constexpr (cat_mask >= category::random_access) { + return std::random_access_iterator_tag{}; + } else if constexpr (cat_mask == category::bidirectional) { + return std::bidirectional_iterator_tag{}; + } else if constexpr (cat_mask == category::forward) { + return std::forward_iterator_tag{}; + } else { + return std::input_iterator_tag{}; + } + } + + public: + using iterator_category = decltype(get_category()); + }; + constexpr static auto get_concept() { + constexpr auto cat_mask = Cat & category::category_mask; + if constexpr (cat_mask >= category::random_access) { + return std::random_access_iterator_tag{}; + } else if constexpr (cat_mask == category::bidirectional) { + return std::bidirectional_iterator_tag{}; + } else if constexpr (cat_mask == category::forward) { + return std::forward_iterator_tag{}; + } else { + return std::input_iterator_tag{}; + } + } + struct any_iterator + : std::conditional_t<(Cat & category::category_mask) >= category::forward, + with_iterator_category, empty_iterator_category> { + using iterator_concept = decltype(get_concept()); + using value_type = Value; + using difference_type = Diff; + + constexpr any_iterator() : impl_(std::make_unique()) {} + + template + consteval static bool category_constraint() { + constexpr auto cat_mask = Cat & category::category_mask; + if constexpr (cat_mask == category::contiguous) { + return std::contiguous_iterator; + } else if constexpr (cat_mask == category::random_access) { + return std::random_access_iterator; + } else if constexpr (cat_mask == category::bidirectional) { + return std::bidirectional_iterator; + } else if constexpr (cat_mask == category::forward) { + return std::forward_iterator; + } else { + return std::input_iterator; + } + } + + template + requires(!std::same_as && category_constraint()) + constexpr any_iterator(Iter iter) + : impl_(std::make_unique>(std::move(iter))) {} + + // TODO: do we allow copy for input iterator and how? + constexpr any_iterator(const any_iterator &other) + requires((Cat & category::category_mask) >= category::forward || + (Cat & category::common) != category::none) + { + impl_ = cast_unique_ptr(other.impl_->clone()); + } + + constexpr any_iterator(any_iterator &&other) { + impl_ = cast_unique_ptr(other.impl_->move()); + } + + constexpr any_iterator &operator=(const any_iterator &other) + requires((Cat & category::category_mask) >= category::forward || + (Cat & category::common) != category::none) + { + if (this != &other) { + impl_ = cast_unique_ptr(other.impl_->clone()); + } + return *this; + } + + constexpr any_iterator &operator=(any_iterator &&other) { + if (this != &other) { + impl_ = cast_unique_ptr(other.impl_->move()); + } + return *this; + } + + constexpr Ref operator*() const { return impl_->deref(); } + + constexpr any_iterator &operator++() { + impl_->increment(); + return *this; + } + + constexpr void operator++(int) { ++(*this); } + + constexpr any_iterator operator++(int) + requires((Cat & category::category_mask) >= category::forward) + { + auto tmp = *this; + ++(*this); + return tmp; + } + + constexpr any_iterator &operator--() + requires((Cat & category::category_mask) >= category::bidirectional) + { + impl_->decrement(); + return *this; + } + + constexpr any_iterator operator--(int) + requires((Cat & category::category_mask) >= category::bidirectional) + { + auto tmp = *this; + --(*this); + return tmp; + } + + constexpr any_iterator &operator+=(difference_type n) + requires((Cat & category::category_mask) >= category::random_access) + { + impl_->advance(n); + return *this; + } + + constexpr any_iterator &operator-=(difference_type n) + requires((Cat & category::category_mask) >= category::random_access) + { + *this += -n; + return *this; + } + + constexpr Ref operator[](difference_type n) const + requires((Cat & category::category_mask) >= category::random_access) + { + return *((*this) + n); + } + + friend constexpr bool operator<(const any_iterator &x, + const any_iterator &y) + requires((Cat & category::category_mask) >= category::random_access) + { + return (x - y) < 0; + } + + friend constexpr bool operator>(const any_iterator &x, + const any_iterator &y) + requires((Cat & category::category_mask) >= category::random_access) + { + return (x - y) > 0; + } + + friend constexpr bool operator<=(const any_iterator &x, + const any_iterator &y) + requires((Cat & category::category_mask) >= category::random_access) + { + return (x - y) <= 0; + } + + friend constexpr bool operator>=(const any_iterator &x, + const any_iterator &y) + requires((Cat & category::category_mask) >= category::random_access) + { + return (x - y) >= 0; + } + + friend constexpr any_iterator operator+(const any_iterator &it, + difference_type n) + requires((Cat & category::category_mask) >= category::random_access) + { + auto temp = it; + temp += n; + return temp; + } + + friend constexpr any_iterator operator+(difference_type n, + const any_iterator &it) + requires((Cat & category::category_mask) >= category::random_access) + { + return it + n; + } + + friend constexpr any_iterator operator-(const any_iterator &it, + difference_type n) + requires((Cat & category::category_mask) >= category::random_access) + { + auto temp = it; + temp -= n; + return temp; + } + + friend constexpr difference_type operator-(const any_iterator &x, + const any_iterator &y) + requires((Cat & category::category_mask) >= category::random_access) + { + return x.impl_->distance_to(*(y.impl_)); + } + + friend constexpr bool operator==(const any_iterator &x, + const any_iterator &y) + requires((Cat & category::category_mask) >= category::forward || + (Cat & category::common) != category::none) + { + return x.impl_->equal(*(y.impl_)); + } + + friend constexpr RValueRef iter_move(const any_iterator &iter) { + return iter.impl_->iter_move(); + } + + constexpr any_iter_interface const &get_impl() const { return *impl_; } + + private: + std::unique_ptr impl_; + + constexpr any_iterator(std::unique_ptr &&impl) + : impl_(std::move(impl)) {} + + template + constexpr static std::unique_ptr cast_unique_ptr( + std::unique_ptr &&p) { + return std::unique_ptr( + static_cast(p.release())); + } + }; + + using iterator = std::conditional_t<(Cat & category::category_mask) == + category::contiguous, + std::add_pointer_t, any_iterator>; + struct any_sentinel_interface { + constexpr virtual std::unique_ptr move() = 0; + + constexpr virtual std::unique_ptr clone() const = 0; + + constexpr virtual ~any_sentinel_interface() = default; + + constexpr virtual bool equal(const iterator &) const = 0; + }; + + template + struct sentinel_impl : any_sentinel_interface { + constexpr explicit sentinel_impl(Sent st) : st_(std::move(st)) {} + constexpr std::unique_ptr move() override { + return std::make_unique(std::move(st_)); + } + + constexpr std::unique_ptr clone() const override { + return std::make_unique(st_); + } + + constexpr bool equal(const iterator &iter) const override { + if constexpr ((Cat & category::category_mask) == category::contiguous) { + return st_ == iter; + } else { + if (auto iter_impl = + dynamic_cast const *>(&iter.get_impl())) { + return iter_impl->it == st_; + } + } + return false; + } + + private: + Sent st_; + }; + + struct any_sentinel { + constexpr any_sentinel() = default; + + template + struct tag {}; + + template + constexpr any_sentinel(Sent sent, tag) + : impl_(std::make_unique>(std::move(sent))) {} + + constexpr any_sentinel(const any_sentinel &other) { + if (other.impl_) { + impl_ = other.impl_->clone(); + } + } + + constexpr any_sentinel(any_sentinel &&other) { + if (other.impl_) { + impl_ = other.impl_->move(); + } + } + + constexpr any_sentinel &operator=(const any_sentinel &other) { + if (this != &other) { + impl_ = other.impl_ ? other.impl_->clone() : other.impl_; + } + return *this; + } + + constexpr any_sentinel &operator=(any_sentinel &&other) { + if (this != &other) { + impl_ = other.impl_ ? other.impl_->move() : other.impl_; + } + return *this; + } + + friend constexpr bool operator==(const iterator &iter, + const any_sentinel &sent) { + if (!sent.impl_) return false; + return sent.impl_->equal(iter); + } + + private: + std::unique_ptr impl_ = nullptr; + + constexpr any_sentinel(std::unique_ptr &&impl) + : impl_(std::move(impl)) {} + }; + + using sentinel = + std::conditional_t<(Cat & category::common) == category::none, + any_sentinel, iterator>; + + struct sized_interface { + constexpr virtual std::size_t size() const = 0; + }; + + template + struct clonable_interface { + constexpr virtual std::unique_ptr clone() const = 0; + }; + + template + struct basic_any_view_interface { + constexpr virtual std::unique_ptr move() = 0; + + constexpr virtual iterator begin() = 0; + constexpr virtual sentinel end() = 0; + + constexpr virtual ~basic_any_view_interface() = default; + }; + + struct any_view_interface : basic_any_view_interface, + sized_interface, + clonable_interface {}; + + template + struct any_view_impl : any_view_interface { + constexpr explicit any_view_impl(View view) : view_(std::move(view)) {} + + constexpr std::unique_ptr move() override { + return std::make_unique(std::move(view_)); + } + + constexpr std::unique_ptr clone() const override { + if constexpr ((Cat & category::move_only_view) == category::none) { + return std::make_unique(view_); + } else { + assert(false); + return nullptr; + } + } + + constexpr iterator begin() override { + if constexpr ((Cat & category::category_mask) == category::contiguous) { + return std::ranges::begin(view_); + } else { + return any_iterator(std::ranges::begin(view_)); + } + } + + constexpr sentinel end() override { + if constexpr ((Cat & category::category_mask) == category::contiguous && + (Cat & category::common) != category::none) { + return std::ranges::end(view_); + } else if constexpr ((Cat & category::common) != category::none) { + return any_iterator(std::ranges::end(view_)); + } else { + using tag_t = any_sentinel::template tag>; + return any_sentinel(std::ranges::end(view_), tag_t{}); + } + } + + constexpr std::size_t size() const override { + if constexpr ((Cat & category::sized) != category::none) { + return std::ranges::size(view_); + } else { + assert(false); + return 0; + } + } + + private: + View view_; + }; + + template + consteval static bool view_category_constraint() { + if constexpr ((Cat & category::sized) != category::none && + !std::ranges::sized_range) { + return false; + } + + if constexpr ((Cat & category::common) != category::none && + !std::ranges::common_range) { + return false; + } + + if constexpr ((Cat & category::borrowed) != category::none && + !std::ranges::borrowed_range) { + return false; + } + + if constexpr ((Cat & category::move_only_view) == category::none && + !std::copyable) { + return false; + } + constexpr auto cat_mask = Cat & category::category_mask; + if constexpr (cat_mask == category::contiguous) { + return std::ranges::contiguous_range; + } else if constexpr (cat_mask == category::random_access) { + return std::ranges::random_access_range; + } else if constexpr (cat_mask == category::bidirectional) { + return std::ranges::bidirectional_range; + } else if constexpr (cat_mask == category::forward) { + return std::ranges::forward_range; + } else { + return std::ranges::input_range; + } + } + + template + requires(!std::same_as && std::ranges::view && + view_category_constraint()) + constexpr any_view(View view) + : impl_(std::make_unique>(std::move(view))) {} + + constexpr any_view(const any_view &other) + requires((Cat & category::move_only_view) == category::none) + : impl_(other.impl_->clone()) {} + + constexpr any_view(any_view &&other) : impl_(other.impl_->move()) {} + + constexpr any_view &operator=(const any_view &other) + requires((Cat & category::move_only_view) == category::none) + { + if (this != &other) { + impl_ = other.impl_->clone(); + } + return *this; + } + + constexpr any_view &operator=(any_view &&other) { + if (this != &other) { + impl_ = other.impl_->move(); + } + return *this; + } + + constexpr iterator begin() { return impl_->begin(); } + constexpr sentinel end() { return impl_->end(); } + + constexpr std::size_t size() const + requires((Cat & category::sized) != category::none) + { + return impl_->size(); + } + + private: + std::unique_ptr impl_; +}; + +template +inline constexpr bool + enable_borrowed_range> = + (Cat & category::borrowed) != category::none; + +} // namespace std::ranges #endif diff --git a/impl/any_view/test/example.cpp b/impl/any_view/test/example.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/impl/any_view/test/iterator/bidirectional.cpp b/impl/any_view/test/iterator/bidirectional.cpp new file mode 100644 index 0000000..26198e1 --- /dev/null +++ b/impl/any_view/test/iterator/bidirectional.cpp @@ -0,0 +1,145 @@ + +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[bidirectional]") + +namespace { + +using AnyView = + std::ranges::any_view; +using Iter = AnyView::any_iterator; + +static_assert(std::bidirectional_iterator); +static_assert(!std::random_access_iterator); +static_assert(std::same_as, int&>); +static_assert(std::same_as, int&&>); +static_assert(std::same_as, int>); +static_assert(std::same_as, ptrdiff_t>); +static_assert( + std::same_as::iterator_category, + std::bidirectional_iterator_tag>); +static_assert( + std::same_as::value_type, int>); +static_assert( + std::same_as::reference, int&>); +static_assert(std::same_as::difference_type, + ptrdiff_t>); + +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter(v.begin()); + { + std::same_as decltype(auto) r = *iter; + assert(r == 1); + } + + { + std::same_as decltype(auto) r = ++iter; + assert(*iter == 2); + assert(&r == &iter); + } + + { + std::same_as decltype(auto) r = iter++; + assert(*iter == 3); + assert(*r == 2); + } + + { + std::same_as decltype(auto) r = std::ranges::iter_move(iter); + assert(r == 3); + } +} + +constexpr void move() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter2(std::move(iter1)); + + assert(*iter1 == 1); + assert(*iter2 == 1); + + ++iter2; + + assert(*iter1 == 1); + assert(*iter2 == 2); + + iter1 = std::move(iter2); + assert(*iter1 == 2); + assert(*iter2 == 2); +} + +constexpr void copy() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter2(iter1); + + assert(*iter1 == 1); + assert(*iter2 == 1); + + ++iter1; + + assert(*iter1 == 2); + assert(*iter2 == 1); + + iter2 = iter1; + assert(*iter1 == 2); + assert(*iter2 == 2); +} + +constexpr void equal() { + std::array v{1, 2, 3, 4, 5}; + Iter iter1(v.begin()); + Iter iter2(iter1); + + std::same_as decltype(auto) r = iter1 == iter2; + assert(r); + + ++iter1; + assert(iter1 != iter2); +} + +constexpr void decrement() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter(v.begin()); + ++iter; + ++iter; + ++iter; + + assert(*iter == 4); + + { + std::same_as decltype(auto) r = --iter; + assert(*iter == 3); + assert(&r == &iter); + } + + { + std::same_as decltype(auto) r = iter--; + assert(*iter == 2); + assert(*r == 3); + } +} + +constexpr bool test() { + basic(); + move(); + copy(); + equal(); + decrement(); + return true; +} + +TEST_POINT("forward") { + test(); + static_assert(test()); +} + +} // namespace diff --git a/impl/any_view/test/iterator/forward.cpp b/impl/any_view/test/iterator/forward.cpp new file mode 100644 index 0000000..02035aa --- /dev/null +++ b/impl/any_view/test/iterator/forward.cpp @@ -0,0 +1,135 @@ + +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[forward]") + +namespace { + +using AnyView = std::ranges::any_view; +using Iter = AnyView::any_iterator; + +static_assert(std::forward_iterator); +static_assert(!std::bidirectional_iterator); +static_assert(std::same_as, int&>); +static_assert(std::same_as, int&&>); +static_assert(std::same_as, int>); +static_assert(std::same_as, ptrdiff_t>); +static_assert( + std::same_as::iterator_category, + std::forward_iterator_tag>); +static_assert( + std::same_as::value_type, int>); +static_assert( + std::same_as::reference, int&>); +static_assert(std::same_as::difference_type, + ptrdiff_t>); + +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter(v.begin()); + { + std::same_as decltype(auto) r = *iter; + assert(r == 1); + } + + { + std::same_as decltype(auto) r = ++iter; + assert(*iter == 2); + assert(&r == &iter); + } + + { + std::same_as decltype(auto) r = iter++; + assert(*iter == 3); + assert(*r == 2); + } + + { + std::same_as decltype(auto) r = std::ranges::iter_move(iter); + assert(r == 3); + } +} + +constexpr void default_ctor() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter(v.begin()); + + Iter iter1, iter2; + + iter1 = iter; + iter2 = std::move(iter); + + assert(*iter1 == 1); + assert(*iter2 == 1); +} + +constexpr void move() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter2(std::move(iter1)); + + assert(*iter1 == 1); + assert(*iter2 == 1); + + ++iter2; + + assert(*iter1 == 1); + assert(*iter2 == 2); + + iter1 = std::move(iter2); + assert(*iter1 == 2); + assert(*iter2 == 2); +} + +constexpr void copy() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter2(iter1); + + assert(*iter1 == 1); + assert(*iter2 == 1); + + ++iter1; + + assert(*iter1 == 2); + assert(*iter2 == 1); + + iter2 = iter1; + assert(*iter1 == 2); + assert(*iter2 == 2); +} + +constexpr void equal() { + std::array v{1, 2, 3, 4, 5}; + Iter iter1(v.begin()); + Iter iter2(iter1); + + std::same_as decltype(auto) r = iter1 == iter2; + assert(r); + + ++iter1; + assert(iter1 != iter2); +} + +constexpr bool test() { + default_ctor(); + basic(); + move(); + copy(); + equal(); + return true; +} + +TEST_POINT("forward") { + test(); + static_assert(test()); +} + +} // namespace diff --git a/impl/any_view/test/iterator/input.cpp b/impl/any_view/test/iterator/input.cpp new file mode 100644 index 0000000..d58ce7b --- /dev/null +++ b/impl/any_view/test/iterator/input.cpp @@ -0,0 +1,83 @@ + +#include +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[input]") + +namespace { + +using AnyView = std::ranges::any_view; +using Iter = AnyView::any_iterator; + +static_assert(std::input_iterator); +static_assert(!std::forward_iterator); +static_assert(std::same_as, int&>); +static_assert(std::same_as, int&&>); +static_assert(std::same_as, int>); +static_assert(std::same_as, ptrdiff_t>); + +template +concept has_iterator_category = requires() { typename T::iterator_category; }; +static_assert(!has_iterator_category); + +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter(v.begin()); + { + std::same_as decltype(auto) r = *iter; + assert(r == 1); + } + + { + std::same_as decltype(auto) r = ++iter; + assert(*iter == 2); + assert(&r == &iter); + } + + { + static_assert(std::same_as); + iter++; + assert(*iter == 3); + } + + { + std::same_as decltype(auto) r = std::ranges::iter_move(iter); + assert(r == 3); + } +} + +constexpr void move() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter2(std::move(iter1)); + + assert(*iter1 == 1); + assert(*iter2 == 1); + + ++iter2; + + assert(*iter1 == 1); + assert(*iter2 == 2); + + iter1 = std::move(iter2); + assert(*iter1 == 2); + assert(*iter2 == 2); +} + +constexpr bool test() { + basic(); + move(); + return true; +} + +TEST_POINT("input") { + test(); + static_assert(test()); +} + +} // namespace diff --git a/impl/any_view/test/iterator/input_common.cpp b/impl/any_view/test/iterator/input_common.cpp new file mode 100644 index 0000000..b77fe6f --- /dev/null +++ b/impl/any_view/test/iterator/input_common.cpp @@ -0,0 +1,117 @@ + +#include +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[input_common]") + +namespace { + +using AnyView = std::ranges::any_view; +using Iter = AnyView::any_iterator; + +static_assert(std::input_iterator); +static_assert(!std::forward_iterator); +static_assert(std::same_as, int&>); +static_assert(std::same_as, int&&>); +static_assert(std::same_as, int>); +static_assert(std::same_as, ptrdiff_t>); + +template +concept has_iterator_category = requires() { typename T::iterator_category; }; +static_assert(!has_iterator_category); + +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter(v.begin()); + { + std::same_as decltype(auto) r = *iter; + assert(r == 1); + } + + { + std::same_as decltype(auto) r = ++iter; + assert(*iter == 2); + assert(&r == &iter); + } + + { + static_assert(std::same_as); + iter++; + assert(*iter == 3); + } + + { + std::same_as decltype(auto) r = std::ranges::iter_move(iter); + assert(r == 3); + } +} + +constexpr void move() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter2(std::move(iter1)); + + assert(*iter1 == 1); + assert(*iter2 == 1); + + ++iter2; + + assert(*iter1 == 1); + assert(*iter2 == 2); + + iter1 = std::move(iter2); + assert(*iter1 == 2); + assert(*iter2 == 2); +} + +constexpr void copy() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter2(iter1); + + assert(*iter1 == 1); + assert(*iter2 == 1); + + ++iter1; + + assert(*iter1 == 2); + assert(*iter2 == 1); + + iter2 = iter1; + assert(*iter1 == 2); + assert(*iter2 == 2); +} + +constexpr void equal() { + std::array v{1, 2, 3, 4, 5}; + Iter iter1(v.begin()); + Iter iter2(v.begin()); + + std::same_as decltype(auto) r = iter1 == iter2; + assert(r); + + ++iter1; + assert(iter1 != iter2); +} + +constexpr bool test() { + basic(); + move(); + copy(); + equal(); + return true; +} + +TEST_POINT("input") { + test(); + static_assert(test()); +} + +} // namespace diff --git a/impl/any_view/test/iterator/random_access.cpp b/impl/any_view/test/iterator/random_access.cpp new file mode 100644 index 0000000..96c1a66 --- /dev/null +++ b/impl/any_view/test/iterator/random_access.cpp @@ -0,0 +1,225 @@ + +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[random_access]") + +namespace { + +using AnyView = + std::ranges::any_view; +using Iter = AnyView::any_iterator; + +static_assert(std::random_access_iterator); +static_assert(!std::contiguous_iterator); +static_assert(std::same_as, int&>); +static_assert(std::same_as, int&&>); +static_assert(std::same_as, int>); +static_assert(std::same_as, ptrdiff_t>); +static_assert( + std::same_as::iterator_category, + std::random_access_iterator_tag>); +static_assert( + std::same_as::value_type, int>); +static_assert( + std::same_as::reference, int&>); +static_assert(std::same_as::difference_type, + ptrdiff_t>); + +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter(v.begin()); + { + std::same_as decltype(auto) r = *iter; + assert(r == 1); + } + + { + std::same_as decltype(auto) r = ++iter; + assert(*iter == 2); + assert(&r == &iter); + } + + { + std::same_as decltype(auto) r = iter++; + assert(*iter == 3); + assert(*r == 2); + } + + { + std::same_as decltype(auto) r = std::ranges::iter_move(iter); + assert(r == 3); + } +} + +constexpr void move() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter2(std::move(iter1)); + + assert(*iter1 == 1); + assert(*iter2 == 1); + + ++iter2; + + assert(*iter1 == 1); + assert(*iter2 == 2); + + iter1 = std::move(iter2); + assert(*iter1 == 2); + assert(*iter2 == 2); +} + +constexpr void copy() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter2(iter1); + + assert(*iter1 == 1); + assert(*iter2 == 1); + + ++iter1; + + assert(*iter1 == 2); + assert(*iter2 == 1); + + iter2 = iter1; + assert(*iter1 == 2); + assert(*iter2 == 2); +} + +constexpr void equal() { + std::array v{1, 2, 3, 4, 5}; + Iter iter1(v.begin()); + Iter iter2(iter1); + + std::same_as decltype(auto) r = iter1 == iter2; + assert(r); + + ++iter1; + assert(iter1 != iter2); +} + +constexpr void decrement() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter(v.begin()); + ++iter; + ++iter; + ++iter; + + assert(*iter == 4); + + { + std::same_as decltype(auto) r = --iter; + assert(*iter == 3); + assert(&r == &iter); + } + + { + std::same_as decltype(auto) r = iter--; + assert(*iter == 2); + assert(*r == 3); + } +} + +constexpr void random_access() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter(v.begin()); + + { + std::same_as decltype(auto) r = iter += 3; + assert(*iter == 4); + assert(&r == &iter); + } + + { + std::same_as decltype(auto) r = iter -= 2; + assert(*iter == 2); + assert(&r == &iter); + } + + { + std::same_as decltype(auto) r = iter[3]; + assert(*iter == 2); + assert(r == 5); + } + + { + std::same_as decltype(auto) r = iter + 3; + assert(*iter == 2); + assert(*r == 5); + } + + { + std::same_as decltype(auto) r = 3 + iter; + assert(*iter == 2); + assert(*r == 5); + } + + Iter iter2(v.begin() + 3); + + { + std::same_as decltype(auto) r = iter2 - 2; + assert(*iter2 == 4); + assert(*r == 2); + } + + { + std::same_as decltype(auto) r = iter2 - iter; + assert(r == 2); + } + + { + std::same_as decltype(auto) r = iter - iter2; + assert(r == -2); + } +} + +constexpr void compare() { + std::array v{1, 2, 3, 4, 5}; + + Iter iter1(v.begin()); + Iter iter1_copy = iter1; + + Iter iter4(v.begin() + 3); + + assert(iter1 < iter4); + assert(iter1 <= iter4); + assert(!(iter1 > iter4)); + assert(!(iter1 >= iter4)); + + assert(!(iter4 < iter1)); + assert(!(iter4 <= iter1)); + assert(iter4 > iter1); + assert(iter4 >= iter1); + + assert(!(iter1 < iter1_copy)); + assert(iter1 <= iter1_copy); + assert(!(iter1 > iter1_copy)); + assert(iter1 >= iter1_copy); +} + +constexpr bool test() { + basic(); + move(); + copy(); + equal(); + decrement(); + random_access(); + compare(); + return true; +} + +TEST_POINT("forward") { + test(); + static_assert(test()); +} + +} // namespace diff --git a/impl/any_view/test/view/bidirectional.cpp b/impl/any_view/test/view/bidirectional.cpp new file mode 100644 index 0000000..87ba444 --- /dev/null +++ b/impl/any_view/test/view/bidirectional.cpp @@ -0,0 +1,146 @@ + +#include +#include +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[bidirectional_view]") + +namespace { + +using AnyView = + std::ranges::any_view; + +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::movable); +static_assert(!std::copyable); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::borrowed_range); + +using AnyViewFull = std::ranges::any_view< + int&, std::ranges::category::bidirectional | std::ranges::category::sized | + std::ranges::category::common | std::ranges::category::borrowed>; + +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::movable); +static_assert(std::copyable); +static_assert(std::ranges::sized_range); +static_assert(std::ranges::common_range); +static_assert(std::ranges::borrowed_range); + +template +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + V view(std::views::all(v)); + + auto it = view.begin(); + assert(*it == 1); + + auto st = view.end(); + assert(it != st); + + ++it; + ++it; + ++it; + ++it; + ++it; + + assert(it == st); +} + +template +constexpr void forward() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + auto it1 = view.begin(); + ++it1; + assert(*it1 == 2); + + auto it2 = view.begin(); + assert(*it2 == 1); + + it2 = it1; + assert(*it2 == 2); +} + +template +constexpr void bidirectional() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + auto it1 = view.begin(); + ++it1; + assert(*it1 == 2); + + --it1; + assert(*it1 == 1); +} + +template +constexpr void move() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(std::move(view1)); + + assert(*view3.begin() == 1); + + view3 = std::move(view2); + assert(*view3.begin() == 3); +} + +template +constexpr void copy() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(view1); + + assert(*view1.begin() == 1); + assert(*view3.begin() == 1); + + view3 = view2; + assert(*view2.begin() == 3); + assert(*view3.begin() == 3); +} + +template +constexpr void size() { + std::array v1{1, 2, 3, 4, 5}; + + V view1(std::views::all(v1)); + assert(view1.size() == 5); +} + +constexpr bool test() { + basic(); + basic(); + forward(); + forward(); + bidirectional(); + bidirectional(); + move(); + move(); + copy(); + size(); + return true; +} + +TEST_POINT("forward") { + test(); + static_assert(test()); +} + +} // namespace diff --git a/impl/any_view/test/view/contiguous.cpp b/impl/any_view/test/view/contiguous.cpp new file mode 100644 index 0000000..44d10d3 --- /dev/null +++ b/impl/any_view/test/view/contiguous.cpp @@ -0,0 +1,166 @@ + +#include +#include +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[contiguous_view]") + +namespace { + +using AnyView = + std::ranges::any_view; + +static_assert(std::ranges::contiguous_range); +static_assert(std::movable); +static_assert(!std::copyable); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::borrowed_range); + +using AnyViewFull = std::ranges::any_view< + int&, std::ranges::category::contiguous | std::ranges::category::sized | + std::ranges::category::common | std::ranges::category::borrowed>; + +static_assert(std::ranges::contiguous_range); +static_assert(std::movable); +static_assert(std::copyable); +static_assert(std::ranges::sized_range); +static_assert(std::ranges::common_range); +static_assert(std::ranges::borrowed_range); + +template +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + V view(std::views::all(v)); + + auto it = view.begin(); + assert(*it == 1); + + auto st = view.end(); + assert(it != st); + + ++it; + ++it; + ++it; + ++it; + ++it; + + assert(it == st); +} + +template +constexpr void forward() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + auto it1 = view.begin(); + ++it1; + assert(*it1 == 2); + + auto it2 = view.begin(); + assert(*it2 == 1); + + it2 = it1; + assert(*it2 == 2); +} + +template +constexpr void bidirectional() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + auto it1 = view.begin(); + ++it1; + assert(*it1 == 2); + + --it1; + assert(*it1 == 1); +} + +template +constexpr void random_access() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + auto it1 = view.begin(); + it1 += 4; + assert(*it1 == 5); + + auto it2 = it1 - 3; + assert(*it2 == 2); +} + +template +constexpr void contiguous() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + std::same_as decltype(auto) it1 = view.begin(); + assert(it1 == v[0]); +} + +template +constexpr void move() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(std::move(view1)); + + assert(*view3.begin() == 1); + + view3 = std::move(view2); + assert(*view3.begin() == 3); +} + +template +constexpr void copy() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(view1); + + assert(*view1.begin() == 1); + assert(*view3.begin() == 1); + + view3 = view2; + assert(*view2.begin() == 3); + assert(*view3.begin() == 3); +} + +template +constexpr void size() { + std::array v1{1, 2, 3, 4, 5}; + + V view1(std::views::all(v1)); + assert(view1.size() == 5); +} + +constexpr bool test() { + basic(); + basic(); + forward(); + forward(); + bidirectional(); + bidirectional(); + random_access(); + random_access(); + move(); + move(); + copy(); + size(); + return true; +} + +TEST_POINT("forward") { + test(); + static_assert(test()); +} + +} // namespace diff --git a/impl/any_view/test/view/forward.cpp b/impl/any_view/test/view/forward.cpp new file mode 100644 index 0000000..109f4c2 --- /dev/null +++ b/impl/any_view/test/view/forward.cpp @@ -0,0 +1,132 @@ + +#include +#include +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[forward_view]") + +namespace { + +using AnyView = + std::ranges::any_view; + +static_assert(std::ranges::forward_range); +static_assert(!std::ranges::bidirectional_range); +static_assert(std::movable); +static_assert(!std::copyable); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::borrowed_range); + +using AnyViewFull = std::ranges::any_view< + int&, std::ranges::category::forward | std::ranges::category::sized | + std::ranges::category::common | std::ranges::category::borrowed>; + +static_assert(std::ranges::forward_range); +static_assert(!std::ranges::bidirectional_range); +static_assert(std::movable); +static_assert(std::copyable); +static_assert(std::ranges::sized_range); +static_assert(std::ranges::common_range); +static_assert(std::ranges::borrowed_range); + +template +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + V view(std::views::all(v)); + + auto it = view.begin(); + assert(*it == 1); + + auto st = view.end(); + assert(it != st); + + ++it; + ++it; + ++it; + ++it; + ++it; + + assert(it == st); +} + +template +constexpr void forward() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + auto it1 = view.begin(); + ++it1; + assert(*it1 == 2); + + auto it2 = view.begin(); + assert(*it2 == 1); + + it2 = it1; + assert(*it2 == 2); +} + +template +constexpr void move() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(std::move(view1)); + + assert(*view3.begin() == 1); + + view3 = std::move(view2); + assert(*view3.begin() == 3); +} + +template +constexpr void copy() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(view1); + + assert(*view1.begin() == 1); + assert(*view3.begin() == 1); + + view3 = view2; + assert(*view2.begin() == 3); + assert(*view3.begin() == 3); +} + +template +constexpr void size() { + std::array v1{1, 2, 3, 4, 5}; + + V view1(std::views::all(v1)); + assert(view1.size() == 5); +} + +constexpr bool test() { + basic(); + basic(); + forward(); + forward(); + move(); + move(); + copy(); + size(); + return true; +} + +TEST_POINT("forward") { + test(); + static_assert(test()); +} + +} // namespace diff --git a/impl/any_view/test/view/input.cpp b/impl/any_view/test/view/input.cpp new file mode 100644 index 0000000..a07ed6a --- /dev/null +++ b/impl/any_view/test/view/input.cpp @@ -0,0 +1,117 @@ + +#include +#include +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[input_view]") + +namespace { + +using AnyView = + std::ranges::any_view; + +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(std::movable); +static_assert(!std::copyable); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::borrowed_range); + +using AnyViewFull = std::ranges::any_view< + int&, std::ranges::category::input | std::ranges::category::sized | + std::ranges::category::common | std::ranges::category::borrowed>; + +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(std::movable); +static_assert(std::copyable); +static_assert(std::ranges::sized_range); +static_assert(std::ranges::common_range); +static_assert(std::ranges::borrowed_range); + +template +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + V view(std::views::all(v)); + + auto it = view.begin(); + assert(*it == 1); + + auto st = view.end(); + assert(it != st); + + ++it; + ++it; + ++it; + ++it; + ++it; + + assert(it == st); +} + +template +constexpr void move() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(std::move(view1)); + + assert(*view3.begin() == 1); + + view3 = std::move(view2); + assert(*view3.begin() == 3); +} + +template +constexpr void copy() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(view1); + + // std::ranges::end(view3); + + assert(*view1.begin() == 1); + assert(*view3.begin() == 1); + + view3 = view2; + assert(*view2.begin() == 3); + assert(*view3.begin() == 3); +} + +template +constexpr void size() { + std::array v1{1, 2, 3, 4, 5}; + + V view1(std::views::all(v1)); + assert(view1.size() == 5); +} + +constexpr bool test() { + basic(); + basic(); + move(); + move(); + copy(); + size(); + return true; +} + +TEST_POINT("input") { + test(); + static_assert(test()); +} + +} // namespace diff --git a/impl/any_view/test/view/random_access.cpp b/impl/any_view/test/view/random_access.cpp new file mode 100644 index 0000000..ddd22cc --- /dev/null +++ b/impl/any_view/test/view/random_access.cpp @@ -0,0 +1,165 @@ + +#include +#include +#include +#include + +#include "any_view.hpp" + +#define TEST_POINT(x) TEST_CASE(x, "[random_access_view]") + +namespace { + +using AnyView = + std::ranges::any_view; + +static_assert(std::ranges::random_access_range); +static_assert(!std::ranges::contiguous_range); +static_assert(std::movable); +static_assert(!std::copyable); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::borrowed_range); + +using AnyViewFull = std::ranges::any_view< + int&, std::ranges::category::random_access | std::ranges::category::sized | + std::ranges::category::common | std::ranges::category::borrowed>; + +static_assert(std::ranges::random_access_range); +static_assert(!std::ranges::contiguous_range); +static_assert(std::movable); +static_assert(std::copyable); +static_assert(std::ranges::sized_range); +static_assert(std::ranges::common_range); +static_assert(std::ranges::borrowed_range); + +template +constexpr void basic() { + std::array v{1, 2, 3, 4, 5}; + + V view(std::views::all(v)); + + auto it = view.begin(); + assert(*it == 1); + + auto st = view.end(); + assert(it != st); + + ++it; + ++it; + ++it; + ++it; + ++it; + + assert(it == st); +} + +template +constexpr void forward() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + auto it1 = view.begin(); + ++it1; + assert(*it1 == 2); + + auto it2 = view.begin(); + assert(*it2 == 1); + + it2 = it1; + assert(*it2 == 2); +} + +template +constexpr void bidirectional() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + auto it1 = view.begin(); + ++it1; + assert(*it1 == 2); + + --it1; + assert(*it1 == 1); +} + +template +constexpr void random_access() { + std::array v{1, 2, 3, 4, 5}; + V view(std::views::all(v)); + auto it1 = view.begin(); + it1 += 4; + assert(*it1 == 5); + + auto it2 = it1 - 3; + assert(*it2 == 2); +} + +template +constexpr void move() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(std::move(view1)); + + assert(*view3.begin() == 1); + + view3 = std::move(view2); + assert(*view3.begin() == 3); +} + +template +constexpr void copy() { + std::array v1{1, 2, 3, 4, 5}; + std::array v2{3}; + + V view1(std::views::all(v1)); + V view2(std::views::all(v2)); + + V view3(view1); + + assert(*view1.begin() == 1); + assert(*view3.begin() == 1); + + view3 = view2; + assert(*view2.begin() == 3); + assert(*view3.begin() == 3); +} + +template +constexpr void size() { + std::array v1{1, 2, 3, 4, 5}; + + V view1(std::views::all(v1)); + assert(view1.size() == 5); +} + +constexpr bool test() { + basic(); + basic(); + forward(); + forward(); + bidirectional(); + bidirectional(); + random_access(); + random_access(); + move(); + move(); + copy(); + size(); + return true; +} + +TEST_POINT("forward") { + std::array v1{1, 2, 3, 4, 5}; + AnyView v(std::views::all(v1)); + auto it = v.begin(); + auto st = v.end(); + + test(); + static_assert(test()); +} + +} // namespace