[libcxx-commits] [libcxx] WIP: [libc++][ranges] Implement `ranges::stride_view`. (PR #65200)
Will Hawkins via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Sep 4 07:20:50 PDT 2023
https://github.com/hawkinsw updated https://github.com/llvm/llvm-project/pull/65200:
>From 9855991cf6294b8de694ec9f24c6fab0094c84e2 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Tue, 8 Aug 2023 01:04:17 -0400
Subject: [PATCH 1/2] WIP: [libc++][ranges] Implement `ranges::stride_view`.
Differential Revision: https://reviews.llvm.org/D156924
Signed-off-by: Will Hawkins <hawkinsw at obs.cr>
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__ranges/stride_view.h | 421 ++++++++++++++++++
libcxx/include/module.modulemap.in | 1 +
libcxx/include/ranges | 1 +
libcxx/include/version | 2 +
libcxx/modules/std/ranges.inc | 14 +-
.../ranges.version.compile.pass.cpp | 31 ++
.../version.version.compile.pass.cpp | 31 ++
.../range.stride.view/adaptor.pass.cpp | 193 ++++++++
.../range.stride.view/base.pass.cpp | 70 +++
.../range.stride.view/begin.pass.cpp | 22 +
.../range.stride.view/ctad.compile.pass.cpp | 22 +
.../enable_borrowed_range.compile.pass.cpp | 32 ++
.../range.stride.view/end.pass.cpp | 22 +
.../iterator/ctor.default.pass.cpp | 49 ++
.../range.stride.view/iterator/equal.pass.cpp | 89 ++++
.../sentinel/ctor.default.pass.cpp | 22 +
.../range.stride.view/sentinel/equal.pass.cpp | 22 +
.../range.stride.view/size.pass.cpp | 46 ++
.../range.adaptors/range.stride.view/test.h | 183 ++++++++
.../generate_feature_test_macro_components.py | 5 +
22 files changed, 1274 insertions(+), 7 deletions(-)
create mode 100644 libcxx/include/__ranges/stride_view.h
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/ctad.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.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/ctor.default.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/sentinel/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/equal.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/test.h
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 63abcbb886ab222..7ee5bf502712cc1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -358,6 +358,8 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_ranges_starts_ends_with`` *unimplemented*
--------------------------------------------------- -----------------
+ ``__cpp_lib_ranges_stride`` ``202207L``
+ --------------------------------------------------- -----------------
``__cpp_lib_ranges_to_container`` ``202202L``
--------------------------------------------------- -----------------
``__cpp_lib_ranges_zip`` *unimplemented*
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 77a7269121ec142..8e322367e6b7f53 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -633,6 +633,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 000000000000000..880c8ce2950e470
--- /dev/null
+++ b/libcxx/include/__ranges/stride_view.h
@@ -0,0 +1,421 @@
+// -*- 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 <__config>
+
+#include <__compare/three_way_comparable.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#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 <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/subrange.h>
+#include <__ranges/view_interface.h>
+#include <__ranges/views.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/maybe_const.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Value>
+_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
+ _Value __r = __left / __right;
+ if (__left % __right) {
+ __r++;
+ }
+ return __r;
+}
+
+template <input_range _View>
+ requires view<_View>
+class stride_view : public view_interface<stride_view<_View>> {
+ _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+ range_difference_t<_View> __stride_ = 0;
+
+ template <bool _Const>
+ class __iterator;
+ template <bool _Const>
+ class __sentinel;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit stride_view(_View __base, range_difference_t<_View> __stride)
+ : __base_(std::move(__base)), __stride_(__stride) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+ requires copy_constructible<_View>
+ {
+ return __base_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto stride() { return __stride_; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+ requires ranges::sized_range<_View>
+ {
+ return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+ requires ranges::sized_range<const _View>
+ {
+ return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+ requires(!__simple_view<_View>)
+ {
+ return __iterator<false>{*this, ranges::begin(__base_)};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+ requires ranges::range<const _View>
+ {
+ return __iterator<true>{*this, ranges::begin(__base_)};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+ requires(!__simple_view<_View> && common_range<_View> && sized_range<_View> && forward_range<_View>)
+ {
+ auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
+ return __iterator<false>{*this, ranges::end(__base_), __missing};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+ requires(!__simple_view<_View> && common_range<_View> && !bidirectional_range<_View>)
+ {
+ return __iterator<false>{*this, ranges::end(__base_)};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+ requires(!__simple_view<_View>)
+ {
+ return std::default_sentinel;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires(range<const _View> && common_range<const _View> && sized_range<const _View> && forward_range<const _View>)
+ {
+ auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
+ return __iterator<true>{*this, ranges::end(__base_), __missing};
+ }
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires(range<const _View> && common_range<_View> && !bidirectional_range<_View>)
+ {
+ return __iterator<true>{*this, ranges::end(__base_)};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires(range<const _View>)
+ {
+ return std::default_sentinel;
+ }
+}; // class stride_view
+
+template <class _View>
+struct __stride_iterator_category {};
+
+template <forward_range _View>
+struct __stride_iterator_category<_View> {
+ using _Cat = typename iterator_traits<iterator_t<_View>>::iterator_category;
+ using iterator_category =
+ _If<derived_from<_Cat, random_access_iterator_tag>,
+ random_access_iterator_tag,
+ /* else */ _Cat >;
+};
+
+template <input_range _View>
+ requires view<_View>
+template <bool _Const>
+class stride_view<_View>::__iterator : public __stride_iterator_category<_View> {
+ using _Parent = __maybe_const<_Const, stride_view<_View>>;
+ using _Base = __maybe_const<_Const, _View>;
+
+public:
+ using difference_type = range_difference_t<_Base>;
+ using value_type = range_value_t<_Base>;
+ using iterator_concept =
+ _If<random_access_range<_Base>,
+ random_access_iterator_tag,
+ _If<bidirectional_range<_Base>,
+ bidirectional_iterator_tag,
+ _If<forward_range<_Base>,
+ forward_iterator_tag,
+ /* else */ input_iterator_tag >>>;
+
+ _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_Base> __current_ = iterator_t<_Base>();
+ _LIBCPP_NO_UNIQUE_ADDRESS ranges::sentinel_t<_Base> __end_ = ranges::sentinel_t<_Base>();
+ difference_type __stride_ = 0;
+ difference_type __missing_ = 0;
+
+ // using iterator_category = inherited;
+ _LIBCPP_HIDE_FROM_ABI __iterator()
+ requires default_initializable<iterator_t<_Base>>
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+ requires _Const && std::convertible_to<ranges::iterator_t<_View>, ranges::iterator_t<_Base>> &&
+ std::convertible_to<ranges::sentinel_t<_View>, ranges::sentinel_t<_Base>>
+ : __current_(std::move(__i.__current_)),
+ __end_(std::move(__i.__end_)),
+ __stride_(__i.__stride_),
+ __missing_(__i.__missing_) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+ _Parent& __parent, ranges::iterator_t<_Base> __current, difference_type __missing = 0)
+ : __current_(std::move(__current)),
+ __end_(ranges::end(__parent.__base_)),
+ __stride_(__parent.__stride_),
+ __missing_(__missing) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__current_; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+ __missing_ = ranges::advance(__current_, __stride_, __end_);
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
+ requires forward_range<_Base>
+ {
+ 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 __s)
+ requires random_access_range<_Base>
+ {
+ if (__s > 0) {
+ ranges::advance(__current_, __stride_ * (__s - 1));
+ __missing_ = ranges::advance(__current_, __stride_, __end_);
+ } else if (__s < 0) {
+ ranges::advance(__current_, __stride_ * __s + __missing_);
+ __missing_ = 0;
+ }
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __s)
+ requires random_access_range<_Base>
+ {
+ return *this += -__s;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator[](difference_type __s) const
+ requires random_access_range<_Base>
+ {
+ return *(*this + __s);
+ }
+
+ _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<iterator_t<_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 __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 bool operator<=>(__iterator const& __x, __iterator const& __y)
+ requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+ {
+ return __x.__current_ <=> __y.__current_;
+ }
+
+ _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;
+ }
+ _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;
+ }
+
+ _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;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(__iterator const& __x, __iterator const& __y)
+ requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>> && forward_range<_Base>
+ {
+ auto __n = __x.__current_ - __y.__current_;
+ return (__n + __x.__missing_ - __y.__missing_) / __x.__stride_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(__iterator const& __x, __iterator const& __y)
+ requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+ {
+ auto __n = __x.__current_ - __y.__current_;
+ if (__n < 0) {
+ return -ranges::__div_ceil(-__n, __x.__stride_);
+ }
+ return ranges::__div_ceil(__n, __x.__stride_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(default_sentinel_t, __iterator const& __x)
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+ {
+ return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+ }
+ _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(__iterator const& __x, default_sentinel_t __y)
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+ {
+ return -(__y - __x);
+ }
+
+ _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<iterator_t<_Base>>
+ {
+ return ranges::iter_swap(__x.__current_, __y.__current_);
+ }
+}; // class stride_view::__iterator
+
+template <input_range _View>
+ requires view<_View>
+template <bool _Const>
+class stride_view<_View>::__sentinel {
+public:
+ sentinel_t<_View> __end_ = sentinel_t<_View>();
+
+ _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(stride_view<_View>& __parent)
+ : __end_(ranges::end(__parent.__base_)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator<true> const& __x, __sentinel const& __y) {
+ return __x.__current_ == __y.__end_;
+ }
+}; // class stride_view::__sentinel
+
+template <class _Range>
+stride_view(_Range&&) -> stride_view<views::all_t<_Range>>;
+
+namespace views {
+namespace __stride {
+// removed this.
+struct __fn {
+ template <viewable_range _Range>
+ [[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};
+ }
+
+ template <class _Np>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Np&& __n) const {
+ return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Np>(__n)));
+ }
+};
+} // namespace __stride
+
+inline namespace __cpo {
+inline constexpr auto stride = __stride::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_STRIDE_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3b85b6a91a21d87..a108d57e3fb387f 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1711,6 +1711,7 @@ module std_private_ranges_size [system] {
export std_private_type_traits_make_unsigned
}
module std_private_ranges_split_view [system] { header "__ranges/split_view.h" }
+module std_private_ranges_stride_view [system] { header "__ranges/stride_view.h" }
module std_private_ranges_subrange [system] {
header "__ranges/subrange.h"
export std_private_ranges_subrange_fwd
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 523f7cdcb360fa3..95d85a92c7b1bad 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -398,6 +398,7 @@ namespace std {
#include <__ranges/single_view.h>
#include <__ranges/size.h>
#include <__ranges/split_view.h>
+#include <__ranges/stride_view.h>
#include <__ranges/subrange.h>
#include <__ranges/take_view.h>
#include <__ranges/take_while_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index db6da2e0a8bd151..357f443cf9c4c2e 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -161,6 +161,7 @@ __cpp_lib_ranges_join_with 202202L <ranges>
__cpp_lib_ranges_repeat 202207L <ranges>
__cpp_lib_ranges_slide 202202L <ranges>
__cpp_lib_ranges_starts_ends_with 202106L <algorithm>
+__cpp_lib_ranges_stride 202207L <ranges>
__cpp_lib_ranges_to_container 202202L <deque> <forward_list> <list>
<map> <queue> <ranges>
<set> <stack> <string>
@@ -441,6 +442,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
# 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 a6b1bf5496251a8..6d1935dce952de5 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -266,6 +266,13 @@ export namespace std {
using std::ranges::views::zip;
} // namespace views
+ // [range.stride], stride view
+ using std::ranges::stride_view;
+
+ namespace views {
+ using std::ranges::views::stride;
+ }
+
#if 0
// [range.zip.transform], zip transform view
using std::ranges::zip_transform_view;
@@ -309,13 +316,6 @@ export namespace std {
using std::ranges::views::chunk_by;
}
- // [range.stride], stride view
- using std::ranges::stride_view;
-
- namespace views {
- using std::ranges::views::stride;
- }
-
using std::ranges::cartesian_product_view;
namespace views {
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 4a439ae90fcc0f3..7f2eabba5ccf2aa 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
@@ -23,6 +23,7 @@
__cpp_lib_ranges_join_with 202202L [C++23]
__cpp_lib_ranges_repeat 202207L [C++23]
__cpp_lib_ranges_slide 202202L [C++23]
+ __cpp_lib_ranges_stride 202207L [C++23]
__cpp_lib_ranges_to_container 202202L [C++23]
__cpp_lib_ranges_zip 202110L [C++23]
*/
@@ -60,6 +61,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
@@ -98,6 +103,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
@@ -136,6 +145,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
@@ -177,6 +190,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
@@ -260,6 +277,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
@@ -355,6 +379,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 8903913027db73f..ab1b22919d3fb1d 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
@@ -149,6 +149,7 @@
__cpp_lib_ranges_repeat 202207L [C++23]
__cpp_lib_ranges_slide 202202L [C++23]
__cpp_lib_ranges_starts_ends_with 202106L [C++23]
+ __cpp_lib_ranges_stride 202207L [C++23]
__cpp_lib_ranges_to_container 202202L [C++23]
__cpp_lib_ranges_zip 202110L [C++23]
__cpp_lib_ratio 202306L [C++26]
@@ -721,6 +722,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
@@ -1479,6 +1484,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
@@ -2408,6 +2417,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
@@ -3607,6 +3620,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
@@ -5043,6 +5060,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
@@ -6590,6 +6614,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/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 000000000000000..8ad758a953426dd
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
@@ -0,0 +1,193 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// ranges
+
+// std::views::stride_view
+
+#include "test.h"
+#include <iterator>
+#include <ranges>
+#include <utility>
+
+template <class View, class T>
+concept CanBePiped = requires(View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+constexpr bool test() {
+ int arr[] = {1, 2, 3};
+
+ // Simple use cases.
+ {
+ {
+ BidirRange view(arr, arr + 3);
+ std::ranges::stride_view<BidirRange> strided(view, 1);
+ auto strided_iter = strided.begin();
+
+ assert(*strided_iter == arr[0]);
+
+ std::ranges::advance(strided_iter, 2);
+ assert(*strided_iter == arr[2]);
+ }
+ {
+ BidirRange view(arr, arr + 3);
+ std::ranges::stride_view<BidirRange> strided(view, 2);
+ auto strided_iter = strided.begin();
+
+ assert(*strided_iter == arr[0]);
+
+ std::ranges::advance(strided_iter, 1);
+ assert(*strided_iter == arr[2]);
+ }
+ }
+
+#if 0
+ // views::reverse(x) is equivalent to subrange{end, begin, size} if x is a
+ // sized subrange over reverse iterators
+ {
+ using It = bidirectional_iterator<int*>;
+ using Subrange = std::ranges::subrange<It, It, std::ranges::subrange_kind::sized>;
+
+ using ReverseIt = std::reverse_iterator<It>;
+ using ReverseSubrange = std::ranges::subrange<ReverseIt, ReverseIt, std::ranges::subrange_kind::sized>;
+
+ {
+ BidirRange view(buf, buf + 3);
+ ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
+ std::same_as<Subrange> auto result = std::views::reverse(subrange);
+ assert(base(result.begin()) == buf);
+ assert(base(result.end()) == buf + 3);
+ }
+ {
+ // std::move into views::reverse
+ BidirRange view(buf, buf + 3);
+ ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
+ std::same_as<Subrange> auto result = std::views::reverse(std::move(subrange));
+ assert(base(result.begin()) == buf);
+ assert(base(result.end()) == buf + 3);
+ }
+ {
+ // with a const subrange
+ BidirRange view(buf, buf + 3);
+ ReverseSubrange const subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
+ std::same_as<Subrange> auto result = std::views::reverse(subrange);
+ assert(base(result.begin()) == buf);
+ assert(base(result.end()) == buf + 3);
+ }
+ }
+
+ // views::reverse(x) is equivalent to subrange{end, begin} if x is an
+ // unsized subrange over reverse iterators
+ {
+ using It = bidirectional_iterator<int*>;
+ using Subrange = std::ranges::subrange<It, It, std::ranges::subrange_kind::unsized>;
+
+ using ReverseIt = std::reverse_iterator<It>;
+ using ReverseSubrange = std::ranges::subrange<ReverseIt, ReverseIt, std::ranges::subrange_kind::unsized>;
+
+ {
+ BidirRange view(buf, buf + 3);
+ ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
+ std::same_as<Subrange> auto result = std::views::reverse(subrange);
+ assert(base(result.begin()) == buf);
+ assert(base(result.end()) == buf + 3);
+ }
+ {
+ // std::move into views::reverse
+ BidirRange view(buf, buf + 3);
+ ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
+ std::same_as<Subrange> auto result = std::views::reverse(std::move(subrange));
+ assert(base(result.begin()) == buf);
+ assert(base(result.end()) == buf + 3);
+ }
+ {
+ // with a const subrange
+ BidirRange view(buf, buf + 3);
+ ReverseSubrange const subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
+ std::same_as<Subrange> auto result = std::views::reverse(subrange);
+ assert(base(result.begin()) == buf);
+ assert(base(result.end()) == buf + 3);
+ }
+ }
+
+ // Otherwise, views::reverse(x) is equivalent to ranges::reverse_view{x}
+ {
+ BidirRange view(buf, buf + 3);
+ std::same_as<std::ranges::reverse_view<BidirRange>> auto result = std::views::reverse(view);
+ assert(base(result.begin().base()) == buf + 3);
+ assert(base(result.end().base()) == buf);
+ }
+
+ // Test that std::views::reverse is a range adaptor
+ {
+ // Test `v | views::reverse`
+ {
+ BidirRange view(buf, buf + 3);
+ std::same_as<std::ranges::reverse_view<BidirRange>> auto result = view | std::views::reverse;
+ assert(base(result.begin().base()) == buf + 3);
+ assert(base(result.end().base()) == buf);
+ }
+
+ // Test `adaptor | views::reverse`
+ {
+ BidirRange view(buf, buf + 3);
+ auto f = [](int i) { return i; };
+ auto const partial = std::views::transform(f) | std::views::reverse;
+ using Result = std::ranges::reverse_view<std::ranges::transform_view<BidirRange, decltype(f)>>;
+ std::same_as<Result> auto result = partial(view);
+ assert(base(result.begin().base().base()) == buf + 3);
+ assert(base(result.end().base().base()) == buf);
+ }
+
+ // Test `views::reverse | adaptor`
+ {
+ BidirRange view(buf, buf + 3);
+ auto f = [](int i) { return i; };
+ auto const partial = std::views::reverse | std::views::transform(f);
+ using Result = std::ranges::transform_view<std::ranges::reverse_view<BidirRange>, decltype(f)>;
+ std::same_as<Result> auto result = partial(view);
+ assert(base(result.begin().base().base()) == buf + 3);
+ assert(base(result.end().base().base()) == buf);
+ }
+ }
+#endif // big block
+
+ // From:
+ // Test that std::views::reverse is a range adaptor
+ // Check SFINAE friendliness
+ {
+ struct NotAViewableRange {};
+ struct NotABidirRange {};
+ // Not invocable because there is no parameter.
+ static_assert(!std::is_invocable_v<decltype(std::views::stride)>);
+ // Not invocable because NotAViewableRange is, well, not a viewable range.
+ static_assert(!std::is_invocable_v<decltype(std::views::reverse), NotAViewableRange>);
+ // Is invocable because BidirRange is a viewable range.
+ static_assert(std::is_invocable_v<decltype(std::views::reverse), BidirRange>);
+
+ // Make sure that pipe operations work!
+ static_assert(CanBePiped<BidirRange, decltype(std::views::stride(std::ranges::range_difference_t<BidirRange>{}))>);
+ static_assert(CanBePiped<BidirRange&, decltype(std::views::stride(std::ranges::range_difference_t<BidirRange>{}))>);
+ static_assert(
+ !CanBePiped<NotABidirRange, decltype(std::views::stride(std::ranges::range_difference_t<BidirRange>{}))>);
+ }
+ // A final sanity check.
+ { static_assert(std::same_as<decltype(std::views::stride), decltype(std::ranges::views::stride)>); }
+
+ 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 000000000000000..badfd532453158c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include "test.h"
+#include <cassert>
+#include <ranges>
+
+template <typename T>
+concept can_call_base_on = requires(T t) { std::forward<T>(t).base(); };
+
+constexpr bool test() {
+ int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ // Check the const& overload
+ {
+ Range range(buff, buff + 8);
+ std::ranges::stride_view<Range<int>> const view(range, 3);
+ std::same_as<Range<int>> decltype(auto) result = view.base();
+ assert(result.wasCopyInitialized);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 8);
+ }
+
+ // Check the && overload
+ {
+ Range range(buff, buff + 8);
+ std::ranges::stride_view<Range<int>> view(range, 3);
+ std::same_as<Range<int>> decltype(auto) result = std::move(view).base();
+ assert(result.wasMoveInitialized);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 8);
+ }
+
+ // Check the && overload (again)
+ {
+ Range range(buff, buff + 8);
+ std::same_as<Range<int>> decltype(auto) result = std::ranges::stride_view<Range<int>>(range, 3).base();
+ assert(result.wasMoveInitialized);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 8);
+ }
+
+ // Ensure the const& overload is not considered when the base is not copy-constructible
+ {
+ static_assert(!can_call_base_on<std::ranges::stride_view<NoCopyRange> const&>);
+ static_assert(!can_call_base_on<std::ranges::stride_view<NoCopyRange>&>);
+ static_assert(can_call_base_on<std::ranges::stride_view<NoCopyRange>&&>);
+ static_assert(can_call_base_on<std::ranges::stride_view<NoCopyRange>>);
+ }
+
+ 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 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
new file mode 100644
index 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp
new file mode 100644
index 000000000000000..646a9423f4523c7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// ranges
+
+// std::views::stride_view
+
+#include "test.h"
+#include <ranges>
+
+constexpr bool test() {
+ using std::ranges::enable_borrowed_range;
+ // Make sure that a stride_view over neither a borrowable nor an unborrowable view
+ // is itself borrowable.
+ static_assert(!enable_borrowed_range<std::ranges::stride_view<Range<int>>>);
+ static_assert(!enable_borrowed_range<std::ranges::stride_view<BorrowedRange<int>>>);
+ 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 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { 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 000000000000000..cfc38b32926805a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// ranges
+
+// std::views::stride_view
+
+#include "../test.h"
+#include <cassert>
+#include <ranges>
+
+bool non_simple_view_iter_ctor_test() {
+ using StrideView = std::ranges::stride_view<NotSimpleView>;
+ using StrideViewIterNonConst = std::ranges::iterator_t<StrideView>;
+ using StrideViewIterConst = std::ranges::iterator_t<const StrideView>;
+
+ StrideView sv{NotSimpleView{}, 1};
+ StrideViewIterNonConst iter = {sv, sv.base().begin(), 0};
+ StrideViewIterConst iterb = {iter};
+ assert(iterb.__end_.moved_from_a == true);
+ return true;
+}
+
+constexpr bool simpleview_iter_ctor_test() {
+ using StrideView = std::ranges::stride_view<ForwardTracedMoveView>;
+ using StrideViewIter = std::ranges::iterator_t<StrideView>;
+
+ StrideView sv{ForwardTracedMoveView{}, 1};
+ StrideViewIter iter = {sv, sv.base().begin(), 0};
+ // Guarantee that when the iterator is given to the constructor that
+ // it is moved there.
+ assert(iter.base().moved);
+
+ return true;
+}
+
+int main(int, char**) {
+ simpleview_iter_ctor_test();
+ non_simple_view_iter_ctor_test();
+ static_assert(simpleview_iter_ctor_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 000000000000000..7f2711adc5179db
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+template <class Iter>
+constexpr void testOne() {
+ using Range = std::ranges::subrange<Iter>;
+ using StrideView = std::ranges::stride_view<Range>;
+ static_assert(std::ranges::common_range<StrideView>);
+
+ {
+ // simple test
+ {
+ int buffer[] = {0, 1, 2, -1, 4, 5, 6};
+ Range input(Iter{buffer}, Iter{buffer + 7});
+ StrideView sv(input, 1);
+ StrideView sv_too(input, 2);
+ auto b = sv.begin(), e = sv.end();
+ auto b_too = sv_too.begin();
+
+ assert(b == b);
+ assert(!(b != b));
+
+ 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));
+
+ 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};
+ Range input(Iter{buffer}, Iter{buffer + 7});
+ std::ranges::stride_view sv(input, 1);
+ using StrideViewIter = decltype(sv.begin());
+ StrideViewIter i1, i2;
+ assert(i1 == i2);
+ assert(!(i1 != i2));
+ }
+ }
+}
+
+constexpr bool test() {
+ testOne<forward_iterator<int*>>();
+ //testOne<bidirectional_iterator<int*>>();
+ //testOne<random_access_iterator<int*>>();
+ //testOne<contiguous_iterator<int*>>();
+ testOne<int*>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/ctor.default.pass.cpp
new file mode 100644
index 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/ctor.default.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/equal.pass.cpp
new file mode 100644
index 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/equal.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
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 000000000000000..981395f9e2c3290
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// ranges
+
+// std::ranges::stride_view
+
+#include <cassert>
+#include <ranges>
+
+bool runtime_test() {
+ auto iot = std::views::iota(1, 22);
+ auto str = std::views::stride(iot, 3);
+ auto result = str.size();
+ assert(result == 7);
+ return true;
+}
+
+constexpr bool test() {
+ {
+ constexpr auto iot = std::views::iota(1, 12);
+ constexpr auto str = std::views::stride(iot, 3);
+ assert(4 == str.size());
+ static_assert(4 == str.size(), "Striding by 3 through a 12 member list has size 4.");
+ }
+ {
+ constexpr auto iot = std::views::iota(1, 22);
+ constexpr auto str = std::views::stride(iot, 3);
+ assert(7 == str.size());
+ static_assert(7 == str.size(), "Striding by 3 through a 12 member list has size 4.");
+ }
+ return true;
+}
+
+int main(int, char**) {
+ runtime_test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
new file mode 100644
index 000000000000000..4d13f05d48c01ba
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
@@ -0,0 +1,183 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "test_iterators.h"
+#include <iterator>
+#include <ranges>
+
+template <typename T = int>
+struct Range : std::ranges::view_base {
+ constexpr explicit Range(T* b, T* e) : begin_(b), end_(e) {}
+ constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {}
+ constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {}
+ Range& operator=(Range const&) = default;
+ Range& operator=(Range&&) = default;
+ constexpr T* begin() const { return begin_; }
+ constexpr T* end() const { return end_; }
+
+ T* begin_;
+ T* end_;
+ bool wasCopyInitialized = false;
+ bool wasMoveInitialized = false;
+};
+
+template <typename T>
+Range(T, T) -> Range<T>;
+
+template <typename T>
+struct BorrowedRange : public Range<T> {};
+
+template <typename T>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowedRange<T>> = true;
+
+struct NoCopyRange : std::ranges::view_base {
+ explicit NoCopyRange(int*, int*);
+ NoCopyRange(NoCopyRange const&) = delete;
+ NoCopyRange(NoCopyRange&&) = default;
+ NoCopyRange& operator=(NoCopyRange const&) = default;
+ NoCopyRange& operator=(NoCopyRange&&) = default;
+ int* begin() const;
+ int* end() const;
+};
+
+template <class Derived>
+struct ForwardIterBase {
+ using iterator_concept = std::forward_iterator_tag;
+ using value_type = int;
+ using difference_type = std::intptr_t;
+
+ constexpr int operator*() const { return 5; }
+
+ constexpr Derived& operator++() { return static_cast<Derived&>(*this); }
+ constexpr Derived operator++(int) { return {}; }
+
+ friend constexpr bool operator==(const ForwardIterBase&, const ForwardIterBase&) { return true; }
+};
+
+template <class Derived>
+struct InputIterBase {
+ using iterator_concept = std::input_iterator_tag;
+ using value_type = int;
+ using difference_type = std::intptr_t;
+
+ constexpr int operator*() const { return 5; }
+
+ constexpr Derived& operator++() { return static_cast<Derived&>(*this); }
+ constexpr Derived operator++(int) { return {}; }
+
+ friend constexpr bool operator==(const InputIterBase&, const InputIterBase&) { return true; }
+};
+
+struct NotSimpleViewIterB : ForwardIterBase<NotSimpleViewIterB> {
+ bool moved = false;
+
+ constexpr NotSimpleViewIterB() = default;
+ constexpr NotSimpleViewIterB(const NotSimpleViewIterB&) = default;
+ constexpr NotSimpleViewIterB(NotSimpleViewIterB&&) : moved{true} {}
+ constexpr NotSimpleViewIterB& operator=(NotSimpleViewIterB&&) = default;
+ constexpr NotSimpleViewIterB& operator=(const NotSimpleViewIterB&) = default;
+};
+
+struct NotSimpleViewIterA : ForwardIterBase<NotSimpleViewIterA> {
+ bool moved = false;
+ bool moved_from_a = false;
+ bool copied_from_a = false;
+
+ constexpr NotSimpleViewIterA() = default;
+ constexpr NotSimpleViewIterA(const NotSimpleViewIterA&) = default;
+ constexpr NotSimpleViewIterA(const NotSimpleViewIterB&) : copied_from_a{true} {}
+ constexpr NotSimpleViewIterA(NotSimpleViewIterA&&) : moved{true} {}
+ constexpr NotSimpleViewIterA(NotSimpleViewIterB&&) : moved_from_a{true} {}
+ constexpr NotSimpleViewIterA& operator=(NotSimpleViewIterA&&) = default;
+ constexpr NotSimpleViewIterA& operator=(const NotSimpleViewIterA&) = default;
+};
+
+struct NotSimpleView : std::ranges::view_base {
+ constexpr NotSimpleViewIterA begin() const { return {}; }
+ constexpr NotSimpleViewIterB begin() { return {}; }
+ constexpr NotSimpleViewIterA end() const { return {}; }
+ constexpr NotSimpleViewIterB end() { return {}; }
+
+ int* begin_;
+ int* end_;
+ bool wasCopyInitialized = false;
+ bool wasMoveInitialized = false;
+};
+
+struct ForwardTracedMoveIter : ForwardIterBase<ForwardTracedMoveIter> {
+ bool moved = false;
+
+ constexpr ForwardTracedMoveIter() = default;
+ constexpr ForwardTracedMoveIter(const ForwardTracedMoveIter&) = default;
+ constexpr ForwardTracedMoveIter(ForwardTracedMoveIter&&) : moved{true} {}
+ constexpr ForwardTracedMoveIter& operator=(ForwardTracedMoveIter&&) = default;
+ constexpr ForwardTracedMoveIter& operator=(const ForwardTracedMoveIter&) = default;
+};
+
+struct ForwardTracedMoveView : std::ranges::view_base {
+ constexpr ForwardTracedMoveIter begin() const { return {}; }
+ constexpr ForwardTracedMoveIter end() const { return {}; }
+};
+
+struct BidirRange : std::ranges::view_base {
+ int* begin_;
+ int* end_;
+
+ constexpr BidirRange(int* b, int* e) : begin_(b), end_(e) {}
+
+ constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{begin_}; }
+ constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{begin_}; }
+ constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{end_}; }
+ constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{end_}; }
+};
+static_assert(std::ranges::bidirectional_range<BidirRange>);
+static_assert(std::ranges::common_range<BidirRange>);
+static_assert(std::ranges::view<BidirRange>);
+static_assert(std::copyable<BidirRange>);
+
+enum CopyCategory { MoveOnly, Copyable };
+template <CopyCategory CC>
+struct BidirSentRange : std::ranges::view_base {
+ using sent_t = sentinel_wrapper<bidirectional_iterator<int*>>;
+ using sent_const_t = sentinel_wrapper<bidirectional_iterator<const int*>>;
+
+ int* begin_;
+ int* end_;
+
+ constexpr BidirSentRange(int* b, int* e) : begin_(b), end_(e) {}
+ constexpr BidirSentRange(const BidirSentRange&)
+ requires(CC == Copyable)
+ = default;
+ constexpr BidirSentRange(BidirSentRange&&)
+ requires(CC == MoveOnly)
+ = default;
+ constexpr BidirSentRange& operator=(const BidirSentRange&)
+ requires(CC == Copyable)
+ = default;
+ constexpr BidirSentRange& operator=(BidirSentRange&&)
+ requires(CC == MoveOnly)
+ = default;
+
+ constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{begin_}; }
+ constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{begin_}; }
+ constexpr sent_t end() { return sent_t{bidirectional_iterator<int*>{end_}}; }
+ constexpr sent_const_t end() const { return sent_const_t{bidirectional_iterator<const int*>{end_}}; }
+};
+static_assert(std::ranges::bidirectional_range<BidirSentRange<MoveOnly>>);
+static_assert(!std::ranges::common_range<BidirSentRange<MoveOnly>>);
+static_assert(std::ranges::view<BidirSentRange<MoveOnly>>);
+static_assert(!std::copyable<BidirSentRange<MoveOnly>>);
+static_assert(std::ranges::bidirectional_range<BidirSentRange<Copyable>>);
+static_assert(!std::ranges::common_range<BidirSentRange<Copyable>>);
+static_assert(std::ranges::view<BidirSentRange<Copyable>>);
+static_assert(std::copyable<BidirSentRange<Copyable>>);
+
+#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 c2251c8d817c1ce..41fc2cf818ee14e 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -832,6 +832,11 @@ def add_version_header(tc):
"headers": ["algorithm"],
"unimplemented": True,
},
+ {
+ "name": "__cpp_lib_ranges_stride",
+ "values": {"c++23": 202207},
+ "headers": ["ranges"],
+ },
{
"name": "__cpp_lib_ranges_to_container",
"values": {"c++23": 202202},
>From 83c5e114447b09741968f9f0f8b484871250787e Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Mon, 4 Sep 2023 10:20:04 -0400
Subject: [PATCH 2/2] fixup! WIP: [libc++][ranges] Implement
`ranges::stride_view`.
Add CTAD testing and refactor test classes.
---
.../range.stride.view/ctad.compile.pass.cpp | 33 ++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
index 68556f32f875b12..7831d792d64e770 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
@@ -12,7 +12,37 @@
// std::views::stride_view
-constexpr bool test() { return true; }
+#include <concepts>
+#include <ranges>
+#include <utility>
+#include "test.h"
+
+constexpr bool test() {
+ int arr[]{1, 2, 3};
+
+ InstrumentedBasicView<int> bv{arr, arr + 3};
+ InstrumentedBasicRange<int> br{};
+
+ static_assert(std::same_as<
+ decltype(std::ranges::stride_view(bv, 2)),
+ std::ranges::stride_view<decltype(bv)>
+ >);
+ static_assert(std::same_as<
+ decltype(std::ranges::stride_view(std::move(bv), 2)),
+ std::ranges::stride_view<decltype(bv)>
+ >);
+
+ static_assert(std::same_as<
+ decltype(std::ranges::drop_view(br, 0)),
+ std::ranges::drop_view<std::ranges::ref_view<InstrumentedBasicRange<int>>>
+ >);
+
+ static_assert(std::same_as<
+ decltype(std::ranges::drop_view(std::move(br), 0)),
+ std::ranges::drop_view<std::ranges::owning_view<InstrumentedBasicRange<int>>>
+ >);
+ return true;
+}
int main(int, char**) {
test();
@@ -20,3 +50,4 @@ int main(int, char**) {
return 0;
}
+
More information about the libcxx-commits
mailing list