diff --git a/libcxx/docs/Status/ZipProjects.csv b/libcxx/docs/Status/ZipProjects.csv --- a/libcxx/docs/Status/ZipProjects.csv +++ b/libcxx/docs/Status/ZipProjects.csv @@ -7,10 +7,10 @@ | `[allocator.uses.construction] <https://wg21.link/allocator.uses.construction>`_", "[pair] uses_allocator_construction_args overloads", None, Unassigned, |Not Started| | `[vector.bool] <https://wg21.link/vector.bool>`_, "[vector<bool>::reference] add const operator= overload", None, Nikolas Klauser, |Not Started| | `[iterator.concept.winc] <https://wg21.link/iterator.concept.winc>`_, "Update weakly_comparable", None, Unassigned, |Not Started| -| `[range.zip] <https://wg21.link/ranges.syn>`_, "zip_view", "| `zip_view::iterator` -| `zip_view::sentinel`", Unassigned, |Not Started| -| `[range.zip.iterator] <https://wg21.link/range.zip.transform>`_, "zip_view::iterator", None, Unassigned, |Not Started| -| `[range.zip.sentinel] <https://wg21.link/range.zip.sentinel>`_, "zip_view::sentinel", None, Unassigned, |Not Started| +| `[range.zip] <https://wg21.link/ranges.syn>`_, "`zip_view <https://reviews.llvm.org/D122806>`_", "| `zip_view::iterator` +| `zip_view::sentinel`", Hui Xie, |Complete| +| `[range.zip.iterator] <https://wg21.link/range.zip.iterator>`_, "`zip_view::iterator <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete| +| `[range.zip.sentinel] <https://wg21.link/range.zip.sentinel>`_, "`zip_view::sentinel <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete| | `[range.zip.transform.view] <https://wg21.link/range.zip.transform.view>`_, "zip_transform_view", "| `zip_transform_view::iterator` | `zip_transform_view::sentinel`", Unassigned, |Not Started| | `[range.zip.transform.iterator] <https://wg21.link/range.zip.transform.iterator>`_, "zip_transform_view::iterator", None, Unassigned, |Not Started| diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -371,6 +371,7 @@ __ranges/transform_view.h __ranges/view_interface.h __ranges/views.h + __ranges/zip_view.h __split_buffer __std_stream __string diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/zip_view.h @@ -0,0 +1,530 @@ +// -*- 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_ZIP_VIEW_H +#define _LIBCPP___RANGES_ZIP_VIEW_H + +#include <__config> + +#include <__algorithm/ranges_min.h> +#include <__compare/three_way_comparable.h> +#include <__concepts/convertible_to.h> +#include <__concepts/equality_comparable.h> +#include <__functional/invoke.h> +#include <__functional/operations.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.h> +#include <__iterator/iterator_traits.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/empty_view.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/size.h> +#include <__ranges/view_interface.h> +#include <__utility/forward.h> +#include <__utility/integer_sequence.h> +#include <__utility/move.h> +#include <tuple> +#include <type_traits> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +namespace ranges { + +template <class... _Ranges> +concept __zip_is_common = (sizeof...(_Ranges) == 1 && (common_range<_Ranges> && ...)) || + (!(bidirectional_range<_Ranges> && ...) && (common_range<_Ranges> && ...)) || + ((random_access_range<_Ranges> && ...) && (sized_range<_Ranges> && ...)); + +template <typename _Tp, typename _Up> +auto __tuple_or_pair_test() -> pair<_Tp, _Up>; + +template <typename... _Types> + requires(sizeof...(_Types) != 2) +auto __tuple_or_pair_test() -> tuple<_Types...>; + +template <class... _Types> +using __tuple_or_pair = decltype(__tuple_or_pair_test<_Types...>()); + +template <class _Fun, class _Tuple> +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tuple) { + return std::apply( + [&]<class... _Types>(_Types&&... __elements) { + return __tuple_or_pair<invoke_result_t<_Fun&, _Types>...>( + std::invoke(__f, std::forward<_Types>(__elements))...); + }, + std::forward<_Tuple>(__tuple)); +} + +template <class _Fun, class _Tuple> +_LIBCPP_HIDE_FROM_ABI constexpr void __tuple_for_each(_Fun&& __f, _Tuple&& __tuple) { + std::apply( + [&]<class... _Types>(_Types&&... __elements) { (std::invoke(__f, std::forward<_Types>(__elements)), ...); }, + std::forward<_Tuple>(__tuple)); +} + +template <class _Fun, class _Tuple1, class _Tuple2, size_t... _Indices> +_LIBCPP_HIDE_FROM_ABI constexpr __tuple_or_pair< + invoke_result_t<_Fun&, typename tuple_element<_Indices, remove_cvref_t<_Tuple1>>::type, + typename tuple_element<_Indices, remove_cvref_t<_Tuple2>>::type>...> +__tuple_zip_transform(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2, index_sequence<_Indices...>) { + return {std::invoke(__f, std::get<_Indices>(std::forward<_Tuple1>(__tuple1)), + std::get<_Indices>(std::forward<_Tuple2>(__tuple2)))...}; +} + +template <class _Fun, class _Tuple1, class _Tuple2> +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_transform(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2) { + return ranges::__tuple_zip_transform(__f, std::forward<_Tuple1>(__tuple1), std::forward<_Tuple2>(__tuple2), + std::make_index_sequence<tuple_size<remove_cvref_t<_Tuple1>>::value>()); +} + +template <class _Fun, class _Tuple1, class _Tuple2, size_t... _Indices> +_LIBCPP_HIDE_FROM_ABI constexpr void __tuple_zip_for_each(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2, + index_sequence<_Indices...>) { + (std::invoke(__f, std::get<_Indices>(std::forward<_Tuple1>(__tuple1)), + std::get<_Indices>(std::forward<_Tuple2>(__tuple2))), + ...); +} + +template <class _Fun, class _Tuple1, class _Tuple2> +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_for_each(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2) { + return ranges::__tuple_zip_for_each(__f, std::forward<_Tuple1>(__tuple1), std::forward<_Tuple2>(__tuple2), + std::make_index_sequence<tuple_size<remove_cvref_t<_Tuple1>>::value>()); +} + +// abs in cstdlib is not constexpr +// TODO : remove __abs once P0533R9 is implemented. +template <class _Tp> +_LIBCPP_HIDE_FROM_ABI constexpr _Tp __abs(_Tp __t) { + return __t < 0 ? -__t : __t; +} + +template <input_range... _Views> + requires(view<_Views> && ...) && (sizeof...(_Views) > 0) +class zip_view : public view_interface<zip_view<_Views...>> { + + _LIBCPP_NO_UNIQUE_ADDRESS tuple<_Views...> __views_; + + template <bool> + class __iterator; + + template <bool> + class __sentinel; + +public: + _LIBCPP_HIDE_FROM_ABI + zip_view() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit zip_view(_Views... __views) : __views_(std::move(__views)...) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() + requires(!(__simple_view<_Views> && ...)) + { + return __iterator<false>(ranges::__tuple_transform(ranges::begin, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() const + requires(range<const _Views> && ...) + { + return __iterator<true>(ranges::__tuple_transform(ranges::begin, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() + requires(!(__simple_view<_Views> && ...)) + { + if constexpr (!__zip_is_common<_Views...>) { + return __sentinel<false>(ranges::__tuple_transform(ranges::end, __views_)); + } else if constexpr ((random_access_range<_Views> && ...)) { + return begin() + iter_difference_t<__iterator<false>>(size()); + } else { + return __iterator<false>(ranges::__tuple_transform(ranges::end, __views_)); + } + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() const + requires(range<const _Views> && ...) + { + if constexpr (!__zip_is_common<const _Views...>) { + return __sentinel<true>(ranges::__tuple_transform(ranges::end, __views_)); + } else if constexpr ((random_access_range<const _Views> && ...)) { + return begin() + iter_difference_t<__iterator<true>>(size()); + } else { + return __iterator<true>(ranges::__tuple_transform(ranges::end, __views_)); + } + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto size() + requires(sized_range<_Views> && ...) + { + return std::apply( + [](auto... __sizes) { + using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>; + return ranges::min({_CT(__sizes)...}); + }, + ranges::__tuple_transform(ranges::size, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto size() const + requires(sized_range<const _Views> && ...) + { + return std::apply( + [](auto... __sizes) { + using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>; + return ranges::min({_CT(__sizes)...}); + }, + ranges::__tuple_transform(ranges::size, __views_)); + } +}; + +template <class... _Ranges> +zip_view(_Ranges&&...) -> zip_view<views::all_t<_Ranges>...>; + +template <bool _Const, class... _Views> +concept __zip_all_random_access = (random_access_range<__maybe_const<_Const, _Views>> && ...); + +template <bool _Const, class... _Views> +concept __zip_all_bidirectional = (bidirectional_range<__maybe_const<_Const, _Views>> && ...); + +template <bool _Const, class... _Views> +concept __zip_all_forward = (forward_range<__maybe_const<_Const, _Views>> && ...); + +template <bool _Const, class... _Views> +consteval auto __get_zip_view_iterator_tag() { + if constexpr (__zip_all_random_access<_Const, _Views...>) { + return random_access_iterator_tag(); + } else if constexpr (__zip_all_bidirectional<_Const, _Views...>) { + return bidirectional_iterator_tag(); + } else if constexpr (__zip_all_forward<_Const, _Views...>) { + return forward_iterator_tag(); + } else { + return input_iterator_tag(); + } +} + +template <bool _Const, class... _Views> +struct __zip_view_iterator_category_base {}; + +template <bool _Const, class... _Views> + requires __zip_all_forward<_Const, _Views...> +struct __zip_view_iterator_category_base<_Const, _Views...> { + using iterator_category = input_iterator_tag; +}; + +template <input_range... _Views> + requires(view<_Views> && ...) && (sizeof...(_Views) > 0) +template <bool _Const> +class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base<_Const, _Views...> { + + __tuple_or_pair<iterator_t<__maybe_const<_Const, _Views>>...> __current_; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __iterator(__tuple_or_pair<iterator_t<__maybe_const<_Const, _Views>>...> __current) + : __current_(std::move(__current)) {} + + template <bool> + friend class zip_view<_Views...>::__iterator; + + template <bool> + friend class zip_view<_Views...>::__sentinel; + + friend class zip_view<_Views...>; + +public: + using iterator_concept = decltype(__get_zip_view_iterator_tag<_Const, _Views...>()); + using value_type = __tuple_or_pair<range_value_t<__maybe_const<_Const, _Views>>...>; + using difference_type = common_type_t<range_difference_t<__maybe_const<_Const, _Views>>...>; + + _LIBCPP_HIDE_FROM_ABI + __iterator() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator(__iterator<!_Const> __i) + requires _Const && (convertible_to<iterator_t<_Views>, iterator_t<__maybe_const<_Const, _Views>>> && ...) + : __current_(std::move(__i.__current_)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator*() const { + return ranges::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator++() { + ranges::__tuple_for_each([](auto& __i) { ++__i; }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr void operator++(int) { ++*this; } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator operator++(int) + requires __zip_all_forward<_Const, _Views...> + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator--() + requires __zip_all_bidirectional<_Const, _Views...> + { + ranges::__tuple_for_each([](auto& __i) { --__i; }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator operator--(int) + requires __zip_all_bidirectional<_Const, _Views...> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator+=(difference_type __x) + requires __zip_all_random_access<_Const, _Views...> + { + ranges::__tuple_for_each([&]<class _Iter>(_Iter& __i) { __i += iter_difference_t<_Iter>(__x); }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator-=(difference_type __x) + requires __zip_all_random_access<_Const, _Views...> + { + ranges::__tuple_for_each([&]<class _Iter>(_Iter& __i) { __i -= iter_difference_t<_Iter>(__x); }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator[](difference_type __n) const + requires __zip_all_random_access<_Const, _Views...> + { + return ranges::__tuple_transform( + [&]<class _Iter>(_Iter& __i) -> decltype(auto) { return __i[iter_difference_t<_Iter>(__n)]; }, __current_); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) + requires(equality_comparable<iterator_t<__maybe_const<_Const, _Views>>> && ...) + { + if constexpr (__zip_all_bidirectional<_Const, _Views...>) { + return __x.__current_ == __y.__current_; + } else { + const auto __it_equals = ranges::__tuple_zip_transform(std::equal_to<>(), __x.__current_, __y.__current_); + return std::apply([](auto... __bs) { return (__bs || ...); }, __it_equals); + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator<(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> + { + return __x.__current_ < __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> + { + return __y < __x; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> + { + return !(__y < __x); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> + { + return !(__x < __y); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> && + (three_way_comparable<iterator_t<__maybe_const<_Const, _Views>>> && ...) + { + return __x.__current_ <=> __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr __iterator operator+(const __iterator& __i, difference_type __n) + requires __zip_all_random_access<_Const, _Views...> + { + auto __r = __i; + __r += __n; + return __r; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr __iterator operator+(difference_type __n, const __iterator& __i) + requires __zip_all_random_access<_Const, _Views...> + { + return __i + __n; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr __iterator operator-(const __iterator& __i, difference_type __n) + requires __zip_all_random_access<_Const, _Views...> + { + auto __r = __i; + __r -= __n; + return __r; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y) + requires(sized_sentinel_for<iterator_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_Const, _Views>>> && + ...) + { + const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __x.__current_, __y.__current_); + return std::apply( + [](auto... __ds) { + return ranges::min({difference_type(__ds)...}, + [](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); }); + }, + __diffs); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr auto iter_move(const __iterator& __i) noexcept( + (noexcept(ranges::iter_move(declval<const iterator_t<__maybe_const<_Const, _Views>>&>())) && ...) && + (is_nothrow_move_constructible_v<range_rvalue_reference_t<__maybe_const<_Const, _Views>>> && ...)) { + return ranges::__tuple_transform(ranges::iter_move, __i.__current_); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr void iter_swap(const __iterator& __l, const __iterator& __r) noexcept( + (noexcept(ranges::iter_swap(declval<const iterator_t<__maybe_const<_Const, _Views>>&>(), + declval<const iterator_t<__maybe_const<_Const, _Views>>&>())) && + ...)) + requires(indirectly_swappable<iterator_t<__maybe_const<_Const, _Views>>> && ...) + { + ranges::__tuple_zip_for_each(ranges::iter_swap, __l.__current_, __r.__current_); + } +}; + +template <input_range... _Views> + requires(view<_Views> && ...) && (sizeof...(_Views) > 0) +template <bool _Const> +class zip_view<_Views...>::__sentinel { + + __tuple_or_pair<sentinel_t<__maybe_const<_Const, _Views>>...> __end_; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __sentinel(__tuple_or_pair<sentinel_t<__maybe_const<_Const, _Views>>...> __end) : __end_(__end) {} + + friend class zip_view<_Views...>; + + // hidden friend cannot access private member of iterator because + // they are friends of friends + template <bool _OtherConst> + _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) + __iter_current(zip_view<_Views...>::__iterator<_OtherConst> const& __it) { + return (__it.__current_); + } + +public: + _LIBCPP_HIDE_FROM_ABI + __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr __sentinel(__sentinel<!_Const> __i) + requires _Const && (convertible_to<sentinel_t<_Views>, sentinel_t<__maybe_const<_Const, _Views>>> && ...) + : __end_(std::move(__i.__end_)) {} + + template <bool _OtherConst> + requires(sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> && + ...) + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + const auto __it_equals = ranges::__tuple_zip_transform(std::equal_to<>(), __iter_current(__x), __y.__end_); + return std::apply([](auto... bs) { return (bs || ...); }, __it_equals); + } + + template <bool _OtherConst> + requires( + sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> && + ...) + _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...> + operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __iter_current(__x), __y.__end_); + return std::apply( + [](auto... __ds) { + using _Diff = common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>; + return ranges::min({_Diff(__ds)...}, + [](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); }); + }, + __diffs); + } + + template <bool _OtherConst> + requires( + sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> && + ...) + _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...> + operator-(const __sentinel& __y, const __iterator<_OtherConst>& __x) { + return -(__x - __y); + } +}; + +template <class... _Views> +inline constexpr bool enable_borrowed_range<zip_view<_Views...>> = (enable_borrowed_range<_Views> && ...); + +namespace views { +namespace __zip { + +struct __fn { + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()() const noexcept { return empty_view<tuple<>>{}; } + + template <class... _Ranges> + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Ranges&&... rs) const + noexcept(noexcept(zip_view(std::forward<_Ranges>(rs)...))) -> decltype(zip_view(std::forward<_Ranges>(rs)...)) { + return zip_view(std::forward<_Ranges>(rs)...); + } +}; + +} // namespace __zip +inline namespace __cpo { + inline constexpr auto zip = __zip::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_ZIP_VIEW_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -853,6 +853,7 @@ } module view_interface { private header "__ranges/view_interface.h" } module views { private header "__ranges/views.h" } + module zip_view { private header "__ranges/zip_view.h" } } } module ratio { diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -196,6 +196,17 @@ template<input_range V> requires view<V> && input_range<range_reference_t<V>> class join_view; + + // [range.zip], zip view + template<input_range... Views> + requires (view<Views> && ...) && (sizeof...(Views) > 0) + class zip_view; // C++2b + + template<class... Views> + inline constexpr bool enable_borrowed_range<zip_view<Views...>> = // C++2b + (enable_borrowed_range<Views> && ...); + + namespace views { inline constexpr unspecified zip = unspecified; } // C++2b } namespace std { @@ -257,6 +268,7 @@ #include <__ranges/transform_view.h> #include <__ranges/view_interface.h> #include <__ranges/views.h> +#include <__ranges/zip_view.h> #include <__tuple> // TODO: <ranges> has to export std::tuple_size. Replace this, once <tuple> is granularized. #include <compare> // Required by the standard. #include <initializer_list> // Required by the standard. diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -402,6 +402,7 @@ #include <__ranges/transform_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/transform_view.h'}} #include <__ranges/view_interface.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/view_interface.h'}} #include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}} +#include <__ranges/zip_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/zip_view.h'}} #include <__split_buffer> // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}} #include <__std_stream> // expected-error@*:* {{use of private header from outside its module: '__std_stream'}} #include <__string> // expected-error@*:* {{use of private header from outside its module: '__string'}} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp @@ -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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto begin() requires (!(simple-view<Views> && ...)); +// constexpr auto begin() const requires (range<const Views> && ...); + +#include <ranges> + +#include <cassert> +#include <concepts> +#include <tuple> +#include <utility> + +#include "types.h" + +template <class T> +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template <class T> +concept HasBegin = requires(T& t) { t.begin(); }; + +template <class T> +concept HasConstAndNonConstBegin = + HasConstBegin<T> && + requires(T& t, const T& ct) { requires !std::same_as<decltype(t.begin()), decltype(ct.begin())>; }; + +template <class T> +concept HasOnlyNonConstBegin = HasBegin<T> && ! +HasConstBegin<T>; + +template <class T> +concept HasOnlyConstBegin = HasConstBegin<T> && ! +HasConstAndNonConstBegin<T>; + +struct NoConstBeginView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + std::same_as<std::tuple<int&, int, double&>> decltype(auto) val = *v.begin(); + assert(val == std::make_tuple(1, 0, 2.0)); + assert(&(std::get<0>(val)) == &buffer[0]); + } + { + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::ranges::empty_view<int>()); + assert(v.begin() == v.end()); + } + { + // underlying ranges all model simple-view + std::ranges::zip_view v(SimpleCommon{buffer}, SimpleCommon{buffer}); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + assert(v.begin() == std::as_const(v).begin()); + auto [x, y] = *std::as_const(v).begin(); + assert(&x == &buffer[0]); + assert(&y == &buffer[0]); + } + { + // not all underlying ranges model simple-view + std::ranges::zip_view v(SimpleCommon{buffer}, NonSimpleNonCommon{buffer}); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + assert(v.begin() == std::as_const(v).begin()); + auto [x, y] = *std::as_const(v).begin(); + assert(&x == &buffer[0]); + assert(&y == &buffer[0]); + } + { + using View = std::ranges::zip_view<SimpleCommon, SimpleCommon>; + static_assert(HasOnlyConstBegin<View>); + static_assert(!HasOnlyNonConstBegin<View>); + static_assert(!HasConstAndNonConstBegin<View>); + } + { + using View = std::ranges::zip_view<SimpleCommon, NonSimpleCommon>; + static_assert(!HasOnlyConstBegin<View>); + static_assert(!HasOnlyNonConstBegin<View>); + static_assert(HasConstAndNonConstBegin<View>); + } + { + using View = std::ranges::zip_view<SimpleCommon, NoConstBeginView>; + static_assert(!HasOnlyConstBegin<View>); + static_assert(HasOnlyNonConstBegin<View>); + static_assert(!HasConstAndNonConstBegin<View>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template<class... Views> +// inline constexpr bool enable_borrowed_range<zip_view<Views...>> = +// (enable_borrowed_range<Views> && ...); + +#include <ranges> +#include <tuple> + +struct Borrowed : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range<Borrowed> = true; + +static_assert(std::ranges::borrowed_range<Borrowed>); + +struct NonBorrowed : std::ranges::view_base { + int* begin() const; + int* end() const; +}; +static_assert(!std::ranges::borrowed_range<NonBorrowed>); + +// test borrwed_range +static_assert(std::ranges::borrowed_range<std::ranges::zip_view<Borrowed>>); +static_assert(std::ranges::borrowed_range<std::ranges::zip_view<Borrowed, Borrowed>>); +static_assert(!std::ranges::borrowed_range<std::ranges::zip_view<Borrowed, NonBorrowed>>); +static_assert(!std::ranges::borrowed_range<std::ranges::zip_view<NonBorrowed>>); +static_assert(!std::ranges::borrowed_range<std::ranges::zip_view<NonBorrowed, NonBorrowed>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::views::zip + +#include <ranges> + +#include <array> +#include <cassert> +#include <tuple> +#include <type_traits> +#include <utility> + +#include "types.h" + +static_assert(std::is_invocable_v<decltype((std::views::zip))>); +static_assert(!std::is_invocable_v<decltype((std::views::zip)), int>); +static_assert(std::is_invocable_v<decltype((std::views::zip)), SizedRandomAccessView>); +static_assert(std::is_invocable_v<decltype((std::views::zip)), SizedRandomAccessView, std::ranges::iota_view<int, int>>); +static_assert(!std::is_invocable_v<decltype((std::views::zip)), SizedRandomAccessView, int>); + +constexpr bool test() { + { + auto v = std::views::zip(); + assert(std::ranges::empty(v)); + static_assert(std::is_same_v<decltype(v), std::ranges::empty_view<std::tuple<>>>); + } + { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + std::same_as<std::ranges::zip_view<SizedRandomAccessView>> auto v = std::views::zip(SizedRandomAccessView{buffer}); + assert(std::ranges::size(v) == 8); + static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, std::tuple<int&>>); + } + { + std::array a{1, 2, 3}; + std::same_as<std::ranges::zip_view<std::ranges::ref_view<std::array<int, 3>>>> auto v = std::views::zip(a); + assert(&(std::get<0>(*v.begin())) == &(a[0])); + static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, std::tuple<int&>>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template <class... Rs> +// zip_view(Rs&&...) -> zip_view<views::all_t<Rs>...>; + +#include <cassert> +#include <ranges> +#include <utility> + +struct Container { + int* begin() const; + int* end() const; +}; + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +void testCTAD() { + static_assert(std::is_same_v<decltype(std::ranges::zip_view(Container{})), + std::ranges::zip_view<std::ranges::owning_view<Container>>>); + + static_assert(std::is_same_v<decltype(std::ranges::zip_view(Container{}, View{})), + std::ranges::zip_view<std::ranges::owning_view<Container>, View>>); + + Container c{}; + static_assert(std::is_same_v< + decltype(std::ranges::zip_view(Container{}, View{}, c)), + std::ranges::zip_view<std::ranges::owning_view<Container>, View, std::ranges::ref_view<Container>>>); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp @@ -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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// zip_view() = default; + +#include <ranges> + +#include <cassert> +#include <type_traits> +#include <utility> + +constexpr int buff[] = {1, 2, 3}; + +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 3) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + +private: + int const* begin_; + int const* end_; +}; + +struct NoDefaultView : std::ranges::view_base { + NoDefaultView() = delete; + int* begin() const; + int* end() const; +}; + +static_assert(std::is_default_constructible_v<std::ranges::zip_view<DefaultConstructibleView>>); +static_assert( + std::is_default_constructible_v<std::ranges::zip_view<DefaultConstructibleView, DefaultConstructibleView>>); +static_assert(!std::is_default_constructible_v<std::ranges::zip_view<DefaultConstructibleView, NoDefaultView>>); +static_assert(!std::is_default_constructible_v<std::ranges::zip_view<NoDefaultView, NoDefaultView>>); +static_assert(!std::is_default_constructible_v<std::ranges::zip_view<NoDefaultView>>); + +constexpr bool test() { + { + using View = std::ranges::zip_view<DefaultConstructibleView, DefaultConstructibleView>; + View v = View(); // the default constructor is not explicit + assert(v.size() == 3); + auto it = v.begin(); + using Pair = std::pair<const int&, const int&>; + assert(*it++ == Pair(buff[0], buff[0])); + assert(*it++ == Pair(buff[1], buff[1])); + assert(*it == Pair(buff[2], buff[2])); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr explicit zip_view(Views...) + +#include <ranges> +#include <tuple> + +#include "types.h" + +template <class T> +void conversion_test(T); + +template <class T, class... Args> +concept implicitly_constructible_from = requires(Args&&... args) { conversion_test<T>({std::move(args)...}); }; + +// test constructor is explicit +static_assert(std::constructible_from<std::ranges::zip_view<SimpleCommon>, SimpleCommon>); +static_assert(!implicitly_constructible_from<std::ranges::zip_view<SimpleCommon>, SimpleCommon>); + +struct MoveAwareView : std::ranges::view_base { + int moves = 0; + constexpr MoveAwareView() = default; + constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; } + constexpr MoveAwareView& operator=(MoveAwareView&& other) { + moves = other.moves + 1; + other.moves = 0; + return *this; + } + constexpr const int* begin() const { return &moves; } + constexpr const int* end() const { return &moves + 1; } +}; + +constexpr bool test() { + + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + int buffer2[4] = {9, 8, 7, 6}; + { + // constructor from views + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + auto [i, j, k] = *v.begin(); + assert(i == 1); + assert(j == 0); + assert(k == 2.0); + } + + { + // arguments are moved once + MoveAwareView mv; + std::ranges::zip_view v{std::move(mv), MoveAwareView{}}; + auto [numMoves1, numMoves2] = *v.begin(); + assert(numMoves1 == 2); // one move from stack to parameter, one move from parameter to member + assert(numMoves2 == 1); + } + + // input and forward + { + std::ranges::zip_view v{InputCommonView{buffer}, ForwardSizedView{buffer2}}; + auto [i, j] = *v.begin(); + assert(i == 1); + assert(j == 9); + } + + // bidi and random_access + { + std::ranges::zip_view v{BidiCommonView{buffer}, SizedRandomAccessView{buffer2}}; + auto [i, j] = *v.begin(); + assert(i == 1); + assert(j == 9); + } + + // contiguous + { + std::ranges::zip_view v{buffer, buffer2}; + auto [i, j] = *v.begin(); + assert(i == 1); + assert(j == 9); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp @@ -0,0 +1,396 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto end() requires(!(simple-view<Views> && ...)) +// constexpr auto end() const requires(range<const Views>&&...) + +#include <ranges> +#include <tuple> + +#include "types.h" + +// ID | simple | common | bidi | random | sized | #views | v.end() | as_const(v) +// | | | | access | | | | .end() +// ---|--------|--------|------|--------|-------|--------|----------------|--------------- +// 1 | Y | Y | Y | Y | Y | 1 | iterator<true> | iterator<true> +// 2 | Y | Y | Y | Y | Y | >1 | iterator<true> | iterator<true> +// 3 | Y | N | Y | Y | N | 1 | sentinel<true> | sentinel<true> +// 4 | Y | N | Y | Y | N | >1 | sentinel<true> | sentinel<true> +// 5 | Y | Y | Y | N | Y | 1 | iterator<true> | iterator<true> +// 6 | Y | Y | Y | N | Y | >1 | sentinel<true> | sentinel<true> +// 7 | Y | Y | Y | N | N | 1 | iterator<true> | iterator<true> +// 8 | Y | Y | Y | N | N | >1 | sentinel<true> | sentinel<true> +// 9 | Y | Y | N | N | Y | 1 | iterator<true> | iterator<true> +// 10 | Y | Y | N | N | Y | >1 | iterator<true> | iterator<true> +// 11 | Y | Y | N | N | N | 1 | iterator<true> | iterator<true> +// 12 | Y | Y | N | N | N | >1 | iterator<true> | iterator<true> +// 13 | Y | N | Y | Y | Y | 1 | iterator<true> | iterator<true> +// 14 | Y | N | Y | Y | Y | >1 | iterator<true> | iterator<true> +// 15 | Y | N | Y | N | Y | 1 | sentinel<true> | sentinel<true> +// 16 | Y | N | Y | N | Y | >1 | sentinel<true> | sentinel<true> +// 17 | Y | N | Y | N | N | 1 | sentinel<true> | sentinel<true> +// 18 | Y | N | Y | N | N | >1 | sentinel<true> | sentinel<true> +// 19 | Y | N | N | N | Y | 1 | sentinel<true> | sentinel<true> +// 20 | Y | N | N | N | Y | >1 | sentinel<true> | sentinel<true> +// 21 | Y | N | N | N | N | 1 | sentinel<true> | sentinel<true> +// 22 | Y | N | N | N | N | >1 | sentinel<true> | sentinel<true> +// 23 | N | Y | Y | Y | Y | 1 | iterator<false>| iterator<true> +// 24 | N | Y | Y | Y | Y | >1 | iterator<false>| iterator<true> +// 25 | N | N | Y | Y | N | 1 | sentinel<false>| sentinel<true> +// 26 | N | N | Y | Y | N | >1 | sentinel<false>| sentinel<true> +// 27 | N | Y | Y | N | Y | 1 | iterator<false>| iterator<true> +// 28 | N | Y | Y | N | Y | >1 | sentinel<false>| sentinel<true> +// 29 | N | Y | Y | N | N | 1 | iterator<false>| iterator<true> +// 30 | N | Y | Y | N | N | >1 | sentinel<false>| sentinel<true> +// 31 | N | Y | N | N | Y | 1 | iterator<false>| iterator<true> +// 32 | N | Y | N | N | Y | >1 | iterator<false>| iterator<true> +// 33 | N | Y | N | N | N | 1 | iterator<false>| iterator<true> +// 34 | N | Y | N | N | N | >1 | iterator<false>| iterator<true> +// 35 | N | N | Y | Y | Y | 1 | iterator<false>| iterator<true> +// 36 | N | N | Y | Y | Y | >1 | iterator<false>| iterator<true> +// 37 | N | N | Y | N | Y | 1 | sentinel<false>| sentinel<true> +// 38 | N | N | Y | N | Y | >1 | sentinel<false>| sentinel<true> +// 39 | N | N | Y | N | N | 1 | sentinel<false>| sentinel<true> +// 40 | N | N | Y | N | N | >1 | sentinel<false>| sentinel<true> +// 41 | N | N | N | N | Y | 1 | sentinel<false>| sentinel<true> +// 42 | N | N | N | N | Y | >1 | sentinel<false>| sentinel<true> +// 43 | N | N | N | N | N | 1 | sentinel<false>| sentinel<true> +// 44 | N | N | N | N | N | >1 | sentinel<false>| sentinel<true> + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + int buffer2[1] = {1}; + int buffer3[3] = {1, 2, 3}; + { + // test ID 1 + std::ranges::zip_view v{SimpleCommonRandomAccessSized(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(v.begin() + 5 == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 2 + std::ranges::zip_view v{SimpleCommonRandomAccessSized(buffer1), SimpleCommonRandomAccessSized(buffer2)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(v.begin() + 1 == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 3 + std::ranges::zip_view v{NonSizedRandomAccessView(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(v.begin() + 5 == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 4 + std::ranges::zip_view v{NonSizedRandomAccessView(buffer1), NonSizedRandomAccessView(buffer3)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(v.begin() + 3 == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 5 + std::ranges::zip_view v{SizedBidiCommon(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 6 + std::ranges::zip_view v{SizedBidiCommon(buffer1), SizedBidiCommon(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 7 + std::ranges::zip_view v{BidiCommonView(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 8 + std::ranges::zip_view v{BidiCommonView(buffer1), BidiCommonView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 9 + std::ranges::zip_view v{ForwardSizedView(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 10 + std::ranges::zip_view v{ForwardSizedView(buffer1), ForwardSizedView(buffer2)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 11 + std::ranges::zip_view v{InputCommonView(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(std::ranges::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 12 + std::ranges::zip_view v{InputCommonView(buffer1), InputCommonView(buffer2)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 13 + std::ranges::zip_view v{SimpleNonCommonRandomAcessSized(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(v.begin() + 5 == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 14 + std::ranges::zip_view v{SimpleNonCommonRandomAcessSized(buffer1), SimpleNonCommonRandomAcessSized(buffer2)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(v.begin() + 1 == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 15 + std::ranges::zip_view v{SizedBidiNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 16 + std::ranges::zip_view v{SizedBidiNonCommonView(buffer1), SizedBidiNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 17 + std::ranges::zip_view v{BidiNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 18 + std::ranges::zip_view v{BidiNonCommonView(buffer1), BidiNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 19 + std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 20 + std::ranges::zip_view v{ForwardSizedNonCommon(buffer1), ForwardSizedNonCommon(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 21 + std::ranges::zip_view v{InputNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(std::ranges::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 22 + std::ranges::zip_view v{InputNonCommonView(buffer1), InputNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 23 + std::ranges::zip_view v{NonSimpleCommonRandomAccessSized(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(v.begin() + 5 == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 24 + std::ranges::zip_view v{NonSimpleCommonRandomAccessSized(buffer1), NonSimpleCommonRandomAccessSized(buffer2)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(v.begin() + 1 == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 25 + std::ranges::zip_view v{NonSimpleNonSizedRandomAccessView(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(v.begin() + 5 == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 26 + std::ranges::zip_view v{NonSimpleNonSizedRandomAccessView(buffer1), NonSimpleNonSizedRandomAccessView(buffer3)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(v.begin() + 3 == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 27 + std::ranges::zip_view v{NonSimpleSizedBidiCommon(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 28 + std::ranges::zip_view v{NonSimpleSizedBidiCommon(buffer1), NonSimpleSizedBidiCommon(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 29 + std::ranges::zip_view v{NonSimpleBidiCommonView(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 30 + std::ranges::zip_view v{NonSimpleBidiCommonView(buffer1), NonSimpleBidiCommonView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 31 + std::ranges::zip_view v{NonSimpleForwardSizedView(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 32 + std::ranges::zip_view v{NonSimpleForwardSizedView(buffer1), NonSimpleForwardSizedView(buffer2)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 33 + std::ranges::zip_view v{NonSimpleInputCommonView(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(std::ranges::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 34 + std::ranges::zip_view v{NonSimpleInputCommonView(buffer1), NonSimpleInputCommonView(buffer2)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 35 + std::ranges::zip_view v{NonSimpleNonCommonRandomAcessSized(buffer1)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(v.begin() + 5 == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 36 + std::ranges::zip_view v{NonSimpleNonCommonRandomAcessSized(buffer1), NonSimpleNonCommonRandomAcessSized(buffer2)}; + static_assert(std::ranges::common_range<decltype(v)>); + assert(v.begin() + 1 == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 37 + std::ranges::zip_view v{NonSimpleSizedBidiNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 38 + std::ranges::zip_view v{NonSimpleSizedBidiNonCommonView(buffer1), NonSimpleSizedBidiNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 39 + std::ranges::zip_view v{NonSimpleBidiNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 40 + std::ranges::zip_view v{NonSimpleBidiNonCommonView(buffer1), NonSimpleBidiNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 41 + std::ranges::zip_view v{NonSimpleForwardSizedNonCommon(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 42 + std::ranges::zip_view v{NonSimpleForwardSizedNonCommon(buffer1), NonSimpleForwardSizedNonCommon(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 43 + std::ranges::zip_view v{NonSimpleInputNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(std::ranges::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // test ID 44 + std::ranges::zip_view v{NonSimpleInputNonCommonView(buffer1), NonSimpleInputNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + } + { + // end should go to the minimum length when zip is common and random_access sized + std::ranges::zip_view v(std::views::iota(0, 4), std::views::iota(0, 8)); + auto it = --(v.end()); + auto [x, y] = *it; + assert(x == 3); + assert(y == 3); // y should not go to the end "7" + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// Some basic examples of how zip_view might be used in the wild. This is a general +// collection of sample algorithms and functions that try to mock general usage of +// this view. + +#include <ranges> + +#include <array> +#include <cassert> +#include <vector> +#include <string> + +int main(int, char**) { + { + std::ranges::zip_view v{ + std::array{1, 2}, + std::vector{4, 5, 6}, + std::array{7}, + }; + assert(std::ranges::size(v) == 1); + assert(*v.begin() == std::make_tuple(1, 4, 7)); + } + { + using namespace std::string_literals; + std::vector v{1, 2, 3, 4}; + std::array a{"abc"s, "def"s, "gh"s}; + auto view = std::views::zip(v, a); + auto it = view.begin(); + assert(&(std::get<0>(*it)) == &(v[0])); + assert(&(std::get<1>(*it)) == &(a[0])); + + ++it; + assert(&(std::get<0>(*it)) == &(v[1])); + assert(&(std::get<1>(*it)) == &(a[1])); + + ++it; + assert(&(std::get<0>(*it)) == &(v[2])); + assert(&(std::get<1>(*it)) == &(a[2])); + + ++it; + assert(it == view.end()); + } + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp @@ -0,0 +1,135 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// x += n; +// x + n; +// n + x; +// x -= n; +// x - n; +// x - y; + +#include <ranges> + +#include <array> +#include <concepts> +#include <functional> + +#include "../types.h" + +template <class T, class U> +concept canPlusEqual = requires(T& t, U& u) { t += u; }; + +template <class T, class U> +concept canMinusEqual = requires(T& t, U& u) { t -= u; }; + +constexpr bool test() { + + int buffer1[5] = {1, 2, 3, 4, 5}; + int buffer2[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + SizedRandomAccessView a{buffer1}; + static_assert(std::ranges::random_access_range<decltype(a)>); + std::array b{4.1, 3.2, 4.3, 0.1, 0.2}; + static_assert(std::ranges::contiguous_range<decltype(b)>); + { + std::ranges::zip_view v(a, b); + auto it1 = v.begin(); + + auto it2 = it1 + 3; + auto [x2, y2] = *it2; + assert(&x2 == &(a[3])); + assert(&y2 == &(b[3])); + + auto it3 = 3 + it1; + auto [x3, y3] = *it3; + assert(&x3 == &(a[3])); + assert(&y3 == &(b[3])); + + it1 += 3; + assert(it1 == it2); + auto [x1, y1] = *it2; + assert(&x1 == &(a[3])); + assert(&y1 == &(b[3])); + + using Iter = decltype(it1); + static_assert(std::invocable<std::plus<>, Iter, intptr_t>); + static_assert(std::invocable<std::plus<>, intptr_t, Iter>); + static_assert(canPlusEqual<Iter, intptr_t>); + } + { + std::ranges::zip_view v(a, b); + auto it1 = v.end(); + + auto it2 = it1 - 3; + auto [x2, y2] = *it2; + assert(&x2 == &(a[2])); + assert(&y2 == &(b[2])); + + it1 -= 3; + assert(it1 == it2); + auto [x1, y1] = *it2; + assert(&x1 == &(a[2])); + assert(&y1 == &(b[2])); + + using Iter = decltype(it1); + static_assert(std::invocable<std::minus<>, Iter, intptr_t>); + static_assert(std::invocable<std::minus<>, Iter, Iter>); + static_assert(canMinusEqual<Iter, intptr_t>); + } + { + std::ranges::zip_view v(a, b); + assert((v.end() - v.begin()) == 5); + + auto it1 = v.begin() + 2; + auto it2 = v.end() - 1; + assert((it1 - it2) == -2); + } + { + // in this case sentinel is computed by getting each underlying sentinel, so the distance + // between begin and end for each underlying iterators can be different + std::ranges::zip_view v{ForwardSizedView(buffer1), ForwardSizedView(buffer2)}; + using View = decltype(v); + static_assert(std::ranges::common_range<View>); + static_assert(!std::ranges::random_access_range<View>); + + auto it1 = v.begin(); + auto it2 = v.end(); + // it1 : <buffer1 + 0, buffer2 + 0> + // it2 : <buffer1 + 5, buffer2 + 9> + assert((it1 - it2) == -5); + assert((it2 - it1) == 5); + } + { + // One of range is not random access + std::ranges::zip_view v(a, b, ForwardSizedView{buffer1}); + using Iter = decltype(v.begin()); + static_assert(!std::invocable<std::plus<>, Iter, intptr_t>); + static_assert(!std::invocable<std::plus<>, intptr_t, Iter>); + static_assert(!canPlusEqual<Iter, intptr_t>); + static_assert(!std::invocable<std::minus<>, Iter, intptr_t>); + static_assert(std::invocable<std::minus<>, Iter, Iter>); + static_assert(!canMinusEqual<Iter, intptr_t>); + } + { + // One of range does not have sized sentinel + std::ranges::zip_view v(a, b, InputCommonView{buffer1}); + using Iter = decltype(v.begin()); + static_assert(!std::invocable<std::minus<>, Iter, Iter>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp @@ -0,0 +1,242 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// zip_view::<iterator>::operator{<,>,<=,>=,==,!=,<=>} + +#include <ranges> +#include <compare> + +#include "test_iterators.h" +#include "../types.h" + +template <class T> +concept canSmallerThan = requires(T&& t1, T&& t2) { t1 < t2; }; + +struct smaller_than_iterator { + int* it_ = nullptr; + smaller_than_iterator() = default; + constexpr smaller_than_iterator(int* it) : it_(it) {} + + using iterator_category = std::random_access_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + constexpr int& operator*() const { return *it_; } + constexpr int& operator[](difference_type n) const { return it_[n]; } + constexpr smaller_than_iterator& operator++() { + ++it_; + return *this; + } + constexpr smaller_than_iterator& operator--() { + --it_; + return *this; + } + constexpr smaller_than_iterator operator++(int) { return smaller_than_iterator(it_++); } + constexpr smaller_than_iterator operator--(int) { return smaller_than_iterator(it_--); } + + constexpr smaller_than_iterator& operator+=(difference_type n) { + it_ += n; + return *this; + } + constexpr smaller_than_iterator& operator-=(difference_type n) { + it_ -= n; + return *this; + } + + constexpr friend smaller_than_iterator operator+(smaller_than_iterator x, difference_type n) { + x += n; + return x; + } + constexpr friend smaller_than_iterator operator+(difference_type n, smaller_than_iterator x) { + x += n; + return x; + } + constexpr friend smaller_than_iterator operator-(smaller_than_iterator x, difference_type n) { + x -= n; + return x; + } + constexpr friend difference_type operator-(smaller_than_iterator x, smaller_than_iterator y) { return x.it_ - y.it_; } + + constexpr friend bool operator==(smaller_than_iterator const&, smaller_than_iterator const&) = default; + friend bool operator!=(smaller_than_iterator const&, smaller_than_iterator const&); + + constexpr friend bool operator<(smaller_than_iterator const& x, smaller_than_iterator const& y) { + return x.it_ < y.it_; + } + friend bool operator<=(smaller_than_iterator const&, smaller_than_iterator const&); + friend bool operator>(smaller_than_iterator const&, smaller_than_iterator const&); + friend bool operator>=(smaller_than_iterator const&, smaller_than_iterator const&); +}; +static_assert(std::random_access_iterator<smaller_than_iterator>); + +struct SmallerThanRange : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + constexpr smaller_than_iterator begin() const { return {buffer_}; } + constexpr smaller_than_iterator end() const { return {buffer_ + size_}; } +}; +static_assert(std::ranges::random_access_range<SmallerThanRange>); + +struct ForwardCommonView : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + using iterator = forward_iterator<int*>; + + constexpr iterator begin() const { return iterator(buffer_); } + constexpr iterator end() const { return iterator(buffer_ + size_); } +}; + +constexpr bool test() { + { + // Test a new-school iterator with operator<=>; the iterator should also have operator<=>. + using It = three_way_contiguous_iterator<int*>; + using SubRange = std::ranges::subrange<It>; + static_assert(std::three_way_comparable<It>); + using R = std::ranges::zip_view<SubRange, SubRange>; + static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>); + + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8, 9}; + auto r = std::views::zip(SubRange(It(a), It(a + 4)), SubRange(It(b), It(b + 5))); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); + + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); + } + + { + // Test an old-school iterator with no operator<=>; the transform iterator shouldn't have + // operator<=> either. + using It = random_access_iterator<int*>; + using SubRange = std::ranges::subrange<It>; + static_assert(!std::three_way_comparable<It>); + using R = std::ranges::zip_view<SubRange, SubRange>; + static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>); + + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8, 9}; + auto r = std::views::zip(SubRange(It(a), It(a + 4)), SubRange(It(b), It(b + 5))); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); + } + { + int buffer1[1] = {1}; + int buffer2[2] = {1, 2}; + + std::ranges::zip_view v{InputCommonView(buffer1), InputCommonView(buffer2)}; + using View = decltype(v); + static_assert(!std::ranges::forward_range<View>); + static_assert(std::ranges::input_range<View>); + static_assert(std::ranges::common_range<View>); + + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 != it2); + + ++it1; + assert(it1 == it2); + + using Iter = decltype(it1); + static_assert(!canSmallerThan<Iter>); + } + { + // in this case sentinel is computed by getting each underlying sentinel, so only one + // underlying iterator is comparing equal + int buffer1[1] = {1}; + int buffer2[2] = {1, 2}; + std::ranges::zip_view v{ForwardCommonView(buffer1), ForwardCommonView(buffer2)}; + using View = decltype(v); + static_assert(std::ranges::common_range<View>); + static_assert(!std::ranges::bidirectional_range<View>); + + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 != it2); + + ++it1; + // it1: <buffer1 + 1, buffer2 + 1> + // it2: <buffer1 + 1, buffer2 + 2> + assert(it1 == it2); + } + { + // only < and == are needed + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8, 9}; + auto r = std::views::zip(SmallerThanRange(a), SmallerThanRange(b)); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp @@ -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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// iterator() = default; + +#include <ranges> +#include <tuple> + +#include "../types.h" + +struct PODIter { + int i; // deliberately uninitialised + + using iterator_category = std::random_access_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + constexpr int operator*() const { return i; } + + constexpr PODIter& operator++() { return *this; } + constexpr void operator++(int) {} + + friend constexpr bool operator==(const PODIter&, const PODIter&) = default; +}; + +struct Default : std::ranges::view_base { + PODIter begin() const; + PODIter end() const; +}; + +struct NonDefault : std::ranges::view_base { + cpp20_input_iterator<int*> begin() const; + sentinel_wrapper<cpp20_input_iterator<int*>> end() const; +}; + +template <class... Views> +using zip_iter = std::ranges::iterator_t<std::ranges::zip_view<Views...>>; + +static_assert(!std::default_initializable<zip_iter<NonDefault>>); +static_assert(!std::default_initializable<zip_iter<NonDefault, Default>>); +static_assert(!std::default_initializable<zip_iter<NonDefault, NonDefault>>); +static_assert(std::default_initializable<zip_iter<Default>>); +static_assert(std::default_initializable<zip_iter<Default, Default>>); + +constexpr bool test() { + using ZipIter = std::ranges::iterator_t<std::ranges::zip_view<Default>>; + { + ZipIter iter; + auto [x] = *iter; + assert(x == 0); // PODIter has to be initialised to have value 0 + } + { + ZipIter iter = {}; + auto [x] = *iter; + assert(x == 0); // PODIter has to be initialised to have value 0 + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator(iterator<!Const> i); + +#include <ranges> + +#include <cassert> +#include <tuple> + +#include "../types.h" + +constexpr bool test() { + int buffer[3] = {1, 2, 3}; + + std::ranges::zip_view v(NonSimpleCommon{buffer}); + auto iter1 = v.begin(); + std::ranges::iterator_t<const decltype(v)> iter2 = iter1; + assert(iter1 == iter2); + + static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>); + + // We cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp @@ -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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator& operator--(); +// constexpr iterator operator--(int); + +#include <array> +#include <cassert> +#include <ranges> +#include <tuple> + +#include "../types.h" + +template <class Iter> +concept canDecrement = requires(Iter it) { --it; }; + +struct NonBidi : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + using iterator = forward_iterator<int*>; + constexpr iterator begin() const { return iterator(buffer_); } + constexpr sentinel_wrapper<iterator> end() const { return sentinel_wrapper<iterator>(iterator(buffer_ + size_)); } +}; + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{4.1, 3.2, 4.3}; + { + // all random access + std::ranges::zip_view v(a, b, std::views::iota(0, 5)); + auto it = v.end(); + using Iter = decltype(it); + + static_assert(std::is_same_v<decltype(--it), Iter&>); + auto& it_ref = --it; + assert(&it_ref == &it); + + assert(&(std::get<0>(*it)) == &(a[2])); + assert(&(std::get<1>(*it)) == &(b[2])); + assert(std::get<2>(*it) == 2); + + static_assert(std::is_same_v<decltype(it--), Iter>); + it--; + assert(&(std::get<0>(*it)) == &(a[1])); + assert(&(std::get<1>(*it)) == &(b[1])); + assert(std::get<2>(*it) == 1); + } + { + // all bidi+ + int buffer[2] = {1, 2}; + + std::ranges::zip_view v(BidiCommonView{buffer}, std::views::iota(0, 5)); + auto it = v.begin(); + using Iter = decltype(it); + + ++it; + ++it; + + static_assert(std::is_same_v<decltype(--it), Iter&>); + auto& it_ref = --it; + assert(&it_ref == &it); + + assert(it == ++v.begin()); + + static_assert(std::is_same_v<decltype(it--), Iter>); + auto tmp = it--; + assert(it == v.begin()); + assert(tmp == ++v.begin()); + } + { + // non bidi + int buffer[3] = {4, 5, 6}; + std::ranges::zip_view v(a, NonBidi{buffer}); + using Iter = std::ranges::iterator_t<decltype(v)>; + static_assert(!canDecrement<Iter>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto operator*() const; + +#include <array> +#include <cassert> +#include <ranges> +#include <tuple> + +#include "../types.h" + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{4.1, 3.2, 4.3}; + { + std::ranges::zip_view v(a); + auto it = v.begin(); + assert(&(std::get<0>(*it)) == &(a[0])); + static_assert(std::is_same_v<decltype(*it), std::tuple<int&>>); + } + { + // operator* is const + std::ranges::zip_view v(a); + const auto it = v.begin(); + assert(&(std::get<0>(*it)) == &(a[0])); + } + { + std::ranges::zip_view v(a, b); + auto it = v.begin(); + auto [x, y] = *it; + assert(&x == &(a[0])); + assert(&y == &(b[0])); + static_assert(std::is_same_v<decltype(*it), std::pair<int&, double&>>); + + x = 5; + y = 0.1; + assert(a[0] == 5); + assert(b[0] == 0.1); + } + { + std::ranges::zip_view v(a, b, std::views::iota(0, 5)); + auto it = v.begin(); + assert(&(std::get<0>(*it)) == &(a[0])); + assert(&(std::get<1>(*it)) == &(b[0])); + assert(std::get<2>(*it) == 0); + static_assert(std::is_same_v<decltype(*it), std::tuple<int&, double&, int>>); + } + { + std::ranges::zip_view v(a, std::as_const(a)); + auto it = v.begin(); + assert(&(std::get<0>(*it)) == &(a[0])); + assert(&(std::get<1>(*it)) == &(a[0])); + static_assert(std::is_same_v<decltype(*it), std::pair<int&, int const&>>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator& operator++(); +// constexpr void operator++(int); +// constexpr iterator operator++(int) requires all_forward<Const, Views...>; + +#include <array> +#include <cassert> +#include <ranges> +#include <tuple> + +#include "../types.h" + +struct InputRange : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + using iterator = cpp20_input_iterator<int*>; + constexpr iterator begin() const { return iterator(buffer_); } + constexpr sentinel_wrapper<iterator> end() const { return sentinel_wrapper<iterator>(iterator(buffer_ + size_)); } +}; + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{4.1, 3.2, 4.3}; + { + // random/contiguous + std::ranges::zip_view v(a, b, std::views::iota(0, 5)); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(std::get<0>(*it)) == &(a[0])); + assert(&(std::get<1>(*it)) == &(b[0])); + assert(std::get<2>(*it) == 0); + + static_assert(std::is_same_v<decltype(++it), Iter&>); + + auto& it_ref = ++it; + assert(&it_ref == &it); + + assert(&(std::get<0>(*it)) == &(a[1])); + assert(&(std::get<1>(*it)) == &(b[1])); + assert(std::get<2>(*it) == 1); + + static_assert(std::is_same_v<decltype(it++), Iter>); + auto original = it; + auto copy = it++; + assert(original == copy); + assert(&(std::get<0>(*it)) == &(a[2])); + assert(&(std::get<1>(*it)) == &(b[2])); + assert(std::get<2>(*it) == 2); + } + { + // bidi + int buffer[2] = {1, 2}; + + std::ranges::zip_view v(BidiCommonView{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(std::get<0>(*it)) == &(buffer[0])); + + static_assert(std::is_same_v<decltype(++it), Iter&>); + auto& it_ref = ++it; + assert(&it_ref == &it); + assert(&(std::get<0>(*it)) == &(buffer[1])); + + static_assert(std::is_same_v<decltype(it++), Iter>); + auto original = it; + auto copy = it++; + assert(copy == original); + assert(&(std::get<0>(*it)) == &(buffer[2])); + } + { + // forword + int buffer[2] = {1, 2}; + + std::ranges::zip_view v(ForwardSizedView{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(std::get<0>(*it)) == &(buffer[0])); + + static_assert(std::is_same_v<decltype(++it), Iter&>); + auto& it_ref = ++it; + assert(&it_ref == &it); + assert(&(std::get<0>(*it)) == &(buffer[1])); + + static_assert(std::is_same_v<decltype(it++), Iter>); + auto original = it; + auto copy = it++; + assert(copy == original); + assert(&(std::get<0>(*it)) == &(buffer[2])); + } + { + // all input+ + int buffer[3] = {4, 5, 6}; + std::ranges::zip_view v(a, InputRange{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(std::get<0>(*it)) == &(a[0])); + assert(&(std::get<1>(*it)) == &(buffer[0])); + + static_assert(std::is_same_v<decltype(++it), Iter&>); + auto& it_ref = ++it; + assert(&it_ref == &it); + assert(&(std::get<0>(*it)) == &(a[1])); + assert(&(std::get<1>(*it)) == &(buffer[1])); + + static_assert(std::is_same_v<decltype(it++), void>); + it++; + assert(&(std::get<0>(*it)) == &(a[2])); + assert(&(std::get<1>(*it)) == &(buffer[2])); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr decltype(auto) iter_move(const iterator& i); + +#include <array> +#include <cassert> +#include <iterator> +#include <ranges> +#include <tuple> + +#include "../types.h" + +struct ThrowingMove { + ThrowingMove() = default; + ThrowingMove(ThrowingMove&&){}; +}; + +constexpr bool test() { + { + std::array a1{1, 2, 3, 4}; + const std::array a2{3.0, 4.0}; + + std::ranges::zip_view v(a1, a2, std::views::iota(3L)); + assert(std::ranges::iter_move(v.begin()) == std::make_tuple(1, 3.0, 3L)); + static_assert(std::is_same_v<decltype(std::ranges::iter_move(v.begin())), std::tuple<int&&, const double&&, long>>); + + static_assert(noexcept(std::ranges::iter_move(std::declval<decltype(v.begin())>()))); + } + { + auto throwingMoveRange = + std::views::iota(0, 2) | std::views::transform([](auto) noexcept { return ThrowingMove{}; }); + std::ranges::zip_view v(throwingMoveRange); + static_assert(!noexcept(std::ranges::iter_move(std::declval<decltype(v.begin())>()))); + } + { + IterMoveSwapRange r1{}, r2{}; + assert(r1.iter_move_called_times == 0); + assert(r2.iter_move_called_times == 0); + std::ranges::zip_view v(r1, r2); + auto it = v.begin(); + { + [[maybe_unused]] auto&& i = std::ranges::iter_move(it); + assert(r1.iter_move_called_times == 1); + assert(r2.iter_move_called_times == 1); + } + { + [[maybe_unused]] auto&& i = std::ranges::iter_move(it); + assert(r1.iter_move_called_times == 2); + assert(r2.iter_move_called_times == 2); + } + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp @@ -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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr void iter_swap(const iterator& x, const iterator& y); + +#include <array> +#include <cassert> +#include <ranges> + +#include "../types.h" + +constexpr bool test() { + { + std::array a1{1, 2, 3, 4}; + std::array a2{0.1, 0.2, 0.3}; + std::ranges::zip_view v(a1, a2); + auto iter1 = v.begin(); + auto iter2 = ++v.begin(); + + std::ranges::iter_swap(iter1, iter2); + + assert(a1[0] == 2); + assert(a1[1] == 1); + assert(a2[0] == 0.2); + assert(a2[1] == 0.1); + + auto [x1, y1] = *iter1; + assert(&x1 == &a1[0]); + assert(&y1 == &a2[0]); + + auto [x2, y2] = *iter2; + assert(&x2 == &a1[1]); + assert(&y2 == &a2[1]); + + static_assert(noexcept(std::ranges::iter_swap(iter1, iter2))); + } + { + IterMoveSwapRange r1, r2; + assert(r1.iter_swap_called_times == 0); + assert(r2.iter_swap_called_times == 0); + + std::ranges::zip_view v{r1, r2}; + auto it1 = v.begin(); + auto it2 = std::ranges::next(it1, 3); + + std::ranges::iter_swap(it1, it2); + assert(r1.iter_swap_called_times == 2); + assert(r2.iter_swap_called_times == 2); + + std::ranges::iter_swap(it1, it2); + assert(r1.iter_swap_called_times == 4); + assert(r2.iter_swap_called_times == 4); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp @@ -0,0 +1,155 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// Iterator traits and member typedefs in zip_view::<iterator>. + +#include <array> +#include <ranges> +#include <tuple> + +#include "test_iterators.h" + +#include "../types.h" + +template <class T> +struct ForwardView : std::ranges::view_base { + forward_iterator<T*> begin() const; + sentinel_wrapper<forward_iterator<T*>> end() const; +}; + +template <class T> +struct InputView : std::ranges::view_base { + cpp17_input_iterator<T*> begin() const; + sentinel_wrapper<cpp17_input_iterator<T*>> end() const; +}; + +template <class T> +concept HasIterCategory = requires { typename T::iterator_category; }; + +template <class T> +struct diff_type_iter { + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = T; + + int operator*() const; + diff_type_iter& operator++(); + void operator++(int); + friend constexpr bool operator==(diff_type_iter, diff_type_iter) = default; +}; + +template <class T> +struct DiffTypeRange { + diff_type_iter<T> begin() const; + diff_type_iter<T> end() const; +}; + +struct Foo {}; +struct Bar {}; + +struct ConstVeryDifferentRange { + int* begin(); + int* end(); + + forward_iterator<double*> begin() const; + forward_iterator<double*> end() const; +}; + +void test() { + { + int buffer[4]; + std::ranges::zip_view v(buffer, buffer); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>); + static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>); + static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>); + static_assert(std::is_same_v<Iter::value_type, std::pair<int, int>>); + static_assert(HasIterCategory<Iter>); + } + { + int buffer[4]; + std::ranges::zip_view v(buffer, buffer, buffer); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>); + static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>); + static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>); + static_assert(std::is_same_v<Iter::value_type, std::tuple<int, int, int>>); + static_assert(HasIterCategory<Iter>); + } + { + using Iter = std::ranges::iterator_t<std::ranges::zip_view<ForwardView<int>>>; + + static_assert(std::is_same_v<Iter::iterator_concept, std::forward_iterator_tag>); + static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>); + static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>); + static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>); + static_assert(HasIterCategory<Iter>); + } + { + int buffer[4]; + std::ranges::zip_view v(buffer, buffer); + std::ranges::zip_view v2(buffer, v); + using Iter = decltype(v2.begin()); + + static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>); + static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>); + static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>); + static_assert(std::is_same_v<Iter::value_type, std::pair<int, std::pair<int, int>>>); + static_assert(HasIterCategory<Iter>); + } + { + using Iter = std::ranges::iterator_t<std::ranges::zip_view<InputView<int>>>; + + static_assert(std::is_same_v<Iter::iterator_concept, std::input_iterator_tag>); + static_assert(!HasIterCategory<Iter>); + static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>); + static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>); + } + { + std::ranges::zip_view v{DiffTypeRange<intptr_t>{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v<Iter::difference_type, intptr_t>); + } + { + std::ranges::zip_view v{DiffTypeRange<intptr_t>{}, DiffTypeRange<std::ptrdiff_t>{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v<Iter::difference_type, std::common_type_t<intptr_t, std::ptrdiff_t>>); + } + const std::array foos{Foo{}}; + std::array bars{Bar{}, Bar{}}; + { + std::ranges::zip_view v{foos}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v<Iter::value_type, std::tuple<Foo>>); + } + { + std::ranges::zip_view v{foos, bars}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v<Iter::value_type, std::pair<Foo, Bar>>); + } + { + std::ranges::zip_view v{ConstVeryDifferentRange{}}; + using Iter = decltype(v.begin()); + using ConstIter = decltype(std::as_const(v).begin()); + + static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>); + static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>); + static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>); + static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>); + + static_assert(std::is_same_v<ConstIter::iterator_concept, std::forward_iterator_tag>); + static_assert(std::is_same_v<ConstIter::iterator_category, std::input_iterator_tag>); + static_assert(std::is_same_v<ConstIter::difference_type, std::ptrdiff_t>); + static_assert(std::is_same_v<ConstIter::value_type, std::tuple<double>>); + } +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto operator[](difference_type __n) const requires +// all_random_access<_Const, _Views...> + +#include <ranges> +#include <cassert> + +#include "../types.h" + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0)); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v<decltype(it[2]), std::pair<int&, int>>); + } + { + std::ranges::zip_view v(ContiguousCommonView{buffer}, ContiguousCommonView{buffer}); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v<decltype(it[2]), std::pair<int&, int&>>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp @@ -0,0 +1,322 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// test if zip_view models input_range, forward_range, bidirectional_range, +// random_access_range, contiguous_range, common_range +// sized_range + +#include <cassert> +#include <concepts> +#include <ranges> +#include <tuple> +#include <utility> + +#include "types.h" + +void testConceptPair() { + int buffer1[2] = {1, 2}; + int buffer2[3] = {1, 2, 3}; + + { + std::ranges::zip_view v{ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{ContiguousNonCommonView{buffer1}, ContiguousNonCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{ContiguousNonCommonSized{buffer1}, ContiguousNonCommonSized{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{SizedRandomAccessView{buffer1}, ContiguousCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{SizedRandomAccessView{buffer1}, SizedRandomAccessView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + + { + std::ranges::zip_view v{NonSizedRandomAccessView{buffer1}, NonSizedRandomAccessView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{BidiCommonView{buffer1}, SizedRandomAccessView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::bidirectional_range<View>); + static_assert(!std::ranges::random_access_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{BidiCommonView{buffer1}, BidiCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::bidirectional_range<View>); + static_assert(!std::ranges::random_access_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{BidiCommonView{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::forward_range<View>); + static_assert(!std::ranges::bidirectional_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{BidiNonCommonView{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::forward_range<View>); + static_assert(!std::ranges::bidirectional_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{ForwardSizedView{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::forward_range<View>); + static_assert(!std::ranges::bidirectional_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{ForwardSizedNonCommon{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::forward_range<View>); + static_assert(!std::ranges::bidirectional_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{InputCommonView{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::input_range<View>); + static_assert(!std::ranges::forward_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{InputCommonView{buffer1}, InputCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::input_range<View>); + static_assert(!std::ranges::forward_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{InputNonCommonView{buffer1}, InputCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::input_range<View>); + static_assert(!std::ranges::forward_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } +} + +void testConceptTuple() { + + int buffer1[2] = {1, 2}; + int buffer2[3] = {1, 2, 3}; + int buffer3[4] = {1, 2, 3, 4}; + + // TODO: uncomment all the static_assert once [tuple.tuple] section in P2321R2 is implemented + // This is because convertible_to<tuple<int&,int&,int&>&, tuple<int,int,int>> is false without + // the above implementation, thus the zip iterator does not model indirectly_readable + + { + std::ranges::zip_view v{ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2}, + ContiguousCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{ContiguousNonCommonView{buffer1}, ContiguousNonCommonView{buffer2}, + ContiguousNonCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{ContiguousNonCommonSized{buffer1}, ContiguousNonCommonSized{buffer2}, + ContiguousNonCommonSized{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{SizedRandomAccessView{buffer1}, ContiguousCommonView{buffer2}, + ContiguousCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{SizedRandomAccessView{buffer1}, SizedRandomAccessView{buffer2}, + SizedRandomAccessView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + + { + std::ranges::zip_view v{NonSizedRandomAccessView{buffer1}, NonSizedRandomAccessView{buffer2}, + NonSizedRandomAccessView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range<View>); + static_assert(!std::ranges::contiguous_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{BidiCommonView{buffer1}, SizedRandomAccessView{buffer2}, SizedRandomAccessView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::bidirectional_range<View>); + static_assert(!std::ranges::random_access_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{BidiCommonView{buffer1}, BidiCommonView{buffer2}, BidiCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::bidirectional_range<View>); + static_assert(!std::ranges::random_access_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{BidiCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::forward_range<View>); + static_assert(!std::ranges::bidirectional_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{BidiNonCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::forward_range<View>); + static_assert(!std::ranges::bidirectional_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{ForwardSizedView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::forward_range<View>); + static_assert(!std::ranges::bidirectional_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{ForwardSizedNonCommon{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::forward_range<View>); + static_assert(!std::ranges::bidirectional_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{InputCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::input_range<View>); + static_assert(!std::ranges::forward_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{InputCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::input_range<View>); + static_assert(!std::ranges::forward_range<View>); + static_assert(std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } + { + std::ranges::zip_view v{InputNonCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::input_range<View>); + static_assert(!std::ranges::forward_range<View>); + static_assert(!std::ranges::common_range<View>); + static_assert(!std::ranges::sized_range<View>); + } +} + +struct OutputIter { + using It = int*; + It it; + struct proxy { + It it; + void operator=(int i) const; + }; + proxy operator*(); + + OutputIter& operator++(); + + OutputIter operator++(int); + + bool operator==(OutputIter other) const; + using difference_type = std::ptrdiff_t; +}; +static_assert(std::output_iterator<OutputIter, int>); + +struct OutputView : std::ranges::view_base { + OutputIter begin() const; + OutputIter end() const; +}; +static_assert(std::ranges::output_range<OutputView, int>); + +template <class... Ts> +concept zippable = requires { + typename std::ranges::zip_view<Ts...>; +}; + +// output_range is not supported +static_assert(!zippable<OutputView>); +static_assert(!zippable<SimpleCommon, OutputView>); +static_assert(zippable<SimpleCommon>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// sentinel() = default; + +#include <cassert> +#include <ranges> +#include <tuple> + +struct PODSentinel { + bool b; // deliberately uninitialised + + friend constexpr bool operator==(int*, const PODSentinel& s) { return s.b; } +}; + +struct Range : std::ranges::view_base { + int* begin() const; + PODSentinel end(); +}; + +constexpr bool test() { + { + using R = std::ranges::zip_view<Range>; + using Sentinel = std::ranges::sentinel_t<R>; + static_assert(!std::is_same_v<Sentinel, std::ranges::iterator_t<R>>); + + std::ranges::iterator_t<R> it; + + Sentinel s1; + assert(it != s1); // PODSentinel.b is initialised to false + + Sentinel s2 = {}; + assert(it != s2); // PODSentinel.b is initialised to false + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr sentinel(sentinel<!Const> s); + +#include <cassert> +#include <ranges> + +#include "../types.h" + +template <class T> +struct convertible_sentinel_wrapper { + explicit convertible_sentinel_wrapper() = default; + constexpr convertible_sentinel_wrapper(const T& it) : it_(it) {} + + template <class U> + requires std::convertible_to<const U&, T> + constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper<U>& other) : it_(other.it_) {} + + constexpr friend bool operator==(convertible_sentinel_wrapper const& self, const T& other) { + return self.it_ == other; + } + T it_; +}; + +struct NonSimpleNonCommonConveritbleView : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + constexpr int* begin() { return buffer_; } + constexpr const int* begin() const { return buffer_; } + constexpr convertible_sentinel_wrapper<int*> end() { return convertible_sentinel_wrapper<int*>(buffer_ + size_); } + constexpr convertible_sentinel_wrapper<const int*> end() const { + return convertible_sentinel_wrapper<const int*>(buffer_ + size_); + } +}; + +static_assert(!std::ranges::common_range<NonSimpleNonCommonConveritbleView>); +static_assert(std::ranges::random_access_range<NonSimpleNonCommonConveritbleView>); +static_assert(!std::ranges::sized_range<NonSimpleNonCommonConveritbleView>); +static_assert(std::convertible_to<std::ranges::sentinel_t<NonSimpleNonCommonConveritbleView>, + std::ranges::sentinel_t<NonSimpleNonCommonConveritbleView const>>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonConveritbleView>); + +constexpr bool test() { + int buffer1[4] = {1, 2, 3, 4}; + int buffer2[5] = {1, 2, 3, 4, 5}; + std::ranges::zip_view v{NonSimpleNonCommonConveritbleView(buffer1), NonSimpleNonCommonConveritbleView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + auto sent1 = v.end(); + std::ranges::sentinel_t<const decltype(v)> sent2 = sent1; + static_assert(!std::is_same_v<decltype(sent1), decltype(sent2)>); + + assert(v.begin() != sent2); + assert(std::as_const(v).begin() != sent2); + assert(v.begin() + 4 == sent2); + assert(std::as_const(v).begin() + 4 == sent2); + + // Cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from<decltype(sent1), decltype(sent2)>); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp @@ -0,0 +1,152 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template<bool OtherConst> +// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> +// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); + +#include <cassert> +#include <compare> +#include <ranges> +#include <tuple> + +#include "../types.h" + +using Iterator = random_access_iterator<int*>; +using ConstIterator = random_access_iterator<const int*>; + +template <bool Const> +struct ComparableSentinel { + + using Iter = std::conditional_t<Const, ConstIterator, Iterator>; + Iter iter_; + + explicit ComparableSentinel() = default; + constexpr explicit ComparableSentinel(const Iter& it) : iter_(it) {} + + constexpr friend bool operator==(const Iterator& i, const ComparableSentinel& s) { return base(i) == base(s.iter_); } + + constexpr friend bool operator==(const ConstIterator& i, const ComparableSentinel& s) { + return base(i) == base(s.iter_); + } +}; + +struct ComparableView : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + constexpr auto begin() { return Iterator(buffer_); } + constexpr auto begin() const { return ConstIterator(buffer_); } + constexpr auto end() { return ComparableSentinel<false>(Iterator(buffer_ + size_)); } + constexpr auto end() const { return ComparableSentinel<true>(ConstIterator(buffer_ + size_)); } +}; + +struct ConstIncompatibleView : std::ranges::view_base { + cpp17_input_iterator<int*> begin(); + forward_iterator<const int*> begin() const; + sentinel_wrapper<cpp17_input_iterator<int*>> end(); + sentinel_wrapper<forward_iterator<const int*>> end() const; +}; + +template <class Iter, class Sent> +concept EqualComparable = std::invocable < std::equal_to<>, +const Iter&, const Sent& > ; + +constexpr bool test() { + int buffer1[4] = {1, 2, 3, 4}; + int buffer2[5] = {1, 2, 3, 4, 5}; + int buffer3[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + std::ranges::zip_view v{SimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)}; + static_assert(!std::ranges::common_range<decltype(v)>); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<decltype(v)>); + + assert(v.begin() != v.end()); + assert(v.begin() + 1 != v.end()); + assert(v.begin() + 2 != v.end()); + assert(v.begin() + 3 != v.end()); + assert(v.begin() + 4 == v.end()); + + using Iter = std::ranges::iterator_t<decltype(v)>; + using Sentinel = std::ranges::sentinel_t<decltype(v)>; + static_assert(EqualComparable<Iter, Sentinel>); + } + { + std::ranges::zip_view v{NonSimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)}; + static_assert(!std::ranges::common_range<decltype(v)>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>); + + assert(v.begin() != v.end()); + assert(v.begin() + 4 == v.end()); + + // const_iterator (const int*) converted to iterator (int*) + assert(v.begin() + 4 == std::as_const(v).end()); + + using Iter = std::ranges::iterator_t<decltype(v)>; + using ConstIter = std::ranges::iterator_t<const decltype(v)>; + static_assert(!std::is_same_v<Iter, ConstIter>); + using Sentinel = std::ranges::sentinel_t<decltype(v)>; + using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>; + static_assert(!std::is_same_v<Sentinel, ConstSentinel>); + + static_assert(EqualComparable<Iter, Sentinel>); + static_assert(!EqualComparable<ConstIter, Sentinel>); + static_assert(EqualComparable<Iter, ConstSentinel>); + static_assert(EqualComparable<ConstIter, ConstSentinel>); + } + { + std::ranges::zip_view v{ComparableView(buffer1), ComparableView(buffer2)}; + static_assert(!std::ranges::common_range<decltype(v)>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>); + + assert(v.begin() != v.end()); + assert(v.begin() + 4 == v.end()); + assert(std::as_const(v).begin() + 4 == v.end()); + assert(std::as_const(v).begin() + 4 == std::as_const(v).end()); + assert(v.begin() + 4 == std::as_const(v).end()); + + using Iter = std::ranges::iterator_t<decltype(v)>; + using ConstIter = std::ranges::iterator_t<const decltype(v)>; + static_assert(!std::is_same_v<Iter, ConstIter>); + using Sentinel = std::ranges::sentinel_t<decltype(v)>; + using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>; + static_assert(!std::is_same_v<Sentinel, ConstSentinel>); + + static_assert(EqualComparable<Iter, Sentinel>); + static_assert(EqualComparable<ConstIter, Sentinel>); + static_assert(EqualComparable<Iter, ConstSentinel>); + static_assert(EqualComparable<ConstIter, ConstSentinel>); + } + { + std::ranges::zip_view v{ComparableView(buffer1), ConstIncompatibleView{}}; + static_assert(!std::ranges::common_range<decltype(v)>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>); + + using Iter = std::ranges::iterator_t<decltype(v)>; + using ConstIter = std::ranges::iterator_t<const decltype(v)>; + static_assert(!std::is_same_v<Iter, ConstIter>); + using Sentinel = std::ranges::sentinel_t<decltype(v)>; + using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>; + static_assert(!std::is_same_v<Sentinel, ConstSentinel>); + + static_assert(EqualComparable<Iter, Sentinel>); + static_assert(!EqualComparable<ConstIter, Sentinel>); + static_assert(!EqualComparable<Iter, ConstSentinel>); + static_assert(EqualComparable<ConstIter, ConstSentinel>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp @@ -0,0 +1,229 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template <bool OtherConst> +// requires(sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>, +// iterator_t<maybe-const<OtherConst, Views>>>&&...) +// friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>...> +// operator-(const iterator<OtherConst>&, const sentinel&) +// +// template <bool OtherConst> +// requires(sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>, +// iterator_t<maybe-const<OtherConst, Views>>>&&...) +// friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>...> +// operator-(const sentinel&, const iterator<OtherConst>&) + +#include <cassert> +#include <concepts> +#include <functional> +#include <ranges> +#include <tuple> + +#include "../types.h" + +template <class Base = int*> +struct convertible_forward_sized_iterator { + Base it_ = nullptr; + + using iterator_category = std::forward_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + convertible_forward_sized_iterator() = default; + constexpr convertible_forward_sized_iterator(Base it) : it_(it) {} + + template <std::convertible_to<Base> U> + constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator<U>& it) : it_(it.it_) {} + + constexpr decltype(*Base{}) operator*() const { return *it_; } + + constexpr convertible_forward_sized_iterator& operator++() { + ++it_; + return *this; + } + constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); } + + friend constexpr bool operator==(const convertible_forward_sized_iterator&, + const convertible_forward_sized_iterator&) = default; + + friend constexpr difference_type operator-(const convertible_forward_sized_iterator& x, + const convertible_forward_sized_iterator& y) { + return x.it_ - y.it_; + } +}; +static_assert(std::forward_iterator<convertible_forward_sized_iterator<>>); + +template <class Base> +struct convertible_sized_sentinel { + Base base_; + explicit convertible_sized_sentinel() = default; + constexpr convertible_sized_sentinel(const Base& it) : base_(it) {} + + template <std::convertible_to<Base> U> + constexpr convertible_sized_sentinel(const convertible_sized_sentinel<U>& other) : base_(other.base_) {} + + template <class U> + requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>) + friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) { + return s.base_ == base; + } + template <class U> + requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>) + friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) { + return s.base_ - i; + } + + template <class U> + requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>) + friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) { + return i - s.base_; + } +}; +static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<>>, + convertible_forward_sized_iterator<>>); +static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<const int*>>, + convertible_forward_sized_iterator<int*>>); +static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<int*>>, + convertible_forward_sized_iterator<const int*>>); + +struct ConstCompatibleForwardSized : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + using iterator = convertible_forward_sized_iterator<int*>; + using const_iterator = convertible_forward_sized_iterator<const int*>; + + constexpr iterator begin() { return {buffer_}; } + constexpr const_iterator begin() const { return {buffer_}; } + constexpr convertible_sized_sentinel<iterator> end() { return iterator{buffer_ + size_}; } + constexpr convertible_sized_sentinel<const_iterator> end() const { return const_iterator{buffer_ + size_}; } +}; + +template <class T, class U> +concept HasMinus = std::invocable < std::minus<>, +const T&, const U& > ; + +template <class T> +concept SentinelHasMinus = HasMinus < std::ranges::sentinel_t<T>, +std::ranges::iterator_t < T >> ; + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + + { + std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)}; + static_assert(!std::ranges::common_range<decltype(v)>); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<decltype(v)>); + + auto it = v.begin(); + auto st = v.end(); + assert(st - it == 5); + assert(st - std::ranges::next(it, 1) == 4); + + assert(it - st == -5); + assert(std::ranges::next(it, 1) - st == -4); + static_assert(SentinelHasMinus<decltype(v)>); + } + + { + std::ranges::zip_view v(std::views::iota(0, 3), ForwardSizedNonCommon(buffer1)); + static_assert(!std::ranges::common_range<decltype(v)>); + auto it = v.begin(); + auto st = v.end(); + assert(st - it == 3); + assert(st - std::ranges::next(it, 1) == 2); + + assert(it - st == -3); + assert(std::ranges::next(it, 1) - st == -2); + static_assert(SentinelHasMinus<decltype(v)>); + } + + { + std::ranges::zip_view v(std::views::iota(0), SizedRandomAccessView(buffer1)); + static_assert(!std::ranges::common_range<decltype(v)>); + static_assert(!SentinelHasMinus<decltype(v)>); + } + { + // const incompatible + std::ranges::zip_view v(NonSimpleForwardSizedNonCommon{buffer1}); + static_assert(!std::ranges::common_range<decltype(v)>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>); + + using Iter = std::ranges::iterator_t<decltype(v)>; + using ConstIter = std::ranges::iterator_t<const decltype(v)>; + static_assert(!std::is_same_v<Iter, ConstIter>); + using Sentinel = std::ranges::sentinel_t<decltype(v)>; + using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>; + static_assert(!std::is_same_v<Sentinel, ConstSentinel>); + + static_assert(HasMinus<Iter, Sentinel>); + static_assert(HasMinus<Sentinel, Iter>); + static_assert(HasMinus<ConstIter, ConstSentinel>); + static_assert(HasMinus<ConstSentinel, ConstIter>); + auto it = v.begin(); + auto const_it = std::as_const(v).begin(); + auto st = v.end(); + auto const_st = std::as_const(v).end(); + assert(it - st == -5); + assert(st - it == 5); + assert(const_it - const_st == -5); + assert(const_st - const_it == 5); + + static_assert(!HasMinus<Iter, ConstSentinel>); + static_assert(!HasMinus<ConstSentinel, Iter>); + static_assert(!HasMinus<ConstIter, Sentinel>); + static_assert(!HasMinus<Sentinel, ConstIter>); + } + + { + // const compatible allow non-const to const conversion + std::ranges::zip_view v(ConstCompatibleForwardSized{buffer1}); + static_assert(!std::ranges::common_range<decltype(v)>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>); + + using Iter = std::ranges::iterator_t<decltype(v)>; + using ConstIter = std::ranges::iterator_t<const decltype(v)>; + static_assert(!std::is_same_v<Iter, ConstIter>); + using Sentinel = std::ranges::sentinel_t<decltype(v)>; + using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>; + static_assert(!std::is_same_v<Sentinel, ConstSentinel>); + + static_assert(HasMinus<Iter, Sentinel>); + static_assert(HasMinus<Sentinel, Iter>); + static_assert(HasMinus<ConstIter, ConstSentinel>); + static_assert(HasMinus<ConstSentinel, ConstIter>); + static_assert(HasMinus<Iter, ConstSentinel>); + static_assert(HasMinus<ConstSentinel, Iter>); + static_assert(HasMinus<ConstIter, Sentinel>); + static_assert(HasMinus<Sentinel, ConstIter>); + + auto it = v.begin(); + auto const_it = std::as_const(v).begin(); + auto st = v.end(); + auto const_st = std::as_const(v).end(); + + assert(it - st == -5); + assert(st - it == 5); + assert(const_it - const_st == -5); + assert(const_st - const_it == 5); + assert(it - const_st == -5); + assert(const_st - it == 5); + assert(const_it - st == -5); + assert(st - const_it == 5); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto size() requires(sized_range<Views>&&...) +// constexpr auto size() const requires(sized_range<const Views>&&...) + +#include <ranges> + +#include <cassert> +#include <tuple> +#include <utility> + +#include "test_iterators.h" +#include "types.h" + +int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; +struct View : std::ranges::view_base { + std::size_t size_ = 0; + constexpr View(std::size_t s) : size_(s) {} + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + size_; } +}; + +struct SizedNonConst : std::ranges::view_base { + using iterator = forward_iterator<int*>; + std::size_t size_ = 0; + constexpr SizedNonConst(std::size_t s) : size_(s) {} + constexpr auto begin() const { return iterator{buffer}; } + constexpr auto end() const { return iterator{buffer + size_}; } + constexpr std::size_t size() { return size_; } +}; + +struct StrangeSizeView : std::ranges::view_base { + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + 8; } + + constexpr auto size() { return 5; } + constexpr auto size() const { return 6; } +}; + +constexpr bool test() { + { + std::ranges::zip_view v(View(8)); + assert(v.size() == 8); + assert(std::as_const(v).size() == 8); + } + { + std::ranges::zip_view v(View(2), View(3)); + assert(v.size() == 2); + assert(std::as_const(v).size() == 2); + } + { + std::ranges::zip_view v(std::views::iota(0, 500), View(3)); + assert(v.size() == 3); + assert(std::as_const(v).size() == 3); + } + { + std::ranges::zip_view v(SizedNonConst(2), View(3)); + assert(v.size() == 2); + static_assert(std::ranges::sized_range<decltype(v)>); + static_assert(!std::ranges::sized_range<decltype(std::as_const(v))>); + } + { + std::ranges::zip_view v(StrangeSizeView{}); + assert(v.size() == 5); + assert(std::as_const(v).size() == 6); + } + { + std::ranges::zip_view v(InputCommonView{buffer}); + static_assert(!std::ranges::sized_range<decltype(v)>); + static_assert(!std::ranges::sized_range<decltype(std::as_const(v))>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/types.h b/libcxx/test/std/ranges/range.adaptors/range.zip/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/types.h @@ -0,0 +1,467 @@ +//===----------------------------------------------------------------------===// +// +// 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_ZIP_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H + +#include <ranges> + +#include "test_macros.h" +#include "test_iterators.h" +#include "test_range.h" + +#if TEST_STD_VER <= 20 +# error "range.zip/types.h" can only be included in builds supporting C++20 +#endif // TEST_STD_VER <= 20 + +template <class T> +struct Buffer { + T* buffer_; + std::size_t size_; + + template <std::size_t N> + constexpr Buffer(T (&b)[N]) : buffer_(b), size_(N) {} +}; + +using IntBuffer = Buffer<int>; + +template <bool Simple> +struct Common : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + constexpr int* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr int* end() + requires(!Simple) + { + return buffer_ + size_; + } + constexpr const int* end() const { return buffer_ + size_; } +}; +using SimpleCommon = Common<true>; +using NonSimpleCommon = Common<false>; + +using SimpleCommonRandomAccessSized = SimpleCommon; +using NonSimpleCommonRandomAccessSized = NonSimpleCommon; + +static_assert(std::ranges::common_range<Common<true>>); +static_assert(std::ranges::random_access_range<SimpleCommon>); +static_assert(std::ranges::sized_range<SimpleCommon>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleCommon>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleCommon>); + +template <bool Simple> +struct CommonNonRandom : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + using const_iterator = forward_iterator<const int*>; + using iterator = forward_iterator<int*>; + constexpr iterator begin() + requires(!Simple) + { + return iterator(buffer_); + } + constexpr const_iterator begin() const { return const_iterator(buffer_); } + constexpr iterator end() + requires(!Simple) + { + return iterator(buffer_ + size_); + } + constexpr const_iterator end() const { return const_iterator(buffer_ + size_); } +}; + +using SimpleCommonNonRandom = CommonNonRandom<true>; +using NonSimpleCommonNonRandom = CommonNonRandom<false>; + +static_assert(std::ranges::common_range<SimpleCommonNonRandom>); +static_assert(!std::ranges::random_access_range<SimpleCommonNonRandom>); +static_assert(!std::ranges::sized_range<SimpleCommonNonRandom>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleCommonNonRandom>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleCommonNonRandom>); + +template <bool Simple> +struct NonCommon : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + constexpr int* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper<int*> end() + requires(!Simple) + { + return sentinel_wrapper<int*>(buffer_ + size_); + } + constexpr sentinel_wrapper<const int*> end() const { return sentinel_wrapper<const int*>(buffer_ + size_); } +}; + +using SimpleNonCommon = NonCommon<true>; +using NonSimpleNonCommon = NonCommon<false>; + +static_assert(!std::ranges::common_range<SimpleNonCommon>); +static_assert(std::ranges::random_access_range<SimpleNonCommon>); +static_assert(!std::ranges::sized_range<SimpleNonCommon>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommon>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommon>); + +template <bool Simple> +struct NonCommonSized : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + constexpr int* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper<int*> end() + requires(!Simple) + { + return sentinel_wrapper<int*>(buffer_ + size_); + } + constexpr sentinel_wrapper<const int*> end() const { return sentinel_wrapper<const int*>(buffer_ + size_); } + constexpr std::size_t size() const { return size_; } +}; + +using SimpleNonCommonSized = NonCommonSized<true>; +using SimpleNonCommonRandomAcessSized = SimpleNonCommonSized; +using NonSimpleNonCommonSized = NonCommonSized<false>; +using NonSimpleNonCommonRandomAcessSized = NonSimpleNonCommonSized; + +static_assert(!std::ranges::common_range<SimpleNonCommonSized>); +static_assert(std::ranges::random_access_range<SimpleNonCommonSized>); +static_assert(std::ranges::sized_range<SimpleNonCommonSized>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommonSized>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonSized>); + +template <bool Simple> +struct NonCommonNonRandom : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + using const_iterator = forward_iterator<const int*>; + using iterator = forward_iterator<int*>; + + constexpr iterator begin() + requires(!Simple) + { + return iterator(buffer_); + } + constexpr const_iterator begin() const { return const_iterator(buffer_); } + constexpr sentinel_wrapper<iterator> end() + requires(!Simple) + { + return sentinel_wrapper<iterator>(iterator(buffer_ + size_)); + } + constexpr sentinel_wrapper<const_iterator> end() const { + return sentinel_wrapper<const_iterator>(const_iterator(buffer_ + size_)); + } +}; + +using SimpleNonCommonNonRandom = NonCommonNonRandom<true>; +using NonSimpleNonCommonNonRandom = NonCommonNonRandom<false>; + +static_assert(!std::ranges::common_range<SimpleNonCommonNonRandom>); +static_assert(!std::ranges::random_access_range<SimpleNonCommonNonRandom>); +static_assert(!std::ranges::sized_range<SimpleNonCommonNonRandom>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommonNonRandom>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonNonRandom>); + +template <class Iter, class Sent = Iter, class NonConstIter = Iter, class NonConstSent = Sent> +struct BasicView : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + constexpr NonConstIter begin() + requires(!std::is_same_v<Iter, NonConstIter>) + { + return NonConstIter(buffer_); + } + constexpr Iter begin() const { return Iter(buffer_); } + + constexpr NonConstSent end() + requires(!std::is_same_v<Sent, NonConstSent>) + { + if constexpr (std::is_same_v<NonConstIter, NonConstSent>) { + return NonConstIter(buffer_ + size_); + } else { + return NonConstSent(NonConstIter(buffer_ + size_)); + } + } + + constexpr Sent end() const { + if constexpr (std::is_same_v<Iter, Sent>) { + return Iter(buffer_ + size_); + } else { + return Sent(Iter(buffer_ + size_)); + } + } +}; + +template <class Base = int*> +struct forward_sized_iterator { + Base it_ = nullptr; + + using iterator_category = std::forward_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + using pointer = Base; + using reference = decltype(*Base{}); + + forward_sized_iterator() = default; + constexpr forward_sized_iterator(Base it) : it_(it) {} + + constexpr reference operator*() const { return *it_; } + + constexpr forward_sized_iterator& operator++() { + ++it_; + return *this; + } + constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); } + + friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default; + + friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) { + return x.it_ - y.it_; + } +}; +static_assert(std::forward_iterator<forward_sized_iterator<>>); +static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>); + +using ForwardSizedView = BasicView<forward_sized_iterator<>>; +static_assert(std::ranges::forward_range<ForwardSizedView>); +static_assert(std::ranges::sized_range<ForwardSizedView>); +static_assert(std::ranges::common_range<ForwardSizedView>); +static_assert(!std::ranges::random_access_range<ForwardSizedView>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<ForwardSizedView>); + +using NonSimpleForwardSizedView = BasicView<forward_sized_iterator<const int*>, forward_sized_iterator<const int*>, + forward_sized_iterator<int*>, forward_sized_iterator<int*>>; +static_assert(std::ranges::forward_range<NonSimpleForwardSizedView>); +static_assert(std::ranges::sized_range<NonSimpleForwardSizedView>); +static_assert(std::ranges::common_range<NonSimpleForwardSizedView>); +static_assert(!std::ranges::random_access_range<NonSimpleForwardSizedView>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleForwardSizedView>); + +using ForwardSizedNonCommon = BasicView<forward_sized_iterator<>, sized_sentinel<forward_sized_iterator<>>>; +static_assert(std::ranges::forward_range<ForwardSizedNonCommon>); +static_assert(std::ranges::sized_range<ForwardSizedNonCommon>); +static_assert(!std::ranges::common_range<ForwardSizedNonCommon>); +static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<ForwardSizedNonCommon>); + +using NonSimpleForwardSizedNonCommon = + BasicView<forward_sized_iterator<const int*>, sized_sentinel<forward_sized_iterator<const int*>>, + forward_sized_iterator<int*>, sized_sentinel<forward_sized_iterator<int*>>>; +static_assert(std::ranges::forward_range<NonSimpleForwardSizedNonCommon>); +static_assert(std::ranges::sized_range<NonSimpleForwardSizedNonCommon>); +static_assert(!std::ranges::common_range<NonSimpleForwardSizedNonCommon>); +static_assert(!std::ranges::random_access_range<NonSimpleForwardSizedNonCommon>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleForwardSizedNonCommon>); + +struct SizedRandomAccessView : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + using iterator = random_access_iterator<int*>; + + constexpr auto begin() const { return iterator(buffer_); } + constexpr auto end() const { return sized_sentinel<iterator>(iterator(buffer_ + size_)); } + + constexpr decltype(auto) operator[](std::size_t n) const { return *(begin() + n); } +}; +static_assert(std::ranges::view<SizedRandomAccessView>); +static_assert(std::ranges::random_access_range<SizedRandomAccessView>); +static_assert(std::ranges::sized_range<SizedRandomAccessView>); + +using NonSizedRandomAccessView = + BasicView<random_access_iterator<int*>, sentinel_wrapper<random_access_iterator<int*>>>; +static_assert(!std::ranges::contiguous_range<NonSizedRandomAccessView>); +static_assert(std::ranges::random_access_range<SizedRandomAccessView>); +static_assert(!std::ranges::common_range<NonSizedRandomAccessView>); +static_assert(!std::ranges::sized_range<NonSizedRandomAccessView>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<NonSizedRandomAccessView>); + +using NonSimpleNonSizedRandomAccessView = + BasicView<random_access_iterator<const int*>, sentinel_wrapper<random_access_iterator<const int*>>, + random_access_iterator<int*>, sentinel_wrapper<random_access_iterator<int*>> >; +static_assert(!std::ranges::contiguous_range<NonSimpleNonSizedRandomAccessView>); +static_assert(std::ranges::random_access_range<NonSimpleNonSizedRandomAccessView>); +static_assert(!std::ranges::common_range<NonSimpleNonSizedRandomAccessView>); +static_assert(!std::ranges::sized_range<NonSimpleNonSizedRandomAccessView>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonSizedRandomAccessView>); + +using ContiguousCommonView = BasicView<int*>; +static_assert(std::ranges::contiguous_range<ContiguousCommonView>); +static_assert(std::ranges::common_range<ContiguousCommonView>); +static_assert(std::ranges::sized_range<ContiguousCommonView>); + +using ContiguousNonCommonView = BasicView<int*, sentinel_wrapper<int*>>; +static_assert(std::ranges::contiguous_range<ContiguousNonCommonView>); +static_assert(!std::ranges::common_range<ContiguousNonCommonView>); +static_assert(!std::ranges::sized_range<ContiguousNonCommonView>); + +using ContiguousNonCommonSized = BasicView<int*, sized_sentinel<int*>>; +; +static_assert(std::ranges::contiguous_range<ContiguousNonCommonSized>); +static_assert(!std::ranges::common_range<ContiguousNonCommonSized>); +static_assert(std::ranges::sized_range<ContiguousNonCommonSized>); + +template <class Base = int*> +struct common_input_iterator { + Base it_; + + using value_type = int; + using difference_type = std::intptr_t; + using iterator_concept = std::input_iterator_tag; + + constexpr common_input_iterator() = default; + constexpr explicit common_input_iterator(Base it) : it_(it) {} + + constexpr common_input_iterator& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } + + constexpr int& operator*() const { return *it_; } + + friend constexpr bool operator==(common_input_iterator const&, common_input_iterator const&) = default; +}; + +using InputCommonView = BasicView<common_input_iterator<>>; +static_assert(std::ranges::input_range<InputCommonView>); +static_assert(!std::ranges::forward_range<InputCommonView>); +static_assert(std::ranges::common_range<InputCommonView>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<InputCommonView>); + +using NonSimpleInputCommonView = BasicView<common_input_iterator<const int*>, common_input_iterator<const int*>, + common_input_iterator<int*>, common_input_iterator<int*>>; +static_assert(std::ranges::input_range<NonSimpleInputCommonView>); +static_assert(!std::ranges::forward_range<NonSimpleInputCommonView>); +static_assert(std::ranges::common_range<NonSimpleInputCommonView>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleInputCommonView>); + +using InputNonCommonView = BasicView<common_input_iterator<>, sentinel_wrapper<common_input_iterator<>>>; +static_assert(std::ranges::input_range<InputNonCommonView>); +static_assert(!std::ranges::forward_range<InputNonCommonView>); +static_assert(!std::ranges::common_range<InputNonCommonView>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<InputNonCommonView>); + +using NonSimpleInputNonCommonView = + BasicView<common_input_iterator<const int*>, sentinel_wrapper<common_input_iterator<const int*>>, + common_input_iterator<int*>, sentinel_wrapper<common_input_iterator<int*>>>; +static_assert(std::ranges::input_range<InputNonCommonView>); +static_assert(!std::ranges::forward_range<InputNonCommonView>); +static_assert(!std::ranges::common_range<InputNonCommonView>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleInputNonCommonView>); + +using BidiCommonView = BasicView<bidirectional_iterator<int*>>; +static_assert(!std::ranges::sized_range<BidiCommonView>); +static_assert(std::ranges::bidirectional_range<BidiCommonView>); +static_assert(!std::ranges::random_access_range<BidiCommonView>); +static_assert(std::ranges::common_range<BidiCommonView>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<BidiCommonView>); + +using NonSimpleBidiCommonView = BasicView<bidirectional_iterator<const int*>, bidirectional_iterator<const int*>, + bidirectional_iterator<int*>, bidirectional_iterator<int*>>; +static_assert(!std::ranges::sized_range<NonSimpleBidiCommonView>); +static_assert(std::ranges::bidirectional_range<NonSimpleBidiCommonView>); +static_assert(!std::ranges::random_access_range<NonSimpleBidiCommonView>); +static_assert(std::ranges::common_range<NonSimpleBidiCommonView>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleBidiCommonView>); + +struct SizedBidiCommon : BidiCommonView { + using BidiCommonView::BidiCommonView; + std::size_t size() const { return base(end()) - base(begin()); } +}; +static_assert(std::ranges::sized_range<SizedBidiCommon>); +static_assert(std::ranges::bidirectional_range<SizedBidiCommon>); +static_assert(!std::ranges::random_access_range<SizedBidiCommon>); +static_assert(std::ranges::common_range<SizedBidiCommon>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SizedBidiCommon>); + +struct NonSimpleSizedBidiCommon : NonSimpleBidiCommonView { + using NonSimpleBidiCommonView::NonSimpleBidiCommonView; + std::size_t size() const { return base(end()) - base(begin()); } +}; +static_assert(std::ranges::sized_range<NonSimpleSizedBidiCommon>); +static_assert(std::ranges::bidirectional_range<NonSimpleSizedBidiCommon>); +static_assert(!std::ranges::random_access_range<NonSimpleSizedBidiCommon>); +static_assert(std::ranges::common_range<NonSimpleSizedBidiCommon>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleSizedBidiCommon>); + +using BidiNonCommonView = BasicView<bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>; +static_assert(!std::ranges::sized_range<BidiNonCommonView>); +static_assert(std::ranges::bidirectional_range<BidiNonCommonView>); +static_assert(!std::ranges::random_access_range<BidiNonCommonView>); +static_assert(!std::ranges::common_range<BidiNonCommonView>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<BidiNonCommonView>); + +using NonSimpleBidiNonCommonView = + BasicView<bidirectional_iterator<const int*>, sentinel_wrapper<bidirectional_iterator<const int*>>, + bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>; +static_assert(!std::ranges::sized_range<NonSimpleBidiNonCommonView>); +static_assert(std::ranges::bidirectional_range<NonSimpleBidiNonCommonView>); +static_assert(!std::ranges::random_access_range<NonSimpleBidiNonCommonView>); +static_assert(!std::ranges::common_range<NonSimpleBidiNonCommonView>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleBidiNonCommonView>); + +using SizedBidiNonCommonView = BasicView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>; +static_assert(std::ranges::sized_range<SizedBidiNonCommonView>); +static_assert(std::ranges::bidirectional_range<SizedBidiNonCommonView>); +static_assert(!std::ranges::random_access_range<SizedBidiNonCommonView>); +static_assert(!std::ranges::common_range<SizedBidiNonCommonView>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SizedBidiNonCommonView>); + +using NonSimpleSizedBidiNonCommonView = + BasicView<bidirectional_iterator<const int*>, sized_sentinel<bidirectional_iterator<const int*>>, + bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>; +static_assert(std::ranges::sized_range<NonSimpleSizedBidiNonCommonView>); +static_assert(std::ranges::bidirectional_range<NonSimpleSizedBidiNonCommonView>); +static_assert(!std::ranges::random_access_range<NonSimpleSizedBidiNonCommonView>); +static_assert(!std::ranges::common_range<NonSimpleSizedBidiNonCommonView>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleSizedBidiNonCommonView>); + +struct iter_move_swap_iterator { + + std::reference_wrapper<int> iter_move_called_times; + std::reference_wrapper<int> iter_swap_called_times; + int i = 0; + + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + constexpr int operator*() const { return i; } + + constexpr iter_move_swap_iterator& operator++() { + ++i; + return *this; + } + constexpr void operator++(int) { ++i; } + + friend constexpr bool operator==(const iter_move_swap_iterator& x, std::default_sentinel_t) { return x.i == 5; } + + friend constexpr int iter_move(iter_move_swap_iterator const& it) { + ++it.iter_move_called_times; + return it.i; + } + friend constexpr void iter_swap(iter_move_swap_iterator const& x, iter_move_swap_iterator const& y) { + ++x.iter_swap_called_times; + ++y.iter_swap_called_times; + } +}; + +struct IterMoveSwapRange { + int iter_move_called_times = 0; + int iter_swap_called_times = 0; + constexpr auto begin() { return iter_move_swap_iterator{iter_move_called_times, iter_swap_called_times}; } + constexpr auto end() const { return std::default_sentinel; } +}; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H