From 2f28e1db535bddca42d0954b365a4e5f17bdf534 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Sat, 25 Apr 2026 06:51:37 -0400 Subject: [PATCH] [libc++] Implement P1899 `ranges::stride_view` (#65200) Implement `ranges::stride_view` in libc++. This PR was migrated from Phabricator (https://reviews.llvm.org/D156924). Closes #105198 Co-authored-by: Louis Dionne Co-authored-by: A. Jiang --- libcxx/docs/FeatureTestMacroTable.rst | 2 + libcxx/docs/ReleaseNotes/23.rst | 1 + libcxx/docs/Status/Cxx23Papers.csv | 2 +- libcxx/include/CMakeLists.txt | 1 + libcxx/include/__ranges/stride_view.h | 410 ++++++++++++++++++ libcxx/include/module.modulemap.in | 4 + libcxx/include/ranges | 12 + libcxx/include/version | 2 + libcxx/modules/std/ranges.inc | 2 +- .../range.stride.view/ctor.assert.pass.cpp | 27 ++ .../iterator/dereference.assert.pass.cpp | 41 ++ .../iterator/increment.assert.pass.cpp | 64 +++ .../iterator/iterator.nodiscard.verify.cpp | 45 ++ .../operator_plus_equal.assert.pass.cpp | 26 ++ .../range.stride.view/nodiscard.verify.cpp | 34 ++ .../ranges.version.compile.pass.cpp | 30 ++ .../version.version.compile.pass.cpp | 30 ++ .../range.stride.view/adaptor.pass.cpp | 118 +++++ .../range.stride.view/base.pass.cpp | 107 +++++ .../range.stride.view/begin.pass.cpp | 115 +++++ .../borrowing.compile.pass.cpp | 19 + .../concept.compile.pass.cpp | 48 ++ .../range.stride.view/ctad.pass.cpp | 103 +++++ .../range.stride.view/ctor.pass.cpp | 72 +++ .../range.stride.view/end.pass.cpp | 222 ++++++++++ .../range.stride.view/iterator/base.pass.cpp | 99 +++++ .../iterator/compare.pass.cpp | 122 ++++++ .../iterator/ctor.copy.pass.cpp | 165 +++++++ .../iterator/ctor.default.pass.cpp | 50 +++ .../iterator/decrement.pass.cpp | 159 +++++++ .../iterator/dereference.pass.cpp | 101 +++++ .../range.stride.view/iterator/equal.pass.cpp | 110 +++++ .../iterator/increment.pass.cpp | 211 +++++++++ .../iterator/iter_move.pass.cpp | 111 +++++ .../iterator/iter_swap.pass.cpp | 92 ++++ .../range.stride.view/iterator/minus.pass.cpp | 194 +++++++++ .../iterator/minus_equal.pass.cpp | 88 ++++ .../range.stride.view/iterator/plus.pass.cpp | 102 +++++ .../iterator/plus_equal.pass.cpp | 134 ++++++ .../iterator/subscript.pass.cpp | 107 +++++ .../iterator/types.compile.pass.cpp | 140 ++++++ .../range.stride.view/size.pass.cpp | 77 ++++ .../range.stride.view/stride.pass.cpp | 37 ++ .../range.adaptors/range.stride.view/types.h | 290 +++++++++++++ .../generate_feature_test_macro_components.py | 5 + 45 files changed, 3929 insertions(+), 2 deletions(-) create mode 100644 libcxx/include/__ranges/stride_view.h create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/types.compile.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 5f7d90e8eca5..1ee5ec7f72ee 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -390,6 +390,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_starts_ends_with`` ``202106L`` ---------------------------------------------------------- ----------------- + ``__cpp_lib_ranges_stride`` ``202207L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_to_container`` ``202202L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_zip`` ``202110L`` diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst index 0160ea2179cc..8e9b1e749d61 100644 --- a/libcxx/docs/ReleaseNotes/23.rst +++ b/libcxx/docs/ReleaseNotes/23.rst @@ -39,6 +39,7 @@ Implemented Papers ------------------ - P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github `__) +- P1899R3: ``stride_view`` (`Github `__) - P3936R1: Safer ``atomic_ref::address`` (`Github `__) - P3953R3: Rename ``std::runtime_format`` (`Github `__) - P4052R0: Renaming saturation arithmetic functions (`Github `__) diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index 80ed7dba3ead..41b1b8f884ef 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -58,7 +58,7 @@ "`P1223R5 `__","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","2022-07 (Virtual)","|Complete|","19","`#105194 `__","" "`P1467R9 `__","Extended ``floating-point`` types and standard names","2022-07 (Virtual)","","","`#105196 `__","" "`P1642R11 `__","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","2022-07 (Virtual)","","","`#105197 `__","" -"`P1899R3 `__","``stride_view``","2022-07 (Virtual)","","","`#105198 `__","" +"`P1899R3 `__","``stride_view``","2022-07 (Virtual)","|Complete|","23","`#105198 `__","" "`P2093R14 `__","Formatted output","2022-07 (Virtual)","|Complete|","18","`#105199 `__","" "`P2165R4 `__","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","2022-07 (Virtual)","|Partial|","","`#105200 `__","Only the part for ``zip_view`` is implemented." "`P2278R4 `__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","","","`#105201 `__","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 95d822ee8c0b..69a6590d18f8 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -751,6 +751,7 @@ set(files __ranges/single_view.h __ranges/size.h __ranges/split_view.h + __ranges/stride_view.h __ranges/subrange.h __ranges/take_view.h __ranges/take_while_view.h diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h new file mode 100644 index 000000000000..780bb25743c1 --- /dev/null +++ b/libcxx/include/__ranges/stride_view.h @@ -0,0 +1,410 @@ +// -*- 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_STRIDE_VIEW_H +#define _LIBCPP___RANGES_STRIDE_VIEW_H + +#include <__assert> +#include <__compare/three_way_comparable.h> +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/derived_from.h> +#include <__concepts/equality_comparable.h> +#include <__config> +#include <__functional/bind_back.h> +#include <__iterator/advance.h> +#include <__iterator/concepts.h> +#include <__iterator/default_sentinel.h> +#include <__iterator/distance.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/enable_borrowed_range.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/view_interface.h> +#include <__type_traits/make_unsigned.h> + +#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 >= 23 + +namespace ranges { + +template +_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) { + _Value __r = __left / __right; + if (__left % __right) { + ++__r; + } + return __r; +} + +template + requires view<_View> +class stride_view : public view_interface> { + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + range_difference_t<_View> __stride_ = 0; + + template + class __iterator; + +public: + _LIBCPP_HIDE_FROM_ABI constexpr explicit stride_view(_View __base, range_difference_t<_View> __stride) + : __base_(std::move(__base)), __stride_(__stride) { + _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__stride > 0, "The value of stride must be greater than 0"); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_difference_t<_View> stride() const noexcept { return __stride_; } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return __iterator(this, ranges::begin(__base_), 0); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range + { + return __iterator(this, ranges::begin(__base_), 0); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + if constexpr (common_range<_View> && sized_range<_View> && forward_range<_View>) { + auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_; + return __iterator(this, ranges::end(__base_), __missing); + } else if constexpr (common_range<_View> && !bidirectional_range<_View>) { + return __iterator(this, ranges::end(__base_), 0); + } else { + return default_sentinel; + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires(range) + { + if constexpr (common_range && sized_range && forward_range) { + auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_; + return __iterator(this, ranges::end(__base_), __missing); + } else if constexpr (common_range && !bidirectional_range) { + return __iterator(this, ranges::end(__base_), 0); + } else { + return default_sentinel; + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_)); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_)); + } +}; // class stride_view + +template +stride_view(_Range&&, range_difference_t<_Range>) -> stride_view>; + +template +struct __stride_iterator_category {}; + +template +struct __stride_iterator_category<_View> { + using _Cat _LIBCPP_NODEBUG = typename iterator_traits>::iterator_category; + using iterator_category = + _If, + /* then */ random_access_iterator_tag, + /* else */ _Cat >; +}; + +template + requires view<_View> +template +class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe_const<_Const, _View>> { + using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, stride_view<_View>>; + using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; + + _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_Base> __current_ = iterator_t<_Base>(); + _LIBCPP_NO_UNIQUE_ADDRESS ranges::sentinel_t<_Base> __end_ = ranges::sentinel_t<_Base>(); + range_difference_t<_Base> __stride_ = 0; + range_difference_t<_Base> __missing_ = 0; + + friend stride_view; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator( + _Parent* __parent, ranges::iterator_t<_Base> __current, range_difference_t<_Base> __missing) + : __current_(std::move(__current)), + __end_(ranges::end(__parent->__base_)), + __stride_(__parent->__stride_), + __missing_(__missing) {} + + static consteval auto __get_stride_view_iterator_concept() { + if constexpr (random_access_range<_Base>) { + return random_access_iterator_tag{}; + } else if constexpr (bidirectional_range<_Base>) { + return bidirectional_iterator_tag{}; + } else if constexpr (forward_range<_Base>) { + return forward_iterator_tag{}; + } else { + return input_iterator_tag{}; + } + } + +public: + using difference_type = range_difference_t<_Base>; + using value_type = range_value_t<_Base>; + using iterator_concept = decltype(__get_stride_view_iterator_concept()); + // using iterator_category = inherited; + + _LIBCPP_HIDE_FROM_ABI __iterator() + requires default_initializable> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator __i) + requires _Const && convertible_to, iterator_t<_Base>> && + convertible_to, sentinel_t<_Base>> + : __current_(std::move(__i.__current_)), + __end_(std::move(__i.__end_)), + __stride_(__i.__stride_), + __missing_(__i.__missing_) {} + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> const& base() const& noexcept { return __current_; } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot dereference an iterator at the end."); + return *__current_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot increment an iterator already at the end."); + __missing_ = ranges::advance(__current_, __stride_, __end_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot increment an iterator already at the end."); + ++*this; + } + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) + requires forward_range<_Base> + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot increment an iterator already at the end."); + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() + requires bidirectional_range<_Base> + { + ranges::advance(__current_, __missing_ - __stride_); + __missing_ = 0; + return *this; + } + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) + requires bidirectional_range<_Base> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __n) + requires random_access_range<_Base> + { + if (__n > 0) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(ranges::distance(__current_, __end_) > __stride_ * (__n - 1), + "Advancing the iterator beyond the end is not allowed."); + ranges::advance(__current_, __stride_ * (__n - 1)); + __missing_ = ranges::advance(__current_, __stride_, __end_); + + } else if (__n < 0) { + ranges::advance(__current_, __stride_ * __n + __missing_); + __missing_ = 0; + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __n) + requires random_access_range<_Base> + { + return *this += -__n; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const + requires random_access_range<_Base> + { + return *(*this + __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, default_sentinel_t) { + return __x.__current_ == __x.__end_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __iterator const& __y) + requires equality_comparable> + { + return __x.__current_ == __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> + { + return __x.__current_ < __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> + { + return __y < __x; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> + { + return !(__y < __x); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> + { + return !(__x < __y); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> && three_way_comparable> + { + return __x.__current_ <=> __y.__current_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(__iterator const& __i, difference_type __s) + requires random_access_range<_Base> + { + auto __r = __i; + __r += __s; + return __r; + } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __s, __iterator const& __i) + requires random_access_range<_Base> + { + auto __r = __i; + __r += __s; + return __r; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(__iterator const& __i, difference_type __s) + requires random_access_range<_Base> + { + auto __r = __i; + __r -= __s; + return __r; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(__iterator const& __x, __iterator const& __y) + requires sized_sentinel_for, iterator_t<_Base>> + { + if constexpr (forward_range<_Base>) { + auto __n = __x.__current_ - __y.__current_; + return (__n + __x.__missing_ - __y.__missing_) / __x.__stride_; + } + auto __n = __x.__current_ - __y.__current_; + if (__n < 0) { + return -ranges::__div_ceil(-__n, __x.__stride_); + } + return ranges::__div_ceil(__n, __x.__stride_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(default_sentinel_t, __iterator const& __x) + requires sized_sentinel_for, iterator_t<_Base>> + { + return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_); + } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(__iterator const& __x, default_sentinel_t __y) + requires sized_sentinel_for, iterator_t<_Base>> + { + return -(__y - __x); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_Base> + iter_move(__iterator const& __it) noexcept(noexcept(ranges::iter_move(__it.__current_))) { + return ranges::iter_move(__it.__current_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr void + iter_swap(__iterator const& __x, + __iterator const& __y) noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_))) + requires indirectly_swappable> + { + return ranges::iter_swap(__x.__current_, __y.__current_); + } +}; // class stride_view::__iterator + +template +inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Tp>; + +namespace views { +namespace __stride_view { +struct __fn { + // clang-format off + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range, range_difference_t<_Range> __n) const + noexcept(noexcept(stride_view{std::forward<_Range>(__range), __n})) + -> decltype( stride_view{std::forward<_Range>(__range), __n}) + { return stride_view(std::forward<_Range>(__range), __n); } + // clang-format on + + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Np&& __n) const { + return __pipeable(std::__bind_back(*this, std::forward<_Np>(__n))); + } +}; +} // namespace __stride_view + +inline namespace __cpo { +inline constexpr auto stride = __stride_view::__fn{}; +} // namespace __cpo +} // namespace views + +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_STRIDE_VIEW_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 234c230ee38d..b7c2ef91be55 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1942,6 +1942,10 @@ module std [system] { header "__ranges/split_view.h" export std.functional.bind_back } + module stride_view { + header "__ranges/stride_view.h" + export std.functional.bind_back + } module subrange { header "__ranges/subrange.h" export std.ranges.subrange_fwd diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 782b4a77c036..308224427db6 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -407,6 +407,17 @@ namespace std::ranges { class chunk_by_view; // C++23 namespace views { inline constexpr unspecified chunk_by = unspecified; } // C++23 + + // [range.stride.view], stride view + template + requires view + class stride_view; // C++23 + + template + constexpr bool enable_borrowed_range> = + enable_borrowed_range; // C++23 + + namespace views { inline constexpr unspecified stride = unspecified; } // C++23 } namespace std { @@ -497,6 +508,7 @@ namespace std { # include <__ranges/from_range.h> # include <__ranges/join_with_view.h> # include <__ranges/repeat_view.h> +# include <__ranges/stride_view.h> # include <__ranges/to.h> # include <__ranges/zip_transform_view.h> # include <__ranges/zip_view.h> diff --git a/libcxx/include/version b/libcxx/include/version index b462fba39cd1..7278be80b7f6 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -218,6 +218,7 @@ __cpp_lib_ranges_join_with 202202L __cpp_lib_ranges_repeat 202207L __cpp_lib_ranges_slide 202202L __cpp_lib_ranges_starts_ends_with 202106L +__cpp_lib_ranges_stride 202207L __cpp_lib_ranges_to_container 202202L __cpp_lib_ranges_zip 202110L __cpp_lib_ratio 202306L @@ -534,6 +535,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_ranges_repeat 202207L // # define __cpp_lib_ranges_slide 202202L # define __cpp_lib_ranges_starts_ends_with 202106L +# define __cpp_lib_ranges_stride 202207L # define __cpp_lib_ranges_to_container 202202L # define __cpp_lib_ranges_zip 202110L // # define __cpp_lib_reference_from_temporary 202202L diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index 97513bc68639..576f0650438e 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -341,7 +341,6 @@ export namespace std { using std::ranges::views::chunk_by; } -# if 0 // [range.stride], stride view using std::ranges::stride_view; @@ -349,6 +348,7 @@ export namespace std { using std::ranges::views::stride; } +# if 0 using std::ranges::cartesian_product_view; namespace views { diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp new file mode 100644 index 000000000000..e564891b15a1 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// REQUIRES: libcpp-hardening-mode={{extensive|debug}} +// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing + +// constexpr explicit stride_view(_View, range_difference_t<_View>) + +#include + +#include "check_assertion.h" + +int main(int, char**) { + int range[] = {1, 2, 3}; + TEST_LIBCPP_ASSERT_FAILURE( + [&range] { std::ranges::stride_view sv(range, 0); }(), "The value of stride must be greater than 0"); + TEST_LIBCPP_ASSERT_FAILURE( + [&range] { std::ranges::stride_view sv(range, -1); }(), "The value of stride must be greater than 0"); + + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp new file mode 100644 index 000000000000..6884c27f5197 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}} +// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing + +// constexpr decltype(auto) operator*() const + +#include + +#include "check_assertion.h" + +int main(int, char**) { + { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 3); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(it), "Cannot dereference an iterator at the end."); + } + { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 4); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(it), "Cannot dereference an iterator at the end."); + } + { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 4); + auto it = view.end(); + TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(it), "Cannot dereference an iterator at the end."); + } + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp new file mode 100644 index 000000000000..1c580fea4a31 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}} +// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing + +// constexpr __iterator& operator++() +// constexpr void operator++(int) +// constexpr __iterator operator++(int) + +#include + +#include "check_assertion.h" +#include "test_iterators.h" + +template > +struct MinimalView : std::ranges::view_base { + Iter begin_; + Sent end_; + + constexpr MinimalView(Iter b, Sent e) : begin_(b), end_(e) {} + MinimalView(MinimalView&&) = default; + MinimalView& operator=(MinimalView&&) = default; + + constexpr Iter begin() const { return begin_; } + constexpr Sent end() const { return end_; } +}; + +int main(int, char**) { + { + int range[] = {1, 2, 3}; + using View = MinimalView>; + auto view = std::ranges::views::stride( + View(cpp17_input_iterator(range), sentinel_wrapper(cpp17_input_iterator(range + 3))), 3); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end."); + TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end."); + } + { + int range[] = {1, 2, 3}; + using View = MinimalView, forward_iterator>; + auto view = std::ranges::views::stride(View(forward_iterator(range), forward_iterator(range + 3)), 3); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end."); + TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end."); + } + { + int range[] = {1, 2, 3}; + using View = MinimalView, forward_iterator>; + auto view = std::ranges::views::stride(View(forward_iterator(range), forward_iterator(range + 3)), 3); + auto it = view.end(); + TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end."); + TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end."); + } + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp new file mode 100644 index 000000000000..152779ec3da8 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test that stride_view's iterator member functions are properly marked nodiscard. + +#include +#include + +void test() { + int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto sv = std::ranges::stride_view(std::ranges::ref_view(range), 3); + auto it = sv.begin(); + auto it2 = sv.begin(); + ++it2; + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + it.base(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(it).base(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + *std::as_const(it); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + it[0]; + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + it + 1; + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + 1 + it; + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + it2 - 1; + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + it2 - it; + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::default_sentinel_t() - it; + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + it - std::default_sentinel_t(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::ranges::iter_move(it); +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp new file mode 100644 index 000000000000..0a84436444cd --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}} +// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing + +// constexpr __iterator& operator+=(difference_type __n) + +#include + +#include "check_assertion.h" + +int main(int, char**) { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 2); + auto it = view.begin(); + TEST_LIBCPP_ASSERT_FAILURE(it += 3, "Advancing the iterator beyond the end is not allowed."); + + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp new file mode 100644 index 000000000000..9c48ecb68e5e --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test that stride_view's member functions are properly marked nodiscard. + +#include +#include + +void test() { + int range[] = {1, 2, 3}; + auto sv = std::ranges::stride_view(range, 2); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + sv.base(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(sv).base(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + sv.begin(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + sv.end(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + sv.size(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + sv.stride(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::stride(range, 2); +} diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp index 7d763f877b32..6825f9675d45 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp @@ -68,6 +68,10 @@ # error "__cpp_lib_ranges_slide should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -126,6 +130,10 @@ # error "__cpp_lib_ranges_slide should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -184,6 +192,10 @@ # error "__cpp_lib_ranges_slide should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -245,6 +257,10 @@ # error "__cpp_lib_ranges_slide should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -348,6 +364,13 @@ # endif # endif +# ifndef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should be defined in c++23" +# endif +# if __cpp_lib_ranges_stride != 202207L +# error "__cpp_lib_ranges_stride should have the value 202207L in c++23" +# endif + # ifndef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should be defined in c++23" # endif @@ -478,6 +501,13 @@ # endif # endif +# ifndef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should be defined in c++26" +# endif +# if __cpp_lib_ranges_stride != 202207L +# error "__cpp_lib_ranges_stride should have the value 202207L in c++26" +# endif + # ifndef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should be defined in c++26" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index 93a156bd9a6b..a403c0c708a7 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -704,6 +704,10 @@ # error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -1668,6 +1672,10 @@ # error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -2797,6 +2805,10 @@ # error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -4193,6 +4205,10 @@ # error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -5808,6 +5824,13 @@ # error "__cpp_lib_ranges_starts_ends_with should have the value 202106L in c++23" # endif +# ifndef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should be defined in c++23" +# endif +# if __cpp_lib_ranges_stride != 202207L +# error "__cpp_lib_ranges_stride should have the value 202207L in c++23" +# endif + # ifndef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should be defined in c++23" # endif @@ -7747,6 +7770,13 @@ # error "__cpp_lib_ranges_starts_ends_with should have the value 202106L in c++26" # endif +# ifndef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should be defined in c++26" +# endif +# if __cpp_lib_ranges_stride != 202207L +# error "__cpp_lib_ranges_stride should have the value 202207L in c++26" +# endif + # ifndef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should be defined in c++26" # endif diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp new file mode 100644 index 000000000000..02b564bda7e0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::stride + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "types.h" + +constexpr BasicTestView> make_input_view(int* begin, int* end) { + return BasicTestView>(cpp17_input_iterator(begin), cpp17_input_iterator(end)); +} + +using ForwardStrideView = std::ranges::stride_view>>; +using BidirStrideView = std::ranges::stride_view>>; +using RandomAccessStrideView = std::ranges::stride_view>>; + +using SizedForwardStrideView = + std::ranges::stride_view, random_access_iterator>>; +using SizedInputStrideView = std::ranges::stride_view>; + +static_assert(std::ranges::forward_range); +static_assert(std::ranges::bidirectional_range); +static_assert(std::ranges::random_access_range); +static_assert(std::ranges::forward_range); +static_assert(std::sized_sentinel_for, + std::ranges::iterator_t>); +static_assert(std::sized_sentinel_for, + std::ranges::iterator_t>); + +constexpr bool test() { + constexpr int N = 3; + int arr[N] = {1, 2, 3}; + + // Test that `std::views::stride` is a range adaptor. + // Check various forms of + + // Test `view | views::stride` + { + using View = std::ranges::stride_view>>; + auto view = make_input_view(arr, arr + N); + std::same_as decltype(auto) result = view | std::views::stride(2); + auto it = result.begin(); + + assert(*it == arr[0]); + std::ranges::advance(it, 1); + assert(*it == arr[2]); + } + + // Test `adaptor | views::stride` + auto twice = [](int i) { return i * 2; }; + { + using View = std::ranges::stride_view< + std::ranges::transform_view>, decltype(twice)>>; + auto view = make_input_view(arr, arr + N); + const auto partial = std::views::transform(twice) | std::views::stride(2); + + std::same_as decltype(auto) result = partial(view); + auto it = result.begin(); + + assert(*it == twice(arr[0])); + std::ranges::advance(it, 1); + assert(*it == twice(arr[2])); + } + + // Test `views::stride | adaptor` + { + using View = std::ranges::transform_view< std::ranges::stride_view>>, + decltype(twice)>; + auto view = make_input_view(arr, arr + N); + std::same_as decltype(auto) result = std::views::stride(view, 2) | std::views::transform(twice); + + auto it = result.begin(); + + assert(*it == twice(arr[0])); + std::ranges::advance(it, 1); + assert(*it == twice(arr[2])); + } + + // Check SFINAE friendliness + { + struct NotAViewableRange {}; + using View = BasicTestView>; + + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + + static_assert(CanBePiped); + static_assert(CanBePiped); + static_assert(!CanBePiped); + static_assert(!CanBePiped); + } + + // A final sanity check. + { + static_assert(std::same_as); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp new file mode 100644 index 000000000000..bb1cc617e68d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr V base() const& requires copy_constructible; +// constexpr V base() &&; + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "types.h" + +template +constexpr bool has_lvalue_qualified_base(T&& t) { + // Thanks to forwarding references, t's type is + // preserved from the caller. No matter the type of + // the argument, when it is used here, t is an l value + // (after all, it has a name). Therefore, this code + // will test whether the l value const-ref-qualified + // version of base is callable. + return requires { t.base(); }; +} + +using CopyableInputView = CopyableView>; +using MoveOnlyInputView = MoveOnlyView>; + +constexpr bool test() { + int buff[] = {1, 2, 3, 4, 5, 6, 7, 8}; + constexpr int N = 8; + + // l-value ref qualified + // const & + { + const std::ranges::stride_view view( + CopyableInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1); + + static_assert(has_lvalue_qualified_base(view)); + + std::same_as decltype(auto) s = view.base(); + assert(*s.begin() == *buff); + } + + // & + { + std::ranges::stride_view view( + CopyableInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1); + + std::same_as decltype(auto) s = view.base(); + assert(*s.begin() == *buff); + + static_assert(has_lvalue_qualified_base(view)); + } + + // r-value ref qualified + // && + { + std::ranges::stride_view view( + CopyableInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1); + + static_assert(has_lvalue_qualified_base(view)); + + std::same_as decltype(auto) s = std::move(view).base(); + assert(*s.begin() == *buff); + } + + // const && + { + const std::ranges::stride_view view( + CopyableInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1); + + static_assert(has_lvalue_qualified_base(view)); + + std::same_as decltype(auto) s = std::move(view).base(); + assert(*s.begin() == *buff); + } + + // && + { + std::ranges::stride_view view( + MoveOnlyInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1); + + // Because the base of the stride view is move only, + // the const & version is not applicable and, therefore, + // there is no l-value qualified base method. + static_assert(!has_lvalue_qualified_base(view)); + + std::same_as decltype(auto) s = std::move(view).base(); + assert(*s.begin() == *buff); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp new file mode 100644 index 000000000000..ca45fd81829c --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto begin() requires(!simple-view) +// constexpr auto begin() const requires range + +// Note: Checks here are augmented by checks in +// iterator/ctor.copy.pass.cpp. + +#include +#include +#include + +#include "types.h" + +template +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template +concept HasBegin = requires(T& t) { t.begin(); }; + +template +concept HasConstAndNonConstBegin = requires(T& t, const T& ct) { + // The return types for begin are different when this is const or not: + // the iterator's _Const non-type-template parameter is true in the former + // and false in the latter. + requires !std::same_as; +}; + +template +// There is a begin but it's not const qualified => Only non-const qualified begin. +concept HasOnlyNonConstBegin = HasBegin && !HasConstBegin; + +template +// There is a const-qualified begin and there are not both const- and non-const qualified begin => Only const-qualified begin. +concept HasOnlyConstBegin = HasConstBegin && !HasConstAndNonConstBegin; + +static_assert(HasOnlyNonConstBegin>); +static_assert(HasOnlyConstBegin>); +static_assert(HasConstAndNonConstBegin>); + +constexpr bool test() { + { + // Return type check for non-simple const view. + const auto v = UnSimpleConstView(); + const auto sv = std::ranges::stride_view(v, 1); + static_assert(std::same_as); + } + { + // Return type check for simple const view. + auto v = SimpleCommonConstView(); + auto sv = std::ranges::stride_view(v, 1); + static_assert(std::same_as); + } + { + // Verify begin() produces the first element with stride 1. + int data[] = {10, 20, 30, 40, 50}; + auto v = BasicTestView{data, data + 5}; + auto sv = std::ranges::stride_view(v, 1); + assert(*sv.begin() == 10); + } + { + // Verify begin() produces the first element with stride 3. + int data[] = {10, 20, 30, 40, 50}; + auto v = BasicTestView{data, data + 5}; + auto sv = std::ranges::stride_view(v, 3); + assert(*sv.begin() == 10); + } + { + // Verify iterating from begin with stride 2 produces correct elements. + int data[] = {1, 2, 3, 4, 5}; + auto v = BasicTestView{data, data + 5}; + auto sv = std::ranges::stride_view(v, 2); + auto it = sv.begin(); + assert(*it == 1); + ++it; + assert(*it == 3); + ++it; + assert(*it == 5); + ++it; + assert(it == sv.end()); + } + { + // Verify begin on forward range. + int data[] = {100, 200, 300}; + using FwdView = BasicTestView, forward_iterator>; + auto v = FwdView{forward_iterator(data), forward_iterator(data + 3)}; + auto sv = std::ranges::stride_view(v, 2); + assert(*sv.begin() == 100); + auto it = sv.begin(); + ++it; + assert(*it == 300); + } + { + // Empty range: begin() == end(). + int data[] = {1}; + auto v = BasicTestView{data, data}; + auto sv = std::ranges::stride_view(v, 3); + assert(sv.begin() == sv.end()); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp new file mode 100644 index 000000000000..cfadffbda28d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template +// inline constexpr bool enable_borrowed_range> = ranges::enable_borrowed_range; + +#include + +#include "test_range.h" + +static_assert(std::ranges::enable_borrowed_range< std::ranges::stride_view>); +static_assert(!std::ranges::enable_borrowed_range< std::ranges::stride_view>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp new file mode 100644 index 000000000000..afc1d16825c0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.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 + +// template requires view + +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" +#include "test_range.h" + +template D> +concept CanStrideView = requires { std::ranges::stride_view{I{}, D}; }; + +// Ensure that the InputRangeNotIndirectlyReadable is a valid range. +static_assert(std::ranges::range); +// Ensure that the InputRangeNotIndirectlyReadable's iterator is not an input range ... +static_assert(!std::ranges::input_range>); +// Because CanStrideView requires that the range/view type be default constructible, let's double check that ... +static_assert(std::is_default_constructible_v); +// And now, finally, let's make sure that we cannot stride over a range whose iterator is not an input iterator ... +static_assert(!CanStrideView); + +// Ensure that a range that is not a view cannot be the subject of a stride_view. +static_assert(std::ranges::range>); +static_assert(std::ranges::input_range>); +static_assert(std::movable>); +static_assert(!std::ranges::view>); +static_assert(!CanStrideView, 1>); + +// And now, let's satisfy all the prerequisites and make sure that we can stride over a range (that is an input range +// and is a view!) +static_assert(std::ranges::range>); +static_assert(std::ranges::input_range>); +static_assert(std::ranges::view>); +static_assert(CanStrideView, 1>); + +// stride_view itself models view. +static_assert(std::ranges::view>>); +static_assert(std::ranges::view>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp new file mode 100644 index 000000000000..12653c4c5dd0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template +// stride_view(R&&, range_difference_t) -> stride_view>; + +#include +#include +#include +#include + +#include "types.h" + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct Range { + int* begin() const; + int* end() const; +}; + +constexpr bool test() { + int a[] = {1, 2, 3, 4, 5}; + + using BaseRange = BasicTestRange>; + using BaseView = BasicTestView; + + auto base_view = BaseView(a, a + 5); + auto base_view_move = BaseView(a, a + 5); + + auto base_range = BaseRange(cpp17_input_iterator(a), cpp17_input_iterator(a + 5)); + auto base_range_move = BaseRange(cpp17_input_iterator(a), cpp17_input_iterator(a + 5)); + + // Deduction from lvalue view and rvalue view. + auto sv_view = std::ranges::stride_view(base_view, 2); + auto sv_view_move = std::ranges::stride_view(std::move(base_view_move), 2); + + // Deduction from lvalue range (-> ref_view) and rvalue range (-> owning_view). + auto sv_range = std::ranges::stride_view(base_range, 2); + auto sv_range_move = std::ranges::stride_view(std::move(base_range_move), 2); + + // Verify deduced types for views. + static_assert(std::same_as>); + static_assert(std::same_as>); + + // Verify deduced types for ranges: lvalue -> ref_view, rvalue -> owning_view. + static_assert(std::same_as> >); + static_assert(std::same_as> >); + + // Verify begin() produces the first element. + assert(*sv_range.begin() == 1); + assert(*sv_range_move.begin() == 1); + assert(*sv_view.begin() == 1); + assert(*sv_view_move.begin() == 1); + + // Verify iteration with stride 2 over a range. + auto it = sv_range.begin(); + it++; + assert(*it == 3); + it++; + it++; + assert(it == sv_range.end()); + + auto it2 = sv_range_move.begin(); + it2++; + it2++; + assert(*it2 == 5); + it2++; + assert(it2 == sv_range_move.end()); + + // Verify iteration with stride 2 over a view. + auto it3 = sv_view.begin(); + it3++; + assert(*it3 == 3); + it3++; + it3++; + assert(it3 == sv_view.end()); + + auto it4 = sv_view.begin(); + it4++; + it4++; + assert(*it4 == 5); + it4++; + assert(it4 == sv_view_move.end()); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp new file mode 100644 index 000000000000..6e7f6c92c46f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.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 + +// constexpr explicit stride_view(V base, range_difference_t stride) + +#include +#include +#include +#include + +#include "test_convertible.h" +#include "test_iterators.h" +#include "types.h" + +constexpr bool test() { + { + // There is no default ctor for stride_view. + using View = BasicTestView>; + static_assert(!std::is_default_constructible_v>); + + // Test that the stride_view can only be explicitly constructed. + static_assert(!test_convertible, View, int>()); + } + + { + int arr[] = {1, 2, 3}; + // Test that what we will stride over is move only. + using View = MoveOnlyView>; + static_assert(!std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + + View mov(cpp17_input_iterator(arr), cpp17_input_iterator(arr + 3)); + // Because MoveOnlyView is, well, move only, we can test that it is moved + // from when the stride view is constructed. + std::ranges::stride_view strided(std::move(mov), 1); + + // While we are here, make sure that the ctor captured the stride. + assert(strided.stride() == 1); + } + { + // Verify salient properties after construction. + int arr[] = {10, 20, 30, 40, 50}; + using Base = BasicTestView; + auto strided = std::ranges::stride_view(Base(arr, arr + 5), 2); + + assert(strided.stride() == 2); + assert(*strided.begin() == 10); + + auto it = strided.begin(); + ++it; + assert(*it == 30); + ++it; + assert(*it == 50); + ++it; + assert(it == strided.end()); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp new file mode 100644 index 000000000000..2f4cc5f87546 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp @@ -0,0 +1,222 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto end() requires(!simple-view) +// constexpr auto end() const requires(range) + +// Note: Checks here are augmented by checks in +// iterator/ctor.copy.pass.cpp. + +#include +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" +#include "types.h" + +// A view that is a common forward range when const, but NOT a common range when non-const. +struct CommonForwardOnlyWhenConst : std::ranges::view_base { + int* data_; + int size_; + + constexpr CommonForwardOnlyWhenConst(int* d, int s) : data_(d), size_(s) {} + + CommonForwardOnlyWhenConst(CommonForwardOnlyWhenConst&&) = default; + CommonForwardOnlyWhenConst& operator=(CommonForwardOnlyWhenConst&&) = default; + + // Non-const: not a common range (iterator and sentinel are different types). + constexpr forward_iterator begin() { return forward_iterator(data_); } + constexpr sentinel_wrapper> end() { + return sentinel_wrapper>(forward_iterator(data_ + size_)); + } + + // Const: a common forward range (begin and end return the same type). + constexpr forward_iterator begin() const { return forward_iterator(data_); } + constexpr forward_iterator end() const { return forward_iterator(data_ + size_); } +}; + +static_assert(std::ranges::range); +static_assert(std::ranges::range); +static_assert(std::ranges::forward_range); +static_assert(!std::ranges::bidirectional_range); +static_assert(std::ranges::common_range); +static_assert(!std::ranges::common_range); +static_assert(std::ranges::view); + +template +concept HasConstEnd = requires(const T& ct) { ct.end(); }; + +template +concept HasEnd = requires(T& t) { t.end(); }; + +template +concept HasConstAndNonConstEnd = + HasConstEnd && requires(T& t, const T& ct) { requires !std::same_as; }; + +template +concept HasOnlyNonConstEnd = HasEnd && !HasConstEnd; + +template +concept HasOnlyConstEnd = HasConstEnd && !HasConstAndNonConstEnd; + +static_assert(HasOnlyNonConstEnd>); +static_assert(HasOnlyConstEnd>); +static_assert(HasConstAndNonConstEnd>); + +constexpr bool test() { + { + // A const, simple, common-, sized- and forward-range. + // Note: sized because it is possible to get a difference between its + // beginning and its end. + const int data[] = {1, 2, 3}; + auto v = BasicTestView{data, data + 3}; + auto sv = std::ranges::stride_view(v, 1); + static_assert(!std::is_same_v); + + // Verify actual end behavior: iterating reaches end. + auto it = sv.begin(); + ++it; + ++it; + ++it; + assert(it == sv.end()); + } + { + // ForwardTestView is not sized and not bidirectional, but it is common. + // Note: It is not sized because BasicTestView has no member function named size (by default) + // and nor is it possible to get a difference between its beginning and its end. + int data[] = {1, 2, 3}; + using ForwardTestView = BasicTestView, forward_iterator>; + auto v = ForwardTestView{forward_iterator(data), forward_iterator(data + 3)}; + auto sv = std::ranges::stride_view(v, 1); + static_assert(!std::is_same_v); + + auto it = sv.begin(); + ++it; + ++it; + ++it; + assert(it == sv.end()); + } + { + // A non-const, non-simple, common-, sized- and forward-range. + static_assert(!simple_view); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::forward_range); + + auto sv = std::ranges::stride_view(UnSimpleNoConstCommonView{}, 1); + static_assert(!std::is_same_v); + } + { + // Uncommon range -> returns default_sentinel. + static_assert(!simple_view); + static_assert(!std::ranges::common_range); + + auto sv = std::ranges::stride_view(UnsimpleUnCommonConstView{}, 1); + ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(sv.end())); + } + { + // Simple, uncommon range -> returns default_sentinel. + static_assert(simple_view); + static_assert(!std::ranges::common_range); + + auto sv = std::ranges::stride_view(SimpleUnCommonConstView{}, 1); + ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(sv.end())); + } + { + // Verify stride > 1 with end(): iterating produces correct elements and terminates. + int data[] = {10, 20, 30, 40, 50}; + auto v = BasicTestView{data, data + 5}; + auto sv = std::ranges::stride_view(v, 2); + + auto it = sv.begin(); + assert(*it == 10); + ++it; + assert(*it == 30); + ++it; + assert(*it == 50); + ++it; + assert(it == sv.end()); + } + { + // Verify end() with stride that doesn't evenly divide the range. + int data[] = {1, 2, 3, 4, 5, 6, 7}; + auto v = BasicTestView{data, data + 7}; + auto sv = std::ranges::stride_view(v, 3); + + auto it = sv.begin(); + assert(*it == 1); + ++it; + assert(*it == 4); + ++it; + assert(*it == 7); + ++it; + assert(it == sv.end()); + } + { + // end() const should use common_range, not common_range<_View>. CommonForwardOnlyWhenConst is + // common + forward-only when const, but NOT common when non-const. + int data[] = {1, 2, 3, 4, 5}; + auto v = CommonForwardOnlyWhenConst(data, 5); + auto sv = std::ranges::stride_view(std::move(v), 2); + const auto& csv = sv; + + // The key assertion: end() on the const stride_view must NOT return default_sentinel_t. + static_assert(!std::is_same_v); + + // Verify iteration actually works and reaches end. + auto it = csv.begin(); + assert(*it == 1); + ++it; + assert(*it == 3); + ++it; + assert(*it == 5); + ++it; + assert(it == csv.end()); + } + { + // Test the `common_range && forward_range && !sized_range && !bidirectional_range` branch. + // forward_iterator does not support operator-, so the view is not sized. + // end() should return an iterator (not default_sentinel). + int data[] = {1, 2, 3, 4, 5}; + using FwdView = BasicTestView, forward_iterator>; + static_assert(std::ranges::common_range); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(!std::ranges::sized_range); + + auto v = FwdView{forward_iterator(data), forward_iterator(data + 5)}; + auto sv = std::ranges::stride_view(v, 2); + static_assert(!std::is_same_v); + + auto it = sv.begin(); + assert(*it == 1); + ++it; + assert(*it == 3); + ++it; + assert(*it == 5); + ++it; + assert(it == sv.end()); + } + { + // Empty range: begin() == end(). + int data[] = {1}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(data, data), 3); + assert(sv.begin() == sv.end()); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp new file mode 100644 index 000000000000..82acab6d6aee --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr const iterator_t& base() const& noexcept +// constexpr iterator_t base() && + +#include +#include +#include +#include + +#include "../types.h" + +constexpr bool test() { + { + // base() const& is noexcept; base() && is not. + int arr[] = {1, 2, 3}; + auto stride = std::ranges::stride_view(arr, 1); + [[maybe_unused]] auto stride_iter = stride.begin(); + + static_assert(noexcept(stride_iter.base())); + static_assert(!noexcept((std::move(stride_iter).base()))); + } + { + // base() const& returns a const ref; base() && returns a non-const value. + int arr[] = {1, 2, 3}; + auto stride = std::ranges::stride_view(arr, 1); + [[maybe_unused]] auto stride_iter = stride.begin(); + + static_assert(std::is_const_v>); + static_assert(!std::is_const_v); + } + { + // base() && moves the underlying iterator. + int move_counter = 0; + int copy_counter = 0; + + auto start = SizedInputIter(); + start.move_counter = &move_counter; + start.copy_counter = ©_counter; + auto stop = SizedInputIter(); + + auto view = BasicTestView{start, stop}; + assert(move_counter == 0); + assert(copy_counter == 1); + + auto sv = std::ranges::stride_view>(view, 1); + assert(move_counter == 1); + assert(copy_counter == 2); + + auto svi = sv.begin(); + assert(copy_counter == 3); + assert(move_counter == 2); + + [[maybe_unused]] auto result = std::move(svi).base(); + assert(move_counter == 3); + assert(copy_counter == 3); + } + { + // base() const& copies the underlying iterator. + int move_counter = 0; + int copy_counter = 0; + auto start = SizedInputIter(); + + start.move_counter = &move_counter; + start.copy_counter = ©_counter; + auto stop = SizedInputIter(); + + auto view = BasicTestView{start, stop}; + assert(move_counter == 0); + assert(copy_counter == 1); + + auto sv = std::ranges::stride_view>(view, 1); + assert(move_counter == 1); + assert(copy_counter == 2); + + [[maybe_unused]] auto svi = sv.begin(); + assert(copy_counter == 3); + assert(move_counter == 2); + + [[maybe_unused]] const SizedInputIter base_result = svi.base(); + assert(move_counter == 2); + assert(copy_counter == 4); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp new file mode 100644 index 000000000000..6f4e098e664f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp @@ -0,0 +1,122 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// friend constexpr bool operator<(const iterator& x, const iterator& y) +// friend constexpr bool operator>(const iterator& x, const iterator& y) +// friend constexpr bool operator<=(const iterator& x, const iterator& y) +// friend constexpr bool operator>=(const iterator& x, const iterator& y) +// friend constexpr auto operator<=>(const iterator& x, const iterator& y) + +#include +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +// Input view: no relational comparisons. +using InputView = BasicTestView, sized_sentinel>>; +using StrideInputIter = std::ranges::iterator_t>; +static_assert(!std::is_invocable_v, StrideInputIter, StrideInputIter>); +static_assert(!std::is_invocable_v, StrideInputIter, StrideInputIter>); +static_assert(!std::is_invocable_v, StrideInputIter, StrideInputIter>); +static_assert(!std::is_invocable_v, StrideInputIter, StrideInputIter>); + +// Forward view: no relational comparisons. +using FwdView = BasicTestView, sized_sentinel>>; +using StrideFwdIter = std::ranges::iterator_t>; +static_assert(!std::is_invocable_v, StrideFwdIter, StrideFwdIter>); +static_assert(!std::is_invocable_v, StrideFwdIter, StrideFwdIter>); +static_assert(!std::is_invocable_v, StrideFwdIter, StrideFwdIter>); +static_assert(!std::is_invocable_v, StrideFwdIter, StrideFwdIter>); + +// Bidirectional view: no relational comparisons. +using BidirView = BasicTestView, sized_sentinel>>; +using StrideBidirIter = std::ranges::iterator_t>; +static_assert(!std::is_invocable_v, StrideBidirIter, StrideBidirIter>); +static_assert(!std::is_invocable_v, StrideBidirIter, StrideBidirIter>); +static_assert(!std::is_invocable_v, StrideBidirIter, StrideBidirIter>); +static_assert(!std::is_invocable_v, StrideBidirIter, StrideBidirIter>); + +// Random access view: all relational comparisons available. +using RAView = BasicTestView>; +using StrideRAIter = std::ranges::iterator_t>; +static_assert(std::is_invocable_v, StrideRAIter, StrideRAIter>); +static_assert(std::is_invocable_v, StrideRAIter, StrideRAIter>); +static_assert(std::is_invocable_v, StrideRAIter, StrideRAIter>); +static_assert(std::is_invocable_v, StrideRAIter, StrideRAIter>); + +// three_way_comparable when the base iterator is three_way_comparable. +using ThreeWayView = BasicTestView>; +using StrideThreeWayIter = std::ranges::iterator_t>; +static_assert(std::three_way_comparable); + +// Not three_way_comparable when the base is not. +using EqualOnlyView = BasicTestView>; +using StrideEqualOnlyIter = std::ranges::iterator_t>; +static_assert(!std::three_way_comparable); + +constexpr bool test() { + { + // <, >, <=, >= on random access range. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 9), 3); + + auto a = sv.begin(); // index 0 + auto b = sv.begin(); + ++b; // index 3 + auto c = sv.begin(); + ++c; + ++c; // index 6 + + assert(a < b); + assert(b < c); + assert(!(b < a)); + assert(!(a < a)); + + assert(b > a); + assert(c > b); + assert(!(a > b)); + assert(!(a > a)); + + assert(a <= b); + assert(a <= a); + assert(!(b <= a)); + + assert(b >= a); + assert(a >= a); + assert(!(a >= b)); + } + { + // <=> on three_way_comparable base. + int arr[] = {1, 2, 3, 4, 5, 6, 7}; + using Base = BasicTestView, three_way_contiguous_iterator>; + auto sv = + std::ranges::stride_view(Base(three_way_contiguous_iterator(arr), three_way_contiguous_iterator(arr + 7)), 2); + + auto a = sv.begin(); + auto b = sv.begin(); + ++b; + + assert((a <=> b) == std::strong_ordering::less); + assert((b <=> a) == std::strong_ordering::greater); + assert((a <=> a) == std::strong_ordering::equal); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp new file mode 100644 index 000000000000..298e7d6e36dd --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp @@ -0,0 +1,165 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr iterator(iterator i) +// requires Const && convertible_to, iterator_t> && +// convertible_to, sentinel_t> + +#include +#include +#include +#include + +#include "../types.h" + +// A non-simple view over actual data. begin()/end() return int* when non-const +// and const int* when const, so iterator and iterator are distinct +// and the converting constructor is available (int* converts to const int*). +struct NonSimpleDataView : std::ranges::view_base { + int* data_; + int size_; + + constexpr NonSimpleDataView(int* d, int s) : data_(d), size_(s) {} + NonSimpleDataView(NonSimpleDataView&&) = default; + NonSimpleDataView& operator=(NonSimpleDataView&&) = default; + + constexpr int* begin() { return data_; } + constexpr const int* begin() const { return data_; } + constexpr int* end() { return data_ + size_; } + constexpr const int* end() const { return data_ + size_; } +}; + +static_assert(!simple_view); + +// Two unrelated input iterator types (no conversion between them). +struct IterA : InputIter {}; +struct IterB : InputIter { + friend constexpr bool operator==(const IterB&, const IterA&) { return true; } +}; + +// Two unrelated sentinel types (no conversion between them). +struct SentA { + friend constexpr bool operator==(const SentA&, const IterA&) { return true; } + friend constexpr bool operator==(const SentA&, const IterB&) { return true; } +}; +struct SentB { + friend constexpr bool operator==(const SentB&, const IterA&) { return true; } + friend constexpr bool operator==(const SentB&, const IterB&) { return true; } +}; + +// Non-simple view where iterator conversion fails (IterA does not convert to IterB). +struct IterNonConvertibleView : std::ranges::view_base { + constexpr IterA begin() { return {}; } + constexpr IterB begin() const { return {}; } + constexpr SentA end() const { return {}; } +}; + +// Non-simple view where sentinel conversion fails (SentA does not convert to SentB). +struct SentNonConvertibleView : std::ranges::view_base { + constexpr IterA begin() const { return {}; } + constexpr SentA end() { return {}; } + constexpr SentB end() const { return {}; } +}; + +// Conversion succeeds: both iterator and sentinel types are convertible (int* -> const int*). +using ConvertibleSV = std::ranges::stride_view; +using ConvertibleIter = std::ranges::iterator_t; +using ConvertibleCIter = std::ranges::iterator_t; +static_assert(!std::same_as); +static_assert(std::convertible_to); +static_assert(std::constructible_from); + +// Conversion fails: underlying iterator types are not convertible (IterA -> IterB). +using IterNCSV = std::ranges::stride_view; +using IterNCIter = std::ranges::iterator_t; +using IterNCCIter = std::ranges::iterator_t; +static_assert(!std::same_as); +static_assert(!std::convertible_to); +static_assert(!std::constructible_from); + +// Conversion fails: underlying sentinel types are not convertible (SentA -> SentB). +using SentNCSV = std::ranges::stride_view; +using SentNCIter = std::ranges::iterator_t; +using SentNCCIter = std::ranges::iterator_t; +static_assert(!std::same_as); +static_assert(!std::convertible_to); +static_assert(!std::constructible_from); + +constexpr bool test() { + { + // Convert non-const iterator to const iterator with stride 1. + int arr[] = {10, 20, 30, 40, 50}; + auto sv = ConvertibleSV(NonSimpleDataView(arr, 5), 1); + auto it = sv.begin(); + assert(*it == 10); + + ConvertibleCIter cit{it}; + assert(*cit == 10); + + ++cit; + assert(*cit == 20); + ++cit; + assert(*cit == 30); + ++cit; + assert(*cit == 40); + ++cit; + assert(*cit == 50); + ++cit; + assert(cit == std::as_const(sv).end()); + } + { + // Convert non-const iterator to const iterator with stride 2. + int arr[] = {10, 20, 30, 40, 50}; + auto sv = ConvertibleSV(NonSimpleDataView(arr, 5), 2); + auto it = sv.begin(); + + ConvertibleCIter cit{it}; + assert(*cit == 10); + ++cit; + assert(*cit == 30); + ++cit; + assert(*cit == 50); + ++cit; + assert(cit == std::as_const(sv).end()); + } + { + // Convert after advancing the non-const iterator. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto sv = ConvertibleSV(NonSimpleDataView(arr, 9), 3); + auto it = sv.begin(); + ++it; + assert(*it == 4); + + ConvertibleCIter cit{it}; + assert(*cit == 4); + ++cit; + assert(*cit == 7); + ++cit; + assert(cit == std::as_const(sv).end()); + } + { + // Convert with stride larger than range size. + int arr[] = {42, 99}; + auto sv = ConvertibleSV(NonSimpleDataView(arr, 2), 5); + auto it = sv.begin(); + + ConvertibleCIter cit{it}; + assert(*cit == 42); + ++cit; + assert(cit == std::as_const(sv).end()); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp new file mode 100644 index 000000000000..b7fbb8a12ed3 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// iterator() requires default_initializable> = default; + +#include +#include + +#include "../types.h" + +struct NonDefaultConstructibleIterator : InputIter { + NonDefaultConstructibleIterator() = delete; + constexpr NonDefaultConstructibleIterator(int) {} +}; + +struct ViewWithNonDefaultConstructibleIterator : std::ranges::view_base { + constexpr NonDefaultConstructibleIterator begin() const { return NonDefaultConstructibleIterator{5}; } + constexpr std::default_sentinel_t end() const { return {}; } +}; +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +// The stride_view iterator is default-constructible iff the iterator type of the range being +// strided is default-constructible. +static_assert(!std::is_default_constructible< std::ranges::iterator_t>()); +static_assert(std::is_default_constructible< + std::ranges::iterator_t< std::ranges::stride_view>>>()); + +constexpr bool test() { + { + // Default construct an iterator over a default-constructible base. + using SV = std::ranges::stride_view>; + using It = std::ranges::iterator_t; + [[maybe_unused]] It it{}; + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp new file mode 100644 index 000000000000..3c40e3c88f93 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp @@ -0,0 +1,159 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr iterator& operator--() +// constexpr iterator operator--(int) + +#include +#include +#include +#include + +#include "../types.h" + +template +concept CanPostDecrement = std::is_same_v()--)> && requires(T& t) { t--; }; +template +concept CanPreDecrement = std::is_same_v()))> && requires(T& t) { --t; }; + +// What operators are valid for an iterator derived from a stride view +// over an input view. +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; + +static_assert(!std::ranges::bidirectional_range); +static_assert(!CanPostDecrement); +static_assert(!CanPreDecrement); + +// What operators are valid for an iterator derived from a stride view +// over a forward view. +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; + +static_assert(!std::ranges::bidirectional_range); +static_assert(!CanPostDecrement); +static_assert(!CanPostDecrement); + +// What operators are valid for an iterator derived from a stride view +// over a bidirectional view. +using BidirectionalView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::bidirectional_range); +static_assert(CanPostDecrement); +static_assert(CanPostDecrement); + +// What operators are valid for an iterator derived from a stride view +// over a random access view. +using RandomAccessView = BasicTestView>; +using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::bidirectional_range); +static_assert(CanPostDecrement); +static_assert(CanPostDecrement); + +template + requires(std::bidirectional_iterator) +constexpr bool test_operator_decrement(Iter begin, Iter end, Difference delta) { + using Base = BasicTestView; + + auto base_view_offset_zero = Base(begin, end); + // Because of the requires on the Iter template type, we are sure + // that the type of sv_incr_one is a bidirectional range. + auto sv_incr_diff = std::ranges::stride_view(base_view_offset_zero, delta); + auto sv_incr_end = sv_incr_diff.end(); + + // Recreate the "missing" calculation here -- to make sure that it matches. + auto missing = delta - (std::ranges::distance(base_view_offset_zero) % delta) % delta; + + auto sought = end + (missing - delta); + + assert(*sought == *(--sv_incr_end)); + assert(*sought == *(sv_incr_end)); + + sv_incr_end = sv_incr_diff.end(); + sv_incr_end--; + assert(*(end + (missing - delta)) == *(sv_incr_end)); + + return true; +} + +constexpr bool test() { + { + // Pre-decrement and post-decrement from end with stride 3. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + test_operator_decrement(arr, arr + 11, 3); + } + { + // Decrement from end when size % stride != 0. + // 10 elements, stride 3: strided elements at indices 0,3,6,9. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 10), 3); + auto it = sv.end(); + --it; + assert(*it == 10); // index 9 + --it; + assert(*it == 7); // index 6 + --it; + assert(*it == 4); // index 3 + --it; + assert(*it == 1); // index 0 + assert(it == sv.begin()); + } + { + // Decrement when stride evenly divides range size. + // 9 elements, stride 3: strided elements at 0,3,6. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 9), 3); + auto it = sv.end(); + --it; + assert(*it == 7); // index 6 + --it; + assert(*it == 4); // index 3 + --it; + assert(*it == 1); // index 0 + } + { + // Decrement from mid-range position (not end). + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 10), 3); + auto it = sv.begin(); + ++it; // index 3 + ++it; // index 6 + assert(*it == 7); + --it; // back to index 3 + assert(*it == 4); + --it; // back to index 0 + assert(*it == 1); + } + { + // Round-trip: decrement from end, increment, decrement again. + int arr[] = {1, 2, 3, 4, 5, 6, 7}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 7), 3); + auto it = sv.end(); + --it; + assert(*it == 7); // index 6 + ++it; + assert(it == sv.end()); + --it; + assert(*it == 7); // back to index 6 again + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp new file mode 100644 index 000000000000..55b76899381e --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp @@ -0,0 +1,101 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr decltype(auto) operator*() const + +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +constexpr bool test() { + { + // Dereference with stride 1. + int arr[] = {10, 20, 30}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 3), 1); + auto it = sv.begin(); + + assert(*it == 10); + ++it; + assert(*it == 20); + ++it; + assert(*it == 30); + } + { + // Dereference with stride 2. + int arr[] = {10, 20, 30, 40, 50}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 5), 2); + auto it = sv.begin(); + + assert(*it == 10); + ++it; + assert(*it == 30); + ++it; + assert(*it == 50); + } + { + // Dereference with stride larger than range. + int arr[] = {42, 99}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 2), 5); + auto it = sv.begin(); + + assert(*it == 42); + } + { + // Dereference returns a reference that can be assigned through. + int arr[] = {1, 2, 3, 4, 5}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 5), 2); + auto it = sv.begin(); + + *it = 100; + assert(arr[0] == 100); + ++it; + *it = 200; + assert(arr[2] == 200); + } + { + // Dereference on a forward range with stride 3. + int arr[] = {5, 10, 15, 20, 25, 30}; + using Base = BasicTestView, forward_iterator>; + auto sv = std::ranges::stride_view(Base(forward_iterator(arr), forward_iterator(arr + 6)), 3); + auto it = sv.begin(); + assert(*it == 5); + ++it; + assert(*it == 20); + } + { + // Return type is a reference, not a value. + int arr[] = {1, 2, 3}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 3), 1); + auto it = sv.begin(); + static_assert(std::is_same_v); + } + { + // Dereference through a const-qualified iterator. + int arr[] = {10, 20, 30}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 3), 2); + const auto cit = sv.begin(); + assert(*cit == 10); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp new file mode 100644 index 000000000000..cd1dd5176fb2 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp @@ -0,0 +1,110 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// friend constexpr bool operator==(const iterator& x, default_sentinel_t) +// friend constexpr bool operator==(const iterator& x, const iterator& y) + +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +// A stride_view iterator over an equality_comparable base should itself be equality_comparable. +using EqualableView = BasicTestView>; +using EqualableViewIter = std::ranges::iterator_t>; +static_assert(std::equality_comparable>); +static_assert(std::equality_comparable); + +// A stride_view iterator over a non-equality_comparable base should NOT be equality_comparable. +using UnEqualableView = ViewOverNonCopyableIterator>; +using UnEqualableViewIter = std::ranges::iterator_t>; +static_assert(!std::equality_comparable>); +static_assert(!std::equality_comparable); + +template +constexpr void testOne() { + using Range = BasicTestView; + static_assert(std::ranges::common_range); + using StrideView = std::ranges::stride_view; + + { + // Equality and inequality between iterators, and comparison with end. + { + int buffer[] = {0, 1, 2, -1, 4, 5, 6, 7}; + const Range input(Iter{buffer}, Iter{buffer + 8}); + const StrideView sv(input, 1); + const StrideView sv_too(input, 2); + auto b = sv.begin(); + auto e = sv.end(); + auto b_too = sv_too.begin(); + + assert(b == b); + assert(!(b != b)); + + // When Range is a bidirectional_range, the type of e is + // default_sentinel_t and those do not compare to one another. + if constexpr (!std::ranges::bidirectional_range) { + assert(e == e); + assert(!(e != e)); + } + assert(!(b == e)); + assert(b != e); + + std::advance(b, 8); + std::advance(b_too, 4); + + assert(b == b_too); + assert(!(b != b_too)); + + assert(b == b); + assert(!(b != b)); + + // See above. + if constexpr (!std::ranges::bidirectional_range) { + assert(e == e); + assert(!(e != e)); + } + + assert(b == e); + assert(!(b != e)); + } + + // Default-constructed iterators compare equal. + { + int buffer[] = {0, 1, 2, -1, 4, 5, 6}; + const Range input(Iter{buffer}, Iter{buffer + 7}); + const std::ranges::stride_view sv(input, 1); + using StrideViewIter = decltype(sv.begin()); + StrideViewIter i1; + StrideViewIter i2; + assert(i1 == i2); + assert(!(i1 != i2)); + } + } +} + +constexpr bool test() { + testOne>(); + testOne>(); + testOne>(); + testOne>(); + testOne(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp new file mode 100644 index 000000000000..671eca2b66a1 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp @@ -0,0 +1,211 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr iterator& operator++() +// constexpr void operator++(int) +// constexpr iterator operator++(int) + +#include +#include +#include +#include + +#include "../types.h" + +template +concept CanPostIncrementVoid = std::is_same_v()++)> && requires(T& t) { t++; }; +template +concept CanPostIncrementIterator = std::is_same_v()++)> && requires(T& t) { t = t++; }; +template +concept CanPreIncrementIterator = std::is_same_v()))> && requires(T& t) { t = ++t; }; + +// A stride view with a base that is a non forward range returns void from operator++ +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; +static_assert(CanPostIncrementVoid); +static_assert(!CanPostIncrementIterator); +static_assert(CanPreIncrementIterator); + +// A stride view with a base that is a forward range returns void from operator++ +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; +static_assert(!CanPostIncrementVoid); +static_assert(CanPostIncrementIterator); +static_assert(CanPreIncrementIterator); + +template + requires std::sized_sentinel_for && (!std::forward_iterator) +constexpr bool test_non_forward_operator_increment(Iter zero_begin, Iter three_begin, Iter end) { + using Base = BasicTestView; + + auto base = Base(zero_begin, end); + auto base_offset = Base(three_begin, end); + auto sv = std::ranges::stride_view(base, 3); + auto sv_offset = std::ranges::stride_view(base_offset, 3); + + auto it = sv.begin(); + auto it_offset = sv_offset.begin(); + + auto it_after = it; // With a stride of 3, so ++ moves 3 indexes. + ++it_after; + assert(*it_offset == *it_after); + + it_after = it; + it_after++; + assert(*it_offset == *it_after); + + // See if both get to the end (with pre-increment). + auto it_to_end = it; + ++it_to_end; // 3 + ++it_to_end; // 6 + ++it_to_end; // 9 + ++it_to_end; // End + + auto it_offset_to_end = it_offset; // With a stride of 3, so ++ moves 3 indexes. + ++it_offset_to_end; // 6 + ++it_offset_to_end; // 9 + ++it_offset_to_end; // End + + assert(it_offset_to_end == it_to_end); + assert(it_offset_to_end == sv_offset.end()); + assert(it_to_end == sv.end()); + + // See if both get to the end (with post-increment). + it_to_end = it; + it_to_end++; // 3 + it_to_end++; // 6 + it_to_end++; // 9 + it_to_end++; // End + + it_offset_to_end = it_offset; // With a stride of 3, so ++ moves 3 indexes. + it_offset_to_end++; // 6 + it_offset_to_end++; // 9 + it_offset_to_end++; // End + + assert(it_offset_to_end == it_to_end); + assert(it_offset_to_end == sv_offset.end()); + assert(it_to_end == sv.end()); + + return true; +} + +template +constexpr bool test_forward_operator_increment(Iter begin, Iter end) { + using Base = BasicTestView; + + using StrideViewIterator = std::ranges::iterator_t>; + static_assert(std::ranges::forward_range); + static_assert(std::weakly_incrementable); + + auto base = Base(begin, end); + auto sv = std::ranges::stride_view(base, 3); + auto it = sv.begin(); + + // Create a ground truth for comparison. + auto expected = sv.begin(); + expected++; + + auto it_after = ++it; + assert(*it_after == *it); + assert(*it_after == *expected); + + it = sv.begin(); + it_after = it; + it_after++; + assert(*it_after == *expected); + + it = sv.begin(); + auto it_to_end = it; + ++it_to_end; // 3 + ++it_to_end; // 6 + ++it_to_end; // 9 + ++it_to_end; // End + + auto it_to_end2 = it; // With a stride of 3, so ++ moves 3 indexes. + it_to_end2 = ++it_to_end2; // 3 + it_to_end2 = ++it_to_end2; // 6 + it_to_end2 = ++it_to_end2; // 9 + it_to_end2 = ++it_to_end2; // End + + assert(it_to_end == it_to_end2); + assert(it_to_end == sv.end()); + + it_to_end = it; + it_to_end++; // 3 + it_to_end++; // 6 + it_to_end++; // 9 + it_to_end++; // End + assert(it_to_end == sv.end()); + + return true; +} + +constexpr bool test_properly_handling_missing() { + // Check whether the "missing" distance to the end gets handled properly. + using Base = BasicTestView; + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto base = Base(arr, arr + 10); + auto strider = std::ranges::stride_view(base, 7); + + auto strider_iter = strider.end(); + + strider_iter--; + assert(*strider_iter == 8); + + // Now that we are back among the valid, we should + // have a normal stride length back (i.e., there is no + // gap between the last stride and the end). + strider_iter--; + assert(*strider_iter == 1); + + strider_iter++; + assert(*strider_iter == 8); + + // By striding past the end, we are going to generate + // another gap between the last stride and the end. + strider_iter++; + assert(strider_iter == strider.end()); + + // Make sure that all sentinel operations work! + assert(strider.end() == std::default_sentinel); + assert(std::default_sentinel == strider.end()); + + assert(strider_iter - std::default_sentinel == 0); + assert(std::default_sentinel - strider.end() == 0); + assert(std::default_sentinel - strider_iter == 0); + + // Let's make sure that the newly regenerated gap gets used. + strider_iter += -2; + assert(*strider_iter == 1); + + return true; +} + +constexpr bool test() { + { + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + test_forward_operator_increment(arr, arr + 11); + } + test_properly_handling_missing(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + // Non-forward iterators can't be tested in a constexpr context. + { + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + test_non_forward_operator_increment(SizedInputIter(arr), SizedInputIter(arr + 3), SizedInputIter(arr + 10)); + } + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp new file mode 100644 index 000000000000..77754e518187 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// friend constexpr range_rvalue_reference_t iter_move(const iterator& i) +// noexcept(noexcept(ranges::iter_move(i.current_))) + +#include +#include +#include + +#include "../types.h" +#include "test_macros.h" + +template +concept iter_moveable = requires(T&& t) { std::ranges::iter_move(t); }; + +constexpr bool test() { + { + // iter_move with a noexcept iter_move on the base iterator. + int a[] = {4, 3, 2, 1}; + + int iter_move_counter(0); + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin(); + + static_assert(iter_moveable>); + ASSERT_SAME_TYPE(int, decltype(std::ranges::iter_move(svb))); + static_assert(noexcept(std::ranges::iter_move(svb))); + + auto&& result = std::ranges::iter_move(svb); + assert(iter_move_counter == 1); + assert(result == 4); + + svb++; + result = std::ranges::iter_move(svb); + assert(iter_move_counter == 2); + assert(result == 3); + } + + { + // iter_move with a potentially-throwing iter_move on the base iterator. + int a[] = {1, 2, 3, 4}; + + int iter_move_counter(0); + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin(); + + static_assert(iter_moveable>); + ASSERT_SAME_TYPE(int, decltype(std::ranges::iter_move(svb))); + static_assert(!noexcept(std::ranges::iter_move(svb))); + + auto&& result = std::ranges::iter_move(svb); + assert(iter_move_counter == 1); + assert(result == 1); + + svb++; + result = std::ranges::iter_move(svb); + assert(iter_move_counter == 2); + assert(result == 2); + } + + { + // iter_move with a non-pointer base iterator (vector::iterator). + std::vector a = {4, 5, 6, 7, 8}; + + int iter_move_counter(0); + using View = IterMoveIterSwapTestRange::iterator, /*IsSwappable=*/true, /*IsNoExcept=*/false>; + + using StrideView = std::ranges::stride_view; + auto svb = StrideView(View(a.begin(), a.end(), &iter_move_counter), 1).begin(); + + static_assert(!noexcept(std::ranges::iter_move(svb))); + + auto&& result = std::ranges::iter_move(svb); + assert(iter_move_counter == 1); + assert(result == 4); + + svb++; + result = std::ranges::iter_move(svb); + assert(iter_move_counter == 2); + assert(result == 5); + } + + { + // Verify return type is a rvalue-reference. + int a[] = {1, 2, 3}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(a, a + 3), 1); + auto it = sv.begin(); + static_assert(std::is_same_v); + assert(std::ranges::iter_move(it) == 1); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp new file mode 100644 index 000000000000..23846a1c2f9d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.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 + +// friend constexpr void iter_swap(const iterator& x, const iterator& y) +// noexcept(noexcept(ranges::iter_swap(x.current_, y.current_))) +// requires indirectly_swappable> + +#include +#include + +#include "../types.h" + +template +concept Swappable = requires(T&& t, T&& u) { std::ranges::iter_swap(t, u); }; + +constexpr bool test() { + { + // iter_swap with a noexcept iter_swap on the base iterator. + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8}; + + int iter_move_counter_one(0); + int iter_move_counter_two(0); + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + auto svba = StrideView(View(a, a + 4, &iter_move_counter_one), 1).begin(); + auto svbb = StrideView(View(b, b + 4, &iter_move_counter_two), 1).begin(); + + static_assert(Swappable>); + static_assert(noexcept(std::ranges::iter_swap(svba, svbb))); + + assert(a[0] == 1); + assert(b[0] == 5); + + std::ranges::iter_swap(svba, svbb); + assert(iter_move_counter_one == 1); + assert(iter_move_counter_two == 1); + + assert(a[0] == 5); + assert(b[0] == 1); + } + + { + // iter_swap with a potentially-throwing iter_swap on the base iterator. + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8}; + + int iter_move_counter_one(0); + int iter_move_counter_two(0); + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + auto svba = StrideView(View(a, a + 4, &iter_move_counter_one), 1).begin(); + auto svbb = StrideView(View(b, b + 4, &iter_move_counter_two), 1).begin(); + + static_assert(Swappable>); + static_assert(!noexcept(std::ranges::iter_swap(svba, svbb))); + + assert(a[0] == 1); + assert(b[0] == 5); + + std::ranges::iter_swap(svba, svbb); + + assert(iter_move_counter_one == 1); + assert(iter_move_counter_two == 1); + assert(a[0] == 5); + assert(b[0] == 1); + } + + { + // iter_swap is not available when the base iterator is not indirectly_swappable. + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + + static_assert(!Swappable>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp new file mode 100644 index 000000000000..04e79f2b40cc --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp @@ -0,0 +1,194 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// friend constexpr difference_type operator-(const iterator& x, const iterator& y) +// friend constexpr difference_type operator-(default_sentinel_t, const iterator& x) +// friend constexpr difference_type operator-(const iterator& x, default_sentinel_t) +// friend constexpr iterator operator-(const iterator& i, difference_type s) + +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanMinus = std::is_same_v() - std::declval())> && + requires(T& t) { t - t; }; + +template +concept CanSentinelMinus = + std::is_same_v() - std::default_sentinel)> && + std::is_same_v())> && requires(T& t) { + t - std::default_sentinel; + std::default_sentinel - t; + }; + +template +concept CanDifferenceMinus = std::is_same_v() - 1)> && requires(T& t) { t - 1; }; + +// Input view: has sentinel minus but not iter-iter minus or difference minus. +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIter = std::ranges::iterator_t>; +static_assert(!CanMinus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); + +// Forward view: has sentinel minus but not iter-iter minus or difference minus. +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIter = std::ranges::iterator_t>; +static_assert(!CanMinus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); + +// Bidirectional view: has sentinel minus but not iter-iter minus or difference minus. +using BidirView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirViewIter = std::ranges::iterator_t>; +static_assert(!CanMinus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); + +// Random access view: has iter-iter minus and difference minus, but no sentinel minus (non-sized sentinel). +using RAView = BasicTestView>; +using StrideViewOverRAViewIter = std::ranges::iterator_t>; +static_assert(CanMinus); +static_assert(CanDifferenceMinus); +static_assert(!CanSentinelMinus); + +template + requires std::sized_sentinel_for && (!std::forward_iterator) +constexpr bool test_non_forward_minus(Iter zero_begin, Iter one_begin, Iter end) { + using Base = BasicTestView; + + auto base_zero = Base(zero_begin, end); + auto base_one = Base(one_begin, end); + auto sv_zero = std::ranges::stride_view(base_zero, 3); + auto sv_one = std::ranges::stride_view(base_one, 3); + + auto begin0 = sv_zero.begin(); + auto mid0 = begin0; + ++mid0; // stride 3 -> index 3 + auto far0 = mid0; + ++far0; // stride 3 -> index 6 + + auto begin1 = sv_one.begin(); + auto mid1 = begin1; + ++mid1; // stride 3 -> index 4 + + // Positive differences (uses ceil for non-forward). + assert(mid0 - begin0 == 1); + assert(far0 - begin0 == 2); + assert(begin1 - begin0 == 1); + assert(mid1 - begin0 == 2); + + // Negative differences. + assert(begin0 - mid0 == -1); + assert(begin0 - far0 == -2); + assert(begin0 - begin1 == -1); + assert(begin0 - mid1 == -2); + + // Sentinel minus. + assert(std::default_sentinel - sv_zero.begin() == std::ranges::distance(sv_zero)); + assert(sv_zero.begin() - std::default_sentinel == -std::ranges::distance(sv_zero)); + assert(std::default_sentinel - sv_zero.end() == 0); + assert(sv_zero.end() - std::default_sentinel == 0); + + return true; +} + +template +constexpr bool test_forward_minus(Iter begin, Iter end) { + using Base = BasicTestView; + + auto base_zero = Base(begin, end); + auto base_one = Base(begin + 1, end); + auto sv_zero = std::ranges::stride_view(base_zero, 3); + auto sv_one = std::ranges::stride_view(base_one, 3); + + auto begin0 = sv_zero.begin(); + auto mid0 = begin0; + ++mid0; // stride 3 -> value 4 + auto far0 = mid0; + ++far0; // stride 3 -> value 7 + + auto begin1 = sv_one.begin(); // value 2 + auto mid1 = begin1; + ++mid1; // value 5 + + // Forward range uses exact division (no ceil). + assert(mid0 - begin0 == 1); + assert(far0 - begin0 == 2); + assert(begin1 - begin0 == 0); + assert(mid1 - begin0 == 1); + + assert(begin0 - mid0 == -1); + assert(begin0 - far0 == -2); + + // Sentinel minus. + assert(std::default_sentinel - sv_zero.begin() == std::ranges::distance(sv_zero)); + assert(sv_zero.begin() - std::default_sentinel == -std::ranges::distance(sv_zero)); + + return true; +} + +constexpr bool test() { + { + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + test_forward_minus(arr, arr + 11); + + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + test_forward_minus(vec.begin(), vec.end()); + } + { + // operator-(iterator, difference_type) -- only for random access ranges. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + using Base = BasicTestView; + + auto base = Base(arr, arr + 10); + auto sv = std::ranges::stride_view(base, 3); + + auto it = sv.begin(); + ++it; + ++it; + ++it; // at index 9 + + auto it2 = it - 2; // back to index 3 + assert(*it2 == 4); + auto it3 = it - 3; // back to index 0 + assert(*it3 == 1); + } + { + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + test_non_forward_minus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10)); + } + { + // Test end - begin on the same view where size % stride != 0. + // 10 elements, stride 3: strided elements at 0,3,6,9. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 10), 3); + auto b = sv.begin(); + auto e = sv.end(); + assert(e - b == 4); + assert(b - e == -4); + assert(b - b == 0); + assert(e - e == 0); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.pass.cpp new file mode 100644 index 000000000000..1f9c2b0d625f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.pass.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr iterator& operator-=(difference_type n) + +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanMinusEqual = std::is_same_v() -= 1)> && requires(T& t) { t -= 1; }; + +// Input view: no -=. +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIter = std::ranges::iterator_t>; +static_assert(!CanMinusEqual); + +// Forward view: no -=. +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIter = std::ranges::iterator_t>; +static_assert(!CanMinusEqual); + +// Bidirectional view: no -=. +using BidirView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirViewIter = std::ranges::iterator_t>; +static_assert(!CanMinusEqual); + +// Random access view: has -=. +using RAView = BasicTestView>; +using StrideViewOverRAViewIter = std::ranges::iterator_t>; +static_assert(CanMinusEqual); + +constexpr bool test() { + { + // Basic -= test with stride 1. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 10), 1); + auto it = sv.begin(); + it += 5; + assert(*it == 6); + it -= 3; + assert(*it == 3); + it -= 2; + assert(*it == 1); + } + { + // -= test with stride 3. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 10), 3); + auto it = sv.begin(); + it += 3; // at index 9 (value 10) + assert(*it == 10); + it -= 1; // at index 6 (value 7) + assert(*it == 7); + it -= 2; // at index 0 (value 1) + assert(*it == 1); + } + { + // -= when the stride doesn't evenly divide the range: stride past the end, then back. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 10), 7); + auto it = sv.end(); + it -= 1; // back to last strided element (value 8) + assert(*it == 8); + it -= 1; // back to first element (value 1) + assert(*it == 1); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp new file mode 100644 index 000000000000..221037a7173a --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr iterator operator+(difference_type n, const iterator& i) +// constexpr iterator operator+(const iterator& i, difference_type n) + +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanPlus = + std::is_same_v() + std::declval())> && + std::is_same_v() + std::declval())> && + requires(T& t, typename T::difference_type u) { t = t + u; } && + requires(T& t, typename T::difference_type u) { t = u + t; }; + +// Make sure that we cannot use + on a stride view iterator and difference_type +// over an input view.(sized sentinel) +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::input_range); +static_assert(!CanPlus); + +// Make sure that we cannot use + on a stride view iterator and difference_type +// over a forward view.(sized sentinel) +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::forward_range); +static_assert(!CanPlus); + +// Make sure that we cannot use + on a stride view iterator and difference_type +// over a bidirectional view. (sized sentinel) +using BidirectionalView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::bidirectional_range); +static_assert(!CanPlus); + +// Make sure that we can use += on a stride view iterator and difference_type +// over a random access view. (non sized sentinel) +template > +using RandomAccessView = BasicTestView; +using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t>>; + +static_assert(std::ranges::random_access_range>); +static_assert(CanPlus); + +constexpr bool test() { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + + auto begin = vec.begin(); + auto end = vec.end(); + auto distance = 4; + + using Base = RandomAccessView::iterator>; + static_assert(std::ranges::random_access_range); + + { + // iterator + distance produces the same element as starting at begin + distance. + auto base_view = Base(begin, end); + auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1); + + auto base_view_offset = Base(begin + distance, end); + auto stride_view_over_base_view_offset = std::ranges::stride_view(base_view_offset, 1); + + assert(*(stride_view_over_base_view.begin() + distance) == *(stride_view_over_base_view_offset.begin())); + } + { + // iterator + 1 with a large stride reaches the same element as iterator + stride with stride 1. + auto base_view = Base(begin, end); + auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1); + auto distance_to_last = (end - 1) - begin; + auto stride_view_over_base_view_big_step = std::ranges::stride_view(base_view, distance_to_last); + + assert(*(stride_view_over_base_view_big_step.begin() + 1) == + *(stride_view_over_base_view.begin() + distance_to_last)); + // iterator + 2 past the end of a large-stride view equals the end of the stride-1 view. + assert((stride_view_over_base_view_big_step.begin() + 2) == (stride_view_over_base_view.end())); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp new file mode 100644 index 000000000000..ce8735e2bf83 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp @@ -0,0 +1,134 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr iterator& operator+=(difference_type n) + +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanPlus = std::is_same_v() += std::declval())> && + requires(T& t, typename T::difference_type u) { t += u; }; + +// Make sure that we cannot use += on a stride view iterator +// over an input view.(sized sentinel) +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::input_range); +static_assert(!CanPlus); + +// Make sure that we cannot use += on a stride view iterator +// over a forward view.(sized sentinel) +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::forward_range); +static_assert(!CanPlus); + +// Make sure that we cannot use += on a stride view iterator +// over a bidirectional view. (sized sentinel) +using BidirectionalView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::bidirectional_range); +static_assert(!CanPlus); + +// Make sure that we can use += on a stride view iterator +// over a random access view. (non sized sentinel) +template > +using RandomAccessView = BasicTestView; +using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t>>; + +static_assert(std::ranges::random_access_range>); +static_assert(CanPlus); + +constexpr bool test() { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + + using Iter = std::vector::iterator; + + auto begin = vec.begin(); + auto end = vec.end(); + + using Base = RandomAccessView; + static_assert(std::ranges::random_access_range); + auto base = Base(begin, end); + + // += with stride 1: advancing by distance matches starting at begin + distance. + { + auto sv = std::ranges::stride_view(base, 1); + auto it = sv.begin(); + auto& result = (it += 4); + assert(&result == &it); + + auto it2 = std::ranges::stride_view(Base(begin + 4, end), 1).begin(); + assert(*it == *it2); + } + + // += past the end, then -= back: the remainder is handled correctly. + { + auto sv = std::ranges::stride_view(base, (end - begin) - 1); + auto it = sv.begin(); + + // This += should move us into a position where the stride doesn't evenly divide the range. + // Do a -= 1 here to confirm that the remainder is taken into account. + it += 2; + it -= 1; + assert(*it == *(sv.begin() + 1)); + } + + // += 0 is a no-op. + { + auto sv = std::ranges::stride_view(base, 3); + auto it = sv.begin(); + it += 1; // at index 3 + assert(*it == *(begin + 3)); + it += 0; + assert(*it == *(begin + 3)); + } + + // += with negative n. + { + auto sv = std::ranges::stride_view(base, 3); + auto it = sv.begin(); + it += 3; // at index 9 + assert(*it == *(begin + 9)); + it += -2; // back to index 3 + assert(*it == *(begin + 3)); + it += -1; // back to index 0 + assert(*it == *begin); + } + + // += negative from end. + { + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + using CommonBase = BasicTestView; + auto sv = std::ranges::stride_view(CommonBase(arr, arr + 11), 3); + auto it = sv.end(); + it += -1; // last strided element (index 9) + assert(*it == 10); + it += -3; // back to begin (index 0) + assert(*it == 1); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp new file mode 100644 index 000000000000..c22d6afe4e46 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr decltype(auto) operator[](difference_type n) const + +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanSubscript = requires(T& t) { t[0]; }; + +// Input view: no subscript. +using InputView = BasicTestView, sized_sentinel>>; +using StrideInputIter = std::ranges::iterator_t>; +static_assert(!CanSubscript); + +// Forward view: no subscript. +using FwdView = BasicTestView, sized_sentinel>>; +using StrideFwdIter = std::ranges::iterator_t>; +static_assert(!CanSubscript); + +// Bidirectional view: no subscript. +using BidirView = BasicTestView, sized_sentinel>>; +using StrideBidirIter = std::ranges::iterator_t>; +static_assert(!CanSubscript); + +// Random access view: subscript available. +using RAView = BasicTestView>; +using StrideRAIter = std::ranges::iterator_t>; +static_assert(CanSubscript); + +constexpr bool test() { + { + // Subscript with stride 1. + int arr[] = {10, 20, 30, 40, 50}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 5), 1); + auto it = sv.begin(); + + assert(it[0] == 10); + assert(it[1] == 20); + assert(it[2] == 30); + assert(it[3] == 40); + assert(it[4] == 50); + } + { + // Subscript with stride 2. + int arr[] = {10, 20, 30, 40, 50}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 5), 2); + auto it = sv.begin(); + + assert(it[0] == 10); + assert(it[1] == 30); + assert(it[2] == 50); + } + { + // Subscript with stride 3. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 10), 3); + auto it = sv.begin(); + + assert(it[0] == 1); + assert(it[1] == 4); + assert(it[2] == 7); + assert(it[3] == 10); + } + { + // Subscript from a non-begin position. + int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 10), 3); + auto it = sv.begin(); + ++it; // now at index 3 (value 4) + + assert(it[0] == 4); + assert(it[1] == 7); + assert(it[2] == 10); + assert(it[-1] == 1); + } + { + // Verify return type is a reference. + int arr[] = {1, 2, 3}; + using Base = BasicTestView; + auto sv = std::ranges::stride_view(Base(arr, arr + 3), 1); + auto it = sv.begin(); + static_assert(std::is_same_v); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/types.compile.pass.cpp new file mode 100644 index 000000000000..1e07e86045b6 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/types.compile.pass.cpp @@ -0,0 +1,140 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// stride_view::iterator::difference_type +// stride_view::iterator::value_type +// stride_view::iterator::iterator_concept +// stride_view::iterator::iterator_category + +#include +#include + +#include "test_iterators.h" +#include "../types.h" + +template +concept HasIteratorCategory = requires { typename T::iterator_category; }; + +template +using StrideViewFor = std::ranges::stride_view>>; + +template +using StrideIteratorFor = std::ranges::iterator_t>; + +struct ForwardIteratorWithInputCategory { + using difference_type = int; + using value_type = int; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + ForwardIteratorWithInputCategory(); + ForwardIteratorWithInputCategory& operator++(); + ForwardIteratorWithInputCategory operator++(int); + int& operator*() const; + friend bool operator==(ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory); +}; +static_assert(std::forward_iterator); + +// Non-simple view: forward when non-const, raw pointer when const. +// This exposes the bug where iterator_category is always derived from +// the non-const view's iterator instead of maybe-const. +struct DifferentCategoryView : std::ranges::view_base { + forward_iterator begin(); + forward_iterator end(); + const int* begin() const; + const int* end() const; +}; + +// Non-simple view: input-only when non-const, forward when const. +// Tests that iterator_category is absent for the non-const iterator +// but present for the const iterator. +struct ForwardWhenConstView : std::ranges::view_base { + cpp17_input_iterator begin(); + sentinel_wrapper> end(); + forward_iterator begin() const; + forward_iterator end() const; +}; + +void f() { + // Check that value_type is range_value_t and difference_type is range_difference_t + { + auto check = [] { + using StrideView = StrideViewFor; + using StrideIterator = StrideIteratorFor; + static_assert(std::is_same_v>); + static_assert( + std::is_same_v>); + }; + check.operator()>(); + check.operator()>(); + check.operator()>(); + check.operator()>(); + check.operator()>(); + check.operator()(); + } + + // Check iterator_concept for various categories of ranges + { + static_assert( + std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert( + std::is_same_v>::iterator_concept, std::forward_iterator_tag>); + static_assert(std::is_same_v::iterator_concept, + std::forward_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, + std::bidirectional_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, + std::random_access_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, + std::random_access_iterator_tag>); + static_assert(std::is_same_v::iterator_concept, std::random_access_iterator_tag>); + } + + // Check iterator_category for various categories of ranges + { + static_assert(!HasIteratorCategory>>); + static_assert( + std::is_same_v>::iterator_category, std::forward_iterator_tag>); + static_assert(std::is_same_v::iterator_category, + std::input_iterator_tag>); + static_assert(std::is_same_v>::iterator_category, + std::bidirectional_iterator_tag>); + static_assert(std::is_same_v>::iterator_category, + std::random_access_iterator_tag>); + static_assert(std::is_same_v>::iterator_category, + std::random_access_iterator_tag>); + static_assert(std::is_same_v::iterator_category, std::random_access_iterator_tag>); + } + + // Check that const vs non-const iterators get the correct iterator_category + // when the view has different iterator categories for const and non-const. + { + using SV = std::ranges::stride_view; + using NonConstIter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + // Check that iterator_category presence/absence depends on the correct + // const-qualified Base, not always the non-const view. + { + using SV = std::ranges::stride_view; + using NonConstIter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + + static_assert(!HasIteratorCategory); + static_assert(HasIteratorCategory); + static_assert(std::is_same_v); + } +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp new file mode 100644 index 000000000000..f5c788c26ce6 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto size() requires sized_range +// constexpr auto size() const requires sized_range + +#include +#include + +#include "test_iterators.h" +#include "types.h" + +// There is no size member function on a stride view over a view that +// is *not* a sized range +static_assert(!std::ranges::sized_range>>); +static_assert(!std::ranges::sized_range>>>); + +// There is a size member function on a stride view over a view that +// *is* a sized range +static_assert(std::ranges::sized_range, /*IsSized=*/true>>); +static_assert( + std::ranges::sized_range, /*IsSized=*/true>>>); + +constexpr bool test() { + { + // Test with stride as exact multiple of number of elements in view strided over. + auto iota = std::views::iota(0, 12); + static_assert(std::ranges::sized_range); + auto strided = std::views::stride(iota, 3); + static_assert(std::ranges::sized_range); + assert(strided.size() == 4); + } + + { + // Test with stride as inexact multiple of number of elements in view strided over. + auto iota = std::views::iota(0, 22); + static_assert(std::ranges::sized_range); + auto strided = std::views::stride(iota, 3); + static_assert(std::ranges::sized_range); + assert(strided.size() == 8); + } + { + // Empty range. + auto strided = std::views::iota(0, 0) | std::views::stride(3); + assert(strided.size() == 0); + } + { + // Stride larger than range size. + auto strided = std::views::iota(0, 3) | std::views::stride(10); + assert(strided.size() == 1); + } + { + // Stride equal to range size. + auto strided = std::views::iota(0, 5) | std::views::stride(5); + assert(strided.size() == 1); + } + { + // Stride of 1. + auto strided = std::views::iota(0, 7) | std::views::stride(1); + assert(strided.size() == 7); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp new file mode 100644 index 000000000000..6ec8b834eb9b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// constexpr range_difference_t stride() const noexcept; + +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" +#include "types.h" + +constexpr bool test() { + using View = BasicTestView>; + int arr[] = {1, 2, 3}; + View view(cpp17_input_iterator(arr), cpp17_input_iterator(arr + 3)); + + const std::ranges::stride_view strided(view, 2); + static_assert(noexcept(strided.stride())); + ASSERT_SAME_TYPE(std::ranges::range_difference_t, decltype(strided.stride())); + assert(strided.stride() == 2); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h new file mode 100644 index 000000000000..5a8d6b6ca2f7 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h @@ -0,0 +1,290 @@ +//===----------------------------------------------------------------------===// +// +// 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_STRIDE_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H + +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_range.h" + +// Concepts + +template +concept IterDifferable = std::invocable, Iter, Iter>; + +// Iterators + +// The base for an iterator that keeps a count of the times that it is +// moved and copied. +template +struct IterBase { + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + + int* move_counter = nullptr; + int* copy_counter = nullptr; + + Iter value_{}; + + constexpr IterBase() = default; + constexpr explicit IterBase(Iter value) : value_(value) {} + + constexpr IterBase(const IterBase& other) noexcept { + copy_counter = other.copy_counter; + move_counter = other.move_counter; + if (copy_counter != nullptr) { + (*copy_counter)++; + } + value_ = other.value_; + } + + constexpr IterBase(IterBase&& other) noexcept { + copy_counter = other.copy_counter; + move_counter = other.move_counter; + if (move_counter != nullptr) { + (*move_counter)++; + } + value_ = std::move(other.value_); + } + constexpr IterBase& operator=(const IterBase& other) = default; + constexpr IterBase& operator=(IterBase&& other) = default; +}; + +// The base for an input iterator that keeps a count of the times that it is +// moved and copied. +template + requires((!IsSized) || (IsSized && IterDifferable)) +struct InputIter : IterBase { + using Base = IterBase; + + using typename Base::difference_type; + using typename Base::value_type; + + using iterator_concept = std::input_iterator_tag; + using iterator_category = std::input_iterator_tag; + + using Base::Base; + + constexpr value_type operator*() const { return *Base::value_; } + constexpr Derived& operator++() { + Base::value_++; + return static_cast(*this); + } + constexpr Derived operator++(int) { + auto nv = *this; + Base::value_++; + return nv; + } + friend constexpr bool operator==(const Derived& left, const Derived& right) { return left.value_ == right.value_; } + friend constexpr difference_type operator-(const Derived& left, const Derived& right) + requires IsSized + { + return left.value_ - right.value_; + } +}; + +// In input iterator that is sized. +struct SizedInputIter : InputIter { + using InputIter::InputIter; +}; +static_assert(std::input_iterator); +static_assert(std::sized_sentinel_for); + +// Views + +// Put IterMoveIterSwapTestRangeIterator in a namespace to test ADL of CPOs iter_swap and iter_move +// (see iter_swap.pass.cpp and iter_move.pass.cpp). +namespace adl { +template +struct IterMoveIterSwapTestRangeIterator + : InputIter, Iter, false> { + int* counter_{nullptr}; + + using InputIter, Iter, false>:: + InputIter; + + constexpr IterMoveIterSwapTestRangeIterator(Iter value, int* counter) + : InputIter, Iter, false>(value), + counter_(counter) {} + + friend constexpr void iter_swap(IterMoveIterSwapTestRangeIterator t, IterMoveIterSwapTestRangeIterator u) noexcept + requires IsIterSwappable && IsNoExceptIterMoveable + { + (*t.counter_)++; + (*u.counter_)++; + std::swap(*t.value_, *u.value_); + } + + friend constexpr void iter_swap(IterMoveIterSwapTestRangeIterator t, IterMoveIterSwapTestRangeIterator u) + requires IsIterSwappable && (!IsNoExceptIterMoveable) + { + (*t.counter_)++; + (*u.counter_)++; + std::swap(*t.value_, *u.value_); + } + + friend constexpr auto iter_move(const IterMoveIterSwapTestRangeIterator& t) + requires(!IsNoExceptIterMoveable) + { + (*t.counter_)++; + return *t.value_; + } + friend constexpr auto iter_move(const IterMoveIterSwapTestRangeIterator& t) noexcept + requires IsNoExceptIterMoveable + { + (*t.counter_)++; + return *t.value_; + } +}; +} // namespace adl + +template +struct IterMoveIterSwapTestRange : std::ranges::view_base { + adl::IterMoveIterSwapTestRangeIterator begin_; + adl::IterMoveIterSwapTestRangeIterator end_; + constexpr IterMoveIterSwapTestRange(const Iter& begin, const Iter& end, int* counter) + : begin_(adl::IterMoveIterSwapTestRangeIterator(begin, counter)), + end_(adl::IterMoveIterSwapTestRangeIterator(end, counter)) {} + constexpr adl::IterMoveIterSwapTestRangeIterator begin() const { return begin_; } + constexpr adl::IterMoveIterSwapTestRangeIterator end() const { return end_; } +}; + +// Views + +// Depending upon configuration, ViewOrRange is either a View or not. +template +struct MaybeView {}; +template <> +struct MaybeView : std::ranges::view_base {}; + +template Sent = sentinel_wrapper, + bool IsSized = false, + bool IsView = false, + bool IsCopyable = false > + requires((!IsSized) || (IsSized && IterDifferable)) +struct BasicTestViewOrRange : MaybeView { + Iter begin_{}; + Iter end_{}; + + constexpr BasicTestViewOrRange(const Iter& b, const Iter& e) : begin_(b), end_(e) {} + + constexpr Iter begin() { return begin_; } + constexpr Iter begin() const { return begin_; } + constexpr Sent end() { return Sent{end_}; } + constexpr Sent end() const { return Sent{end_}; } + + constexpr auto size() const + requires IsSized + { + return begin_ - end_; + } + + constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other) = default; + constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default; + + constexpr BasicTestViewOrRange(const BasicTestViewOrRange&) + requires(!IsCopyable) + = delete; + constexpr BasicTestViewOrRange(const BasicTestViewOrRange&) + requires IsCopyable + = default; + + constexpr BasicTestViewOrRange& operator=(const BasicTestViewOrRange&) + requires(!IsCopyable) + = delete; + constexpr BasicTestViewOrRange& operator=(const BasicTestViewOrRange&) + requires IsCopyable + = default; +}; + +template Sent = sentinel_wrapper, bool IsSized = false> + requires((!IsSized) || (IsSized && IterDifferable)) +using BasicTestView = BasicTestViewOrRange; + +template Sent = sentinel_wrapper, bool IsCopyable = true> +using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange; + +static_assert(std::ranges::view>>); +static_assert(std::ranges::view, + sentinel_wrapper>, + false>>); + +static_assert(std::copyable>>); +template Sent = sentinel_wrapper> +using CopyableView = MaybeCopyableAlwaysMoveableView; +static_assert(std::copyable>>); + +template Sent = sentinel_wrapper> +using MoveOnlyView = MaybeCopyableAlwaysMoveableView; +static_assert(!std::copyable>>); + +template +struct MaybeConstCommonSimpleView : std::ranges::view_base { + int* begin(); + int* begin() const + requires(IsConst && IsSimple); + double* begin() const + requires(IsConst && !IsSimple); + + int* end() + requires(IsCommon); + void* end() + requires(!IsCommon); + + int* end() const + requires(IsConst && IsCommon && IsSimple); + double* end() const + requires(IsConst && IsCommon && !IsSimple); + + void* end() const + requires(IsConst && !IsCommon); + + size_t size() const + requires(IsSized); +}; + +using UnSimpleNoConstCommonView = MaybeConstCommonSimpleView; +using UnSimpleConstView = MaybeConstCommonSimpleView; +using UnsimpleUnCommonConstView = MaybeConstCommonSimpleView; +using SimpleUnCommonConstView = MaybeConstCommonSimpleView; +using SimpleCommonConstView = MaybeConstCommonSimpleView; + +// Don't move/hold the iterator itself, copy/hold the base +// of that iterator and reconstruct the iterator on demand. +// May result in aliasing (if, e.g., Iterator is an iterator +// over int *). +template Sent = sentinel_wrapper> +struct ViewOverNonCopyableIterator : std::ranges::view_base { + constexpr explicit ViewOverNonCopyableIterator(Iter it, Sent sent) : it_(base(it)), sent_(base(sent)) {} + + ViewOverNonCopyableIterator(ViewOverNonCopyableIterator&&) = default; + ViewOverNonCopyableIterator& operator=(ViewOverNonCopyableIterator&&) = default; + + constexpr Iter begin() const { return Iter(it_); } + constexpr Sent end() const { return Sent(sent_); } + +private: + decltype(base(std::declval())) it_; + decltype(base(std::declval())) sent_; +}; + +// Ranges + +template Sent = sentinel_wrapper, bool IsSized = false> + requires((!IsSized) || (IsSized && IterDifferable)) +using BasicTestRange = BasicTestViewOrRange; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 8f421e8cbd0f..7bb78cc96b4e 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1173,6 +1173,11 @@ feature_test_macros = [ "values": {"c++23": 202106}, "headers": ["algorithm"], }, + { + "name": "__cpp_lib_ranges_stride", + "values": {"c++23": 202207}, + "headers": ["ranges"], + }, { "name": "__cpp_lib_ranges_to_container", "values": {"c++23": 202202},