[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 <ldionne.2@gmail.com>
Co-authored-by: A. Jiang <de34@live.cn>
This commit is contained in:
Will Hawkins
2026-04-25 06:51:37 -04:00
committed by GitHub
parent 3e10b2fe21
commit 2f28e1db53
45 changed files with 3929 additions and 2 deletions

View File

@@ -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``

View File

@@ -39,6 +39,7 @@ Implemented Papers
------------------
- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
- P1899R3: ``stride_view`` (`Github <https://llvm.org/PR105198>`__)
- P3936R1: Safer ``atomic_ref::address`` (`Github <https://llvm.org/PR189594>`__)
- P3953R3: Rename ``std::runtime_format`` (`Github <https://llvm.org/PR189624>`__)
- P4052R0: Renaming saturation arithmetic functions (`Github <https://llvm.org/PR189589>`__)

View File

@@ -58,7 +58,7 @@
"`P1223R5 <https://wg21.link/P1223R5>`__","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","2022-07 (Virtual)","|Complete|","19","`#105194 <https://github.com/llvm/llvm-project/issues/105194>`__",""
"`P1467R9 <https://wg21.link/P1467R9>`__","Extended ``floating-point`` types and standard names","2022-07 (Virtual)","","","`#105196 <https://github.com/llvm/llvm-project/issues/105196>`__",""
"`P1642R11 <https://wg21.link/P1642R11>`__","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","2022-07 (Virtual)","","","`#105197 <https://github.com/llvm/llvm-project/issues/105197>`__",""
"`P1899R3 <https://wg21.link/P1899R3>`__","``stride_view``","2022-07 (Virtual)","","","`#105198 <https://github.com/llvm/llvm-project/issues/105198>`__",""
"`P1899R3 <https://wg21.link/P1899R3>`__","``stride_view``","2022-07 (Virtual)","|Complete|","23","`#105198 <https://github.com/llvm/llvm-project/issues/105198>`__",""
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","2022-07 (Virtual)","|Complete|","18","`#105199 <https://github.com/llvm/llvm-project/issues/105199>`__",""
"`P2165R4 <https://wg21.link/P2165R4>`__","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","2022-07 (Virtual)","|Partial|","","`#105200 <https://github.com/llvm/llvm-project/issues/105200>`__","Only the part for ``zip_view`` is implemented."
"`P2278R4 <https://wg21.link/P2278R4>`__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","","","`#105201 <https://github.com/llvm/llvm-project/issues/105201>`__",""
1 Paper # Paper Name Meeting Status First released version GitHub issue Notes
58 `P1223R5 <https://wg21.link/P1223R5>`__ ``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()`` 2022-07 (Virtual) |Complete| 19 `#105194 <https://github.com/llvm/llvm-project/issues/105194>`__
59 `P1467R9 <https://wg21.link/P1467R9>`__ Extended ``floating-point`` types and standard names 2022-07 (Virtual) `#105196 <https://github.com/llvm/llvm-project/issues/105196>`__
60 `P1642R11 <https://wg21.link/P1642R11>`__ Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]`` 2022-07 (Virtual) `#105197 <https://github.com/llvm/llvm-project/issues/105197>`__
61 `P1899R3 <https://wg21.link/P1899R3>`__ ``stride_view`` 2022-07 (Virtual) |Complete| 23 `#105198 <https://github.com/llvm/llvm-project/issues/105198>`__
62 `P2093R14 <https://wg21.link/P2093R14>`__ Formatted output 2022-07 (Virtual) |Complete| 18 `#105199 <https://github.com/llvm/llvm-project/issues/105199>`__
63 `P2165R4 <https://wg21.link/P2165R4>`__ Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects 2022-07 (Virtual) |Partial| `#105200 <https://github.com/llvm/llvm-project/issues/105200>`__ Only the part for ``zip_view`` is implemented.
64 `P2278R4 <https://wg21.link/P2278R4>`__ ``cbegin`` should always return a constant iterator 2022-07 (Virtual) `#105201 <https://github.com/llvm/llvm-project/issues/105201>`__

View File

@@ -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

View File

@@ -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 <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;
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</*_Const=*/false>(this, ranges::begin(__base_), 0);
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
requires range<const _View>
{
return __iterator</*_Const=*/true>(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</*_Const=*/false>(this, ranges::end(__base_), __missing);
} else if constexpr (common_range<_View> && !bidirectional_range<_View>) {
return __iterator</*_Const=*/false>(this, ranges::end(__base_), 0);
} else {
return default_sentinel;
}
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
requires(range<const _View>)
{
if constexpr (common_range<const _View> && sized_range<const _View> && forward_range<const _View>) {
auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
return __iterator</*_Const=*/true>(this, ranges::end(__base_), __missing);
} else if constexpr (common_range<const _View> && !bidirectional_range<const _View>) {
return __iterator</*_Const=*/true>(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<const _View>
{
return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
}
}; // class stride_view
template <class _Range>
stride_view(_Range&&, range_difference_t<_Range>) -> stride_view<views::all_t<_Range>>;
template <class _View>
struct __stride_iterator_category {};
template <forward_range _View>
struct __stride_iterator_category<_View> {
using _Cat _LIBCPP_NODEBUG = typename iterator_traits<iterator_t<_View>>::iterator_category;
using iterator_category =
_If<derived_from<_Cat, random_access_iterator_tag>,
/* then */ 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<__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<iterator_t<_Base>>
= default;
_LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
requires _Const && convertible_to<ranges::iterator_t<_View>, iterator_t<_Base>> &&
convertible_to<sentinel_t<_View>, 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<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 auto operator<=>(__iterator const& __x, __iterator const& __y)
requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
{
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>, 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<sentinel_t<_Base>, 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<sentinel_t<_Base>, 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<iterator_t<_Base>>
{
return ranges::iter_swap(__x.__current_, __y.__current_);
}
}; // class stride_view::__iterator
template <class _Tp>
inline constexpr bool enable_borrowed_range<stride_view<_Tp>> = enable_borrowed_range<_Tp>;
namespace views {
namespace __stride_view {
struct __fn {
// clang-format off
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); }
// clang-format on
template <class _Np>
[[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

View File

@@ -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

View File

@@ -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<input_range V>
requires view<V>
class stride_view; // C++23
template<class V>
constexpr bool enable_borrowed_range<stride_view<V>> =
enable_borrowed_range<V>; // 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>

View File

@@ -218,6 +218,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 <ranges>
__cpp_lib_ranges_zip 202110L <ranges> <tuple> <utility>
__cpp_lib_ratio 202306L <ratio>
@@ -534,6 +535,7 @@ __cpp_lib_void_t 201411L <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

View File

@@ -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 {

View File

@@ -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 <ranges>
#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;
}

View File

@@ -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 <ranges>
#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;
}

View File

@@ -0,0 +1,64 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, 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 <ranges>
#include "check_assertion.h"
#include "test_iterators.h"
template <class Iter, class Sent = sentinel_wrapper<Iter>>
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<cpp17_input_iterator<int*>>;
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<int*>, forward_iterator<int*>>;
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<int*>, forward_iterator<int*>>;
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;
}

View File

@@ -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 <ranges>
#include <utility>
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);
}

View File

@@ -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 <ranges>
#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;
}

View File

@@ -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 <ranges>
#include <utility>
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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 <cassert>
#include <concepts>
#include <ranges>
#include <type_traits>
#include "test_iterators.h"
#include "types.h"
constexpr BasicTestView<cpp17_input_iterator<int*>> make_input_view(int* begin, int* end) {
return BasicTestView<cpp17_input_iterator<int*>>(cpp17_input_iterator<int*>(begin), cpp17_input_iterator<int*>(end));
}
using ForwardStrideView = std::ranges::stride_view<BasicTestView<forward_iterator<int*>>>;
using BidirStrideView = std::ranges::stride_view<BasicTestView<bidirectional_iterator<int*>>>;
using RandomAccessStrideView = std::ranges::stride_view<BasicTestView<random_access_iterator<int*>>>;
using SizedForwardStrideView =
std::ranges::stride_view<BasicTestView<random_access_iterator<int*>, random_access_iterator<int*>>>;
using SizedInputStrideView = std::ranges::stride_view<BasicTestView<SizedInputIter, SizedInputIter>>;
static_assert(std::ranges::forward_range<ForwardStrideView>);
static_assert(std::ranges::bidirectional_range<BidirStrideView>);
static_assert(std::ranges::random_access_range<RandomAccessStrideView>);
static_assert(std::ranges::forward_range<SizedForwardStrideView>);
static_assert(std::sized_sentinel_for<std::ranges::iterator_t<SizedForwardStrideView>,
std::ranges::iterator_t<SizedForwardStrideView>>);
static_assert(std::sized_sentinel_for<std::ranges::iterator_t<SizedInputStrideView>,
std::ranges::iterator_t<SizedInputStrideView>>);
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<BasicTestView<cpp17_input_iterator<int*>>>;
auto view = make_input_view(arr, arr + N);
std::same_as<View> 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<BasicTestView<cpp17_input_iterator<int*>>, decltype(twice)>>;
auto view = make_input_view(arr, arr + N);
const auto partial = std::views::transform(twice) | std::views::stride(2);
std::same_as<View> 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<BasicTestView<cpp17_input_iterator<int*>>>,
decltype(twice)>;
auto view = make_input_view(arr, arr + N);
std::same_as<View> 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<bidirectional_iterator<int*>>;
static_assert(!std::is_invocable_v<decltype(std::views::stride)>);
static_assert(!std::is_invocable_v<decltype(std::views::stride), NotAViewableRange, int>);
static_assert(CanBePiped<View, decltype(std::views::stride(5))>);
static_assert(CanBePiped<View&, decltype(std::views::stride(5))>);
static_assert(!CanBePiped<NotAViewableRange, decltype(std::views::stride(5))>);
static_assert(!CanBePiped<View&, decltype(std::views::stride(NotAViewableRange{}))>);
}
// 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;
}

View File

@@ -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<V>;
// constexpr V base() &&;
#include <cassert>
#include <concepts>
#include <ranges>
#include <utility>
#include "test_iterators.h"
#include "types.h"
template <typename T>
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<cpp17_input_iterator<int*>>;
using MoveOnlyInputView = MoveOnlyView<cpp17_input_iterator<int*>>;
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<CopyableInputView> view(
CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1);
static_assert(has_lvalue_qualified_base(view));
std::same_as<CopyableInputView> decltype(auto) s = view.base();
assert(*s.begin() == *buff);
}
// &
{
std::ranges::stride_view<CopyableInputView> view(
CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1);
std::same_as<CopyableInputView> decltype(auto) s = view.base();
assert(*s.begin() == *buff);
static_assert(has_lvalue_qualified_base(view));
}
// r-value ref qualified
// &&
{
std::ranges::stride_view<CopyableInputView> view(
CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1);
static_assert(has_lvalue_qualified_base(view));
std::same_as<CopyableInputView> decltype(auto) s = std::move(view).base();
assert(*s.begin() == *buff);
}
// const &&
{
const std::ranges::stride_view<CopyableInputView> view(
CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1);
static_assert(has_lvalue_qualified_base(view));
std::same_as<CopyableInputView> decltype(auto) s = std::move(view).base();
assert(*s.begin() == *buff);
}
// &&
{
std::ranges::stride_view<MoveOnlyInputView> view(
MoveOnlyInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(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<MoveOnlyInputView> decltype(auto) s = std::move(view).base();
assert(*s.begin() == *buff);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@@ -0,0 +1,115 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// constexpr auto begin() requires(!simple-view<V>)
// constexpr auto begin() const requires range<const V>
// Note: Checks here are augmented by checks in
// iterator/ctor.copy.pass.cpp.
#include <cassert>
#include <concepts>
#include <ranges>
#include "types.h"
template <class T>
concept HasConstBegin = requires(const T& ct) { ct.begin(); };
template <class T>
concept HasBegin = requires(T& t) { t.begin(); };
template <class T>
concept HasConstAndNonConstBegin = 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<decltype(t.begin()), decltype(ct.begin())>;
};
template <class T>
// There is a begin but it's not const qualified => Only non-const qualified begin.
concept HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>;
template <class T>
// There is a const-qualified begin and there are not both const- and non-const qualified begin => Only const-qualified begin.
concept HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>;
static_assert(HasOnlyNonConstBegin<std::ranges::stride_view<UnSimpleNoConstCommonView>>);
static_assert(HasOnlyConstBegin<std::ranges::stride_view<SimpleCommonConstView>>);
static_assert(HasConstAndNonConstBegin<std::ranges::stride_view<UnSimpleConstView>>);
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<decltype(*sv.begin()), double&>);
}
{
// Return type check for simple const view.
auto v = SimpleCommonConstView();
auto sv = std::ranges::stride_view(v, 1);
static_assert(std::same_as<decltype(*sv.begin()), int&>);
}
{
// Verify begin() produces the first element with stride 1.
int data[] = {10, 20, 30, 40, 50};
auto v = BasicTestView<int*, int*>{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<int*, int*>{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<int*, int*>{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<int*>, forward_iterator<int*>>;
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<int*, int*>{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;
}

View File

@@ -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<class V>
// inline constexpr bool enable_borrowed_range<stride_view<V>> = ranges::enable_borrowed_range<V>;
#include <ranges>
#include "test_range.h"
static_assert(std::ranges::enable_borrowed_range< std::ranges::stride_view<BorrowedView>>);
static_assert(!std::ranges::enable_borrowed_range< std::ranges::stride_view<NonBorrowedView>>);

View File

@@ -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 <input_range V> requires view<V>
#include <ranges>
#include <type_traits>
#include "almost_satisfies_types.h"
#include "test_iterators.h"
#include "test_range.h"
template <typename I, std::ranges::range_difference_t<I> D>
concept CanStrideView = requires { std::ranges::stride_view<I>{I{}, D}; };
// Ensure that the InputRangeNotIndirectlyReadable is a valid range.
static_assert(std::ranges::range<InputRangeNotIndirectlyReadable>);
// Ensure that the InputRangeNotIndirectlyReadable's iterator is not an input range ...
static_assert(!std::ranges::input_range<std::ranges::iterator_t<InputRangeNotIndirectlyReadable>>);
// Because CanStrideView requires that the range/view type be default constructible, let's double check that ...
static_assert(std::is_default_constructible_v<InputRangeNotIndirectlyReadable>);
// And now, finally, let's make sure that we cannot stride over a range whose iterator is not an input iterator ...
static_assert(!CanStrideView<InputRangeNotIndirectlyReadable, 1>);
// Ensure that a range that is not a view cannot be the subject of a stride_view.
static_assert(std::ranges::range<test_non_const_range<cpp17_input_iterator>>);
static_assert(std::ranges::input_range<test_non_const_range<cpp17_input_iterator>>);
static_assert(std::movable<test_non_const_range<cpp17_input_iterator>>);
static_assert(!std::ranges::view<test_non_const_range<cpp17_input_iterator>>);
static_assert(!CanStrideView<test_non_const_range<cpp17_input_iterator>, 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<test_view<cpp17_input_iterator>>);
static_assert(std::ranges::input_range<test_view<cpp17_input_iterator>>);
static_assert(std::ranges::view<test_view<cpp17_input_iterator>>);
static_assert(CanStrideView<test_view<cpp17_input_iterator>, 1>);
// stride_view itself models view.
static_assert(std::ranges::view<std::ranges::stride_view<test_view<cpp17_input_iterator>>>);
static_assert(std::ranges::view<std::ranges::stride_view<test_view<forward_iterator>>>);

View File

@@ -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 <class R>
// stride_view(R&&, range_difference_t<R>) -> stride_view<views::all_t<R>>;
#include <cassert>
#include <concepts>
#include <ranges>
#include <utility>
#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<cpp17_input_iterator<int*>>;
using BaseView = BasicTestView<int*>;
auto base_view = BaseView(a, a + 5);
auto base_view_move = BaseView(a, a + 5);
auto base_range = BaseRange(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 5));
auto base_range_move = BaseRange(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(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<decltype(sv_view), std::ranges::stride_view<BaseView>>);
static_assert(std::same_as<decltype(sv_view_move), std::ranges::stride_view<BaseView>>);
// Verify deduced types for ranges: lvalue -> ref_view, rvalue -> owning_view.
static_assert(std::same_as<decltype(sv_range), std::ranges::stride_view<std::ranges::ref_view<BaseRange>> >);
static_assert(std::same_as<decltype(sv_range_move), std::ranges::stride_view<std::ranges::owning_view<BaseRange>> >);
// 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;
}

View File

@@ -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<V> stride)
#include <cassert>
#include <ranges>
#include <type_traits>
#include <utility>
#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<cpp17_input_iterator<int*>>;
static_assert(!std::is_default_constructible_v<std::ranges::stride_view<View>>);
// Test that the stride_view can only be explicitly constructed.
static_assert(!test_convertible<std::ranges::stride_view<View>, View, int>());
}
{
int arr[] = {1, 2, 3};
// Test that what we will stride over is move only.
using View = MoveOnlyView<cpp17_input_iterator<int*>>;
static_assert(!std::is_copy_constructible_v<View>);
static_assert(std::is_move_constructible_v<View>);
View mov(cpp17_input_iterator<int*>(arr), cpp17_input_iterator<int*>(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<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<int*, int*>;
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;
}

View File

@@ -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<V>)
// constexpr auto end() const requires(range<const V>)
// Note: Checks here are augmented by checks in
// iterator/ctor.copy.pass.cpp.
#include <ranges>
#include <cassert>
#include <type_traits>
#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<int*> begin() { return forward_iterator<int*>(data_); }
constexpr sentinel_wrapper<forward_iterator<int*>> end() {
return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(data_ + size_));
}
// Const: a common forward range (begin and end return the same type).
constexpr forward_iterator<const int*> begin() const { return forward_iterator<const int*>(data_); }
constexpr forward_iterator<const int*> end() const { return forward_iterator<const int*>(data_ + size_); }
};
static_assert(std::ranges::range<CommonForwardOnlyWhenConst>);
static_assert(std::ranges::range<const CommonForwardOnlyWhenConst>);
static_assert(std::ranges::forward_range<const CommonForwardOnlyWhenConst>);
static_assert(!std::ranges::bidirectional_range<const CommonForwardOnlyWhenConst>);
static_assert(std::ranges::common_range<const CommonForwardOnlyWhenConst>);
static_assert(!std::ranges::common_range<CommonForwardOnlyWhenConst>);
static_assert(std::ranges::view<CommonForwardOnlyWhenConst>);
template <class T>
concept HasConstEnd = requires(const T& ct) { ct.end(); };
template <class T>
concept HasEnd = requires(T& t) { t.end(); };
template <class T>
concept HasConstAndNonConstEnd =
HasConstEnd<T> && requires(T& t, const T& ct) { requires !std::same_as<decltype(t.end()), decltype(ct.end())>; };
template <class T>
concept HasOnlyNonConstEnd = HasEnd<T> && !HasConstEnd<T>;
template <class T>
concept HasOnlyConstEnd = HasConstEnd<T> && !HasConstAndNonConstEnd<T>;
static_assert(HasOnlyNonConstEnd<std::ranges::stride_view<UnSimpleNoConstCommonView>>);
static_assert(HasOnlyConstEnd<std::ranges::stride_view<SimpleCommonConstView>>);
static_assert(HasConstAndNonConstEnd<std::ranges::stride_view<UnSimpleConstView>>);
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<const int*, const int*>{data, data + 3};
auto sv = std::ranges::stride_view(v, 1);
static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
// 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<int*>, forward_iterator<int*>>;
auto v = ForwardTestView{forward_iterator(data), forward_iterator(data + 3)};
auto sv = std::ranges::stride_view(v, 1);
static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
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<UnSimpleNoConstCommonView>);
static_assert(std::ranges::common_range<UnSimpleNoConstCommonView>);
static_assert(std::ranges::sized_range<UnSimpleNoConstCommonView>);
static_assert(std::ranges::forward_range<UnSimpleNoConstCommonView>);
auto sv = std::ranges::stride_view<UnSimpleNoConstCommonView>(UnSimpleNoConstCommonView{}, 1);
static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
}
{
// Uncommon range -> returns default_sentinel.
static_assert(!simple_view<UnsimpleUnCommonConstView>);
static_assert(!std::ranges::common_range<UnsimpleUnCommonConstView>);
auto sv = std::ranges::stride_view<UnsimpleUnCommonConstView>(UnsimpleUnCommonConstView{}, 1);
ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(sv.end()));
}
{
// Simple, uncommon range -> returns default_sentinel.
static_assert(simple_view<SimpleUnCommonConstView>);
static_assert(!std::ranges::common_range<SimpleUnCommonConstView>);
auto sv = std::ranges::stride_view<SimpleUnCommonConstView>(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<int*, int*>{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<int*, int*>{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<const _View>, 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<std::default_sentinel_t, decltype(csv.end())>);
// 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<int*> 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<int*>, forward_iterator<int*>>;
static_assert(std::ranges::common_range<FwdView>);
static_assert(std::ranges::forward_range<FwdView>);
static_assert(!std::ranges::bidirectional_range<FwdView>);
static_assert(!std::ranges::sized_range<FwdView>);
auto v = FwdView{forward_iterator(data), forward_iterator(data + 5)};
auto sv = std::ranges::stride_view(v, 2);
static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
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<int*, int*>;
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;
}

View File

@@ -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>& base() const& noexcept
// constexpr iterator_t<Base> base() &&
#include <cassert>
#include <ranges>
#include <type_traits>
#include <utility>
#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<std::remove_reference_t<decltype(stride_iter.base())>>);
static_assert(!std::is_const_v<decltype(std::move(stride_iter).base())>);
}
{
// base() && moves the underlying iterator.
int move_counter = 0;
int copy_counter = 0;
auto start = SizedInputIter();
start.move_counter = &move_counter;
start.copy_counter = &copy_counter;
auto stop = SizedInputIter();
auto view = BasicTestView<SizedInputIter>{start, stop};
assert(move_counter == 0);
assert(copy_counter == 1);
auto sv = std::ranges::stride_view<BasicTestView<SizedInputIter>>(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 = &copy_counter;
auto stop = SizedInputIter();
auto view = BasicTestView<SizedInputIter>{start, stop};
assert(move_counter == 0);
assert(copy_counter == 1);
auto sv = std::ranges::stride_view<BasicTestView<SizedInputIter>>(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;
}

View File

@@ -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 <cassert>
#include <compare>
#include <functional>
#include <ranges>
#include <type_traits>
#include "../types.h"
#include "test_iterators.h"
// Input view: no relational comparisons.
using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
using StrideInputIter = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
static_assert(!std::is_invocable_v<std::less<>, StrideInputIter, StrideInputIter>);
static_assert(!std::is_invocable_v<std::greater<>, StrideInputIter, StrideInputIter>);
static_assert(!std::is_invocable_v<std::less_equal<>, StrideInputIter, StrideInputIter>);
static_assert(!std::is_invocable_v<std::greater_equal<>, StrideInputIter, StrideInputIter>);
// Forward view: no relational comparisons.
using FwdView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
using StrideFwdIter = std::ranges::iterator_t<std::ranges::stride_view<FwdView>>;
static_assert(!std::is_invocable_v<std::less<>, StrideFwdIter, StrideFwdIter>);
static_assert(!std::is_invocable_v<std::greater<>, StrideFwdIter, StrideFwdIter>);
static_assert(!std::is_invocable_v<std::less_equal<>, StrideFwdIter, StrideFwdIter>);
static_assert(!std::is_invocable_v<std::greater_equal<>, StrideFwdIter, StrideFwdIter>);
// Bidirectional view: no relational comparisons.
using BidirView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
using StrideBidirIter = std::ranges::iterator_t<std::ranges::stride_view<BidirView>>;
static_assert(!std::is_invocable_v<std::less<>, StrideBidirIter, StrideBidirIter>);
static_assert(!std::is_invocable_v<std::greater<>, StrideBidirIter, StrideBidirIter>);
static_assert(!std::is_invocable_v<std::less_equal<>, StrideBidirIter, StrideBidirIter>);
static_assert(!std::is_invocable_v<std::greater_equal<>, StrideBidirIter, StrideBidirIter>);
// Random access view: all relational comparisons available.
using RAView = BasicTestView<random_access_iterator<int*>>;
using StrideRAIter = std::ranges::iterator_t<std::ranges::stride_view<RAView>>;
static_assert(std::is_invocable_v<std::less<>, StrideRAIter, StrideRAIter>);
static_assert(std::is_invocable_v<std::greater<>, StrideRAIter, StrideRAIter>);
static_assert(std::is_invocable_v<std::less_equal<>, StrideRAIter, StrideRAIter>);
static_assert(std::is_invocable_v<std::greater_equal<>, StrideRAIter, StrideRAIter>);
// three_way_comparable when the base iterator is three_way_comparable.
using ThreeWayView = BasicTestView<three_way_contiguous_iterator<int*>>;
using StrideThreeWayIter = std::ranges::iterator_t<std::ranges::stride_view<ThreeWayView>>;
static_assert(std::three_way_comparable<StrideThreeWayIter>);
// Not three_way_comparable when the base is not.
using EqualOnlyView = BasicTestView<cpp17_input_iterator<int*>>;
using StrideEqualOnlyIter = std::ranges::iterator_t<std::ranges::stride_view<EqualOnlyView>>;
static_assert(!std::three_way_comparable<StrideEqualOnlyIter>);
constexpr bool test() {
{
// <, >, <=, >= on random access range.
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
using Base = BasicTestView<int*, int*>;
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<int*>, three_way_contiguous_iterator<int*>>;
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;
}

View File

@@ -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<!Const> i)
// requires Const && convertible_to<iterator_t<V>, iterator_t<Base>> &&
// convertible_to<sentinel_t<V>, sentinel_t<Base>>
#include <cassert>
#include <concepts>
#include <ranges>
#include <utility>
#include "../types.h"
// A non-simple view over actual data. begin()/end() return int* when non-const
// and const int* when const, so iterator<false> and iterator<true> 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<NonSimpleDataView>);
// Two unrelated input iterator types (no conversion between them).
struct IterA : InputIter<IterA> {};
struct IterB : InputIter<IterB> {
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<NonSimpleDataView>;
using ConvertibleIter = std::ranges::iterator_t<ConvertibleSV>;
using ConvertibleCIter = std::ranges::iterator_t<const ConvertibleSV>;
static_assert(!std::same_as<ConvertibleIter, ConvertibleCIter>);
static_assert(std::convertible_to<ConvertibleIter, ConvertibleCIter>);
static_assert(std::constructible_from<ConvertibleCIter, ConvertibleIter>);
// Conversion fails: underlying iterator types are not convertible (IterA -> IterB).
using IterNCSV = std::ranges::stride_view<IterNonConvertibleView>;
using IterNCIter = std::ranges::iterator_t<IterNCSV>;
using IterNCCIter = std::ranges::iterator_t<const IterNCSV>;
static_assert(!std::same_as<IterNCIter, IterNCCIter>);
static_assert(!std::convertible_to<IterNCIter, IterNCCIter>);
static_assert(!std::constructible_from<IterNCCIter, IterNCIter>);
// Conversion fails: underlying sentinel types are not convertible (SentA -> SentB).
using SentNCSV = std::ranges::stride_view<SentNonConvertibleView>;
using SentNCIter = std::ranges::iterator_t<SentNCSV>;
using SentNCCIter = std::ranges::iterator_t<const SentNCSV>;
static_assert(!std::same_as<SentNCIter, SentNCCIter>);
static_assert(!std::convertible_to<SentNCIter, SentNCCIter>);
static_assert(!std::constructible_from<SentNCCIter, SentNCIter>);
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;
}

View File

@@ -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<iterator_t<Base>> = default;
#include <ranges>
#include <type_traits>
#include "../types.h"
struct NonDefaultConstructibleIterator : InputIter<NonDefaultConstructibleIterator> {
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<ViewWithNonDefaultConstructibleIterator> = 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<ViewWithNonDefaultConstructibleIterator>>());
static_assert(std::is_default_constructible<
std::ranges::iterator_t< std::ranges::stride_view<std::ranges::ref_view<const int[3]>>>>());
constexpr bool test() {
{
// Default construct an iterator over a default-constructible base.
using SV = std::ranges::stride_view<std::ranges::ref_view<const int[3]>>;
using It = std::ranges::iterator_t<SV>;
[[maybe_unused]] It it{};
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@@ -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 <cassert>
#include <ranges>
#include <type_traits>
#include <vector>
#include "../types.h"
template <class T>
concept CanPostDecrement = std::is_same_v<T, decltype(std::declval<T>()--)> && requires(T& t) { t--; };
template <class T>
concept CanPreDecrement = std::is_same_v<T, decltype(--(std::declval<T>()))> && requires(T& t) { --t; };
// What operators are valid for an iterator derived from a stride view
// over an input view.
using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
static_assert(!std::ranges::bidirectional_range<InputView>);
static_assert(!CanPostDecrement<StrideViewOverInputViewIterator>);
static_assert(!CanPreDecrement<StrideViewOverInputViewIterator>);
// What operators are valid for an iterator derived from a stride view
// over a forward view.
using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
static_assert(!std::ranges::bidirectional_range<ForwardView>);
static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
// What operators are valid for an iterator derived from a stride view
// over a bidirectional view.
using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
static_assert(std::ranges::bidirectional_range<BidirectionalView>);
static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
// What operators are valid for an iterator derived from a stride view
// over a random access view.
using RandomAccessView = BasicTestView<random_access_iterator<int*>>;
using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView>>;
static_assert(std::ranges::bidirectional_range<RandomAccessView>);
static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
template <typename Iter, typename Difference>
requires(std::bidirectional_iterator<Iter>)
constexpr bool test_operator_decrement(Iter begin, Iter end, Difference delta) {
using Base = BasicTestView<Iter, Iter>;
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<int*, int*>;
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<int*, int*>;
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<int*, int*>;
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<int*, int*>;
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;
}

View File

@@ -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 <cassert>
#include <ranges>
#include "../types.h"
#include "test_iterators.h"
constexpr bool test() {
{
// Dereference with stride 1.
int arr[] = {10, 20, 30};
using Base = BasicTestView<int*, int*>;
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<int*, int*>;
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<int*, int*>;
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<int*, int*>;
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<int*>, forward_iterator<int*>>;
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<int*, int*>;
auto sv = std::ranges::stride_view(Base(arr, arr + 3), 1);
auto it = sv.begin();
static_assert(std::is_same_v<decltype(*it), int&>);
}
{
// Dereference through a const-qualified iterator.
int arr[] = {10, 20, 30};
using Base = BasicTestView<int*, int*>;
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;
}

View File

@@ -0,0 +1,110 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// friend constexpr bool operator==(const iterator& x, default_sentinel_t)
// friend constexpr bool operator==(const iterator& x, const iterator& y)
#include <cassert>
#include <concepts>
#include <ranges>
#include "../types.h"
#include "test_iterators.h"
// A stride_view iterator over an equality_comparable base should itself be equality_comparable.
using EqualableView = BasicTestView<cpp17_input_iterator<int*>>;
using EqualableViewIter = std::ranges::iterator_t<std::ranges::stride_view<EqualableView>>;
static_assert(std::equality_comparable<std::ranges::iterator_t<EqualableView>>);
static_assert(std::equality_comparable<EqualableViewIter>);
// A stride_view iterator over a non-equality_comparable base should NOT be equality_comparable.
using UnEqualableView = ViewOverNonCopyableIterator<cpp20_input_iterator<int*>>;
using UnEqualableViewIter = std::ranges::iterator_t<std::ranges::stride_view<UnEqualableView>>;
static_assert(!std::equality_comparable<std::ranges::iterator_t<UnEqualableView>>);
static_assert(!std::equality_comparable<UnEqualableViewIter>);
template <class Iter>
constexpr void testOne() {
using Range = BasicTestView<Iter, Iter>;
static_assert(std::ranges::common_range<Range>);
using StrideView = std::ranges::stride_view<Range>;
{
// 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<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<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<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;
}

View File

@@ -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 <cassert>
#include <iterator>
#include <ranges>
#include <type_traits>
#include "../types.h"
template <class T>
concept CanPostIncrementVoid = std::is_same_v<void, decltype(std::declval<T>()++)> && requires(T& t) { t++; };
template <class T>
concept CanPostIncrementIterator = std::is_same_v<T, decltype(std::declval<T>()++)> && requires(T& t) { t = t++; };
template <class T>
concept CanPreIncrementIterator = std::is_same_v<T&, decltype(++(std::declval<T>()))> && requires(T& t) { t = ++t; };
// A stride view with a base that is a non forward range returns void from operator++
using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
static_assert(CanPostIncrementVoid<StrideViewOverInputViewIterator>);
static_assert(!CanPostIncrementIterator<StrideViewOverInputViewIterator>);
static_assert(CanPreIncrementIterator<StrideViewOverInputViewIterator>);
// A stride view with a base that is a forward range returns void from operator++
using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
static_assert(!CanPostIncrementVoid<StrideViewOverForwardViewIterator>);
static_assert(CanPostIncrementIterator<StrideViewOverForwardViewIterator>);
static_assert(CanPreIncrementIterator<StrideViewOverForwardViewIterator>);
template <typename Iter>
requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
constexpr bool test_non_forward_operator_increment(Iter zero_begin, Iter three_begin, Iter end) {
using Base = BasicTestView<Iter, Iter>;
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 <std::forward_iterator Iter>
constexpr bool test_forward_operator_increment(Iter begin, Iter end) {
using Base = BasicTestView<Iter, Iter>;
using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
static_assert(std::ranges::forward_range<Base>);
static_assert(std::weakly_incrementable<StrideViewIterator>);
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*, int*>;
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>(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;
}

View File

@@ -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<Base> iter_move(const iterator& i)
// noexcept(noexcept(ranges::iter_move(i.current_)))
#include <cassert>
#include <ranges>
#include <vector>
#include "../types.h"
#include "test_macros.h"
template <typename T>
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<int*, /*IsSwappable=*/true, /*IsNoExcept=*/true>;
using StrideView = std::ranges::stride_view<View>;
auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin();
static_assert(iter_moveable<std::ranges::iterator_t<StrideView>>);
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<int*, /*IsSwappable=*/true, /*IsNoExcept=*/false>;
using StrideView = std::ranges::stride_view<View>;
auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin();
static_assert(iter_moveable<std::ranges::iterator_t<StrideView>>);
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<int> a = {4, 5, 6, 7, 8};
int iter_move_counter(0);
using View = IterMoveIterSwapTestRange<std::vector<int>::iterator, /*IsSwappable=*/true, /*IsNoExcept=*/false>;
using StrideView = std::ranges::stride_view<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<int*, int*>;
auto sv = std::ranges::stride_view(Base(a, a + 3), 1);
auto it = sv.begin();
static_assert(std::is_same_v<decltype(std::ranges::iter_move(it)), int&&>);
assert(std::ranges::iter_move(it) == 1);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@@ -0,0 +1,92 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, 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<iterator_t<Base>>
#include <cassert>
#include <ranges>
#include "../types.h"
template <typename T>
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<int*, /*IsSwappable=*/true, /*IsNoExcept=*/true>;
using StrideView = std::ranges::stride_view<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<std::ranges::iterator_t<StrideView>>);
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<int*, /*IsSwappable=*/true, /*IsNoExcept=*/false>;
using StrideView = std::ranges::stride_view<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<std::ranges::iterator_t<StrideView>>);
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<int*, /*IsSwappable=*/false, /*IsNoExcept=*/false>;
using StrideView = std::ranges::stride_view<View>;
static_assert(!Swappable<std::ranges::iterator_t<StrideView>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@@ -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 <cassert>
#include <ranges>
#include <type_traits>
#include <vector>
#include "../types.h"
#include "test_iterators.h"
template <class T>
concept CanMinus = std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::declval<T>())> &&
requires(T& t) { t - t; };
template <class T>
concept CanSentinelMinus =
std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::default_sentinel)> &&
std::is_same_v<typename T::difference_type, decltype(std::default_sentinel - std::declval<T>())> && requires(T& t) {
t - std::default_sentinel;
std::default_sentinel - t;
};
template <class T>
concept CanDifferenceMinus = std::is_same_v<T, decltype(std::declval<T>() - 1)> && requires(T& t) { t - 1; };
// Input view: has sentinel minus but not iter-iter minus or difference minus.
using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
using StrideViewOverInputViewIter = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
static_assert(!CanMinus<StrideViewOverInputViewIter>);
static_assert(!CanDifferenceMinus<StrideViewOverInputViewIter>);
static_assert(CanSentinelMinus<StrideViewOverInputViewIter>);
// Forward view: has sentinel minus but not iter-iter minus or difference minus.
using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
using StrideViewOverForwardViewIter = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
static_assert(!CanMinus<StrideViewOverForwardViewIter>);
static_assert(!CanDifferenceMinus<StrideViewOverForwardViewIter>);
static_assert(CanSentinelMinus<StrideViewOverForwardViewIter>);
// Bidirectional view: has sentinel minus but not iter-iter minus or difference minus.
using BidirView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
using StrideViewOverBidirViewIter = std::ranges::iterator_t<std::ranges::stride_view<BidirView>>;
static_assert(!CanMinus<StrideViewOverBidirViewIter>);
static_assert(!CanDifferenceMinus<StrideViewOverBidirViewIter>);
static_assert(CanSentinelMinus<StrideViewOverBidirViewIter>);
// Random access view: has iter-iter minus and difference minus, but no sentinel minus (non-sized sentinel).
using RAView = BasicTestView<random_access_iterator<int*>>;
using StrideViewOverRAViewIter = std::ranges::iterator_t<std::ranges::stride_view<RAView>>;
static_assert(CanMinus<StrideViewOverRAViewIter>);
static_assert(CanDifferenceMinus<StrideViewOverRAViewIter>);
static_assert(!CanSentinelMinus<StrideViewOverRAViewIter>);
template <typename Iter>
requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
constexpr bool test_non_forward_minus(Iter zero_begin, Iter one_begin, Iter end) {
using Base = BasicTestView<Iter, Iter>;
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 <std::forward_iterator Iter>
constexpr bool test_forward_minus(Iter begin, Iter end) {
using Base = BasicTestView<Iter, Iter>;
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<int> 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<int*, int*>;
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<int*, int*>;
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;
}

View File

@@ -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 <cassert>
#include <ranges>
#include <type_traits>
#include "../types.h"
#include "test_iterators.h"
template <class T>
concept CanMinusEqual = std::is_same_v<T&, decltype(std::declval<T>() -= 1)> && requires(T& t) { t -= 1; };
// Input view: no -=.
using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
using StrideViewOverInputViewIter = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
static_assert(!CanMinusEqual<StrideViewOverInputViewIter>);
// Forward view: no -=.
using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
using StrideViewOverForwardViewIter = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
static_assert(!CanMinusEqual<StrideViewOverForwardViewIter>);
// Bidirectional view: no -=.
using BidirView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
using StrideViewOverBidirViewIter = std::ranges::iterator_t<std::ranges::stride_view<BidirView>>;
static_assert(!CanMinusEqual<StrideViewOverBidirViewIter>);
// Random access view: has -=.
using RAView = BasicTestView<random_access_iterator<int*>>;
using StrideViewOverRAViewIter = std::ranges::iterator_t<std::ranges::stride_view<RAView>>;
static_assert(CanMinusEqual<StrideViewOverRAViewIter>);
constexpr bool test() {
{
// Basic -= test with stride 1.
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
using Base = BasicTestView<int*, int*>;
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<int*, int*>;
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<int*, int*>;
auto sv = std::ranges::stride_view<Base>(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;
}

View File

@@ -0,0 +1,102 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// constexpr iterator operator+(difference_type n, const iterator& i)
// constexpr iterator operator+(const iterator& i, difference_type n)
#include <cassert>
#include <ranges>
#include <type_traits>
#include <vector>
#include "../types.h"
#include "test_iterators.h"
template <class T>
concept CanPlus =
std::is_same_v<T, decltype(std::declval<T>() + std::declval<typename T::difference_type>())> &&
std::is_same_v<T, decltype(std::declval<typename T::difference_type>() + std::declval<T>())> &&
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<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
static_assert(std::ranges::input_range<InputView>);
static_assert(!CanPlus<StrideViewOverInputViewIterator>);
// Make sure that we cannot use + on a stride view iterator and difference_type
// over a forward view.(sized sentinel)
using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
static_assert(std::ranges::forward_range<ForwardView>);
static_assert(!CanPlus<StrideViewOverForwardViewIterator>);
// Make sure that we cannot use + on a stride view iterator and difference_type
// over a bidirectional view. (sized sentinel)
using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
static_assert(std::ranges::bidirectional_range<BidirectionalView>);
static_assert(!CanPlus<StrideViewOverBidirectionalViewIterator>);
// Make sure that we can use += on a stride view iterator and difference_type
// over a random access view. (non sized sentinel)
template <typename RandomAccessIterator = random_access_iterator<int*>>
using RandomAccessView = BasicTestView<RandomAccessIterator>;
using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView<>>>;
static_assert(std::ranges::random_access_range<RandomAccessView<>>);
static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
constexpr bool test() {
std::vector<int> 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<std::vector<int>::iterator>;
static_assert(std::ranges::random_access_range<Base>);
{
// 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;
}

View File

@@ -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 <cassert>
#include <ranges>
#include <type_traits>
#include <vector>
#include "../types.h"
#include "test_iterators.h"
template <class T>
concept CanPlus = std::is_same_v<T&, decltype(std::declval<T>() += std::declval<typename T::difference_type>())> &&
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<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
static_assert(std::ranges::input_range<InputView>);
static_assert(!CanPlus<StrideViewOverInputViewIterator>);
// Make sure that we cannot use += on a stride view iterator
// over a forward view.(sized sentinel)
using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
static_assert(std::ranges::forward_range<ForwardView>);
static_assert(!CanPlus<StrideViewOverForwardViewIterator>);
// Make sure that we cannot use += on a stride view iterator
// over a bidirectional view. (sized sentinel)
using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
static_assert(std::ranges::bidirectional_range<BidirectionalView>);
static_assert(!CanPlus<StrideViewOverBidirectionalViewIterator>);
// Make sure that we can use += on a stride view iterator
// over a random access view. (non sized sentinel)
template <typename RandomAccessIterator = random_access_iterator<int*>>
using RandomAccessView = BasicTestView<RandomAccessIterator>;
using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView<>>>;
static_assert(std::ranges::random_access_range<RandomAccessView<>>);
static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
constexpr bool test() {
std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
using Iter = std::vector<int>::iterator;
auto begin = vec.begin();
auto end = vec.end();
using Base = RandomAccessView<Iter>;
static_assert(std::ranges::random_access_range<Base>);
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<int*, int*>;
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;
}

View File

@@ -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 <cassert>
#include <ranges>
#include "../types.h"
#include "test_iterators.h"
template <class T>
concept CanSubscript = requires(T& t) { t[0]; };
// Input view: no subscript.
using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
using StrideInputIter = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
static_assert(!CanSubscript<StrideInputIter>);
// Forward view: no subscript.
using FwdView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
using StrideFwdIter = std::ranges::iterator_t<std::ranges::stride_view<FwdView>>;
static_assert(!CanSubscript<StrideFwdIter>);
// Bidirectional view: no subscript.
using BidirView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
using StrideBidirIter = std::ranges::iterator_t<std::ranges::stride_view<BidirView>>;
static_assert(!CanSubscript<StrideBidirIter>);
// Random access view: subscript available.
using RAView = BasicTestView<random_access_iterator<int*>>;
using StrideRAIter = std::ranges::iterator_t<std::ranges::stride_view<RAView>>;
static_assert(CanSubscript<StrideRAIter>);
constexpr bool test() {
{
// Subscript with stride 1.
int arr[] = {10, 20, 30, 40, 50};
using Base = BasicTestView<int*, int*>;
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<int*, int*>;
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<int*, int*>;
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<int*, int*>;
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<int*, int*>;
auto sv = std::ranges::stride_view(Base(arr, arr + 3), 1);
auto it = sv.begin();
static_assert(std::is_same_v<decltype(it[0]), int&>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@@ -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 <ranges>
#include <type_traits>
#include "test_iterators.h"
#include "../types.h"
template <class T>
concept HasIteratorCategory = requires { typename T::iterator_category; };
template <class Iterator>
using StrideViewFor = std::ranges::stride_view<BasicTestView<Iterator, sentinel_wrapper<Iterator>>>;
template <class Iterator>
using StrideIteratorFor = std::ranges::iterator_t<StrideViewFor<Iterator>>;
struct ForwardIteratorWithInputCategory {
using difference_type = int;
using value_type = int;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
ForwardIteratorWithInputCategory();
ForwardIteratorWithInputCategory& operator++();
ForwardIteratorWithInputCategory operator++(int);
int& operator*() const;
friend bool operator==(ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory);
};
static_assert(std::forward_iterator<ForwardIteratorWithInputCategory>);
// 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<Const, V>.
struct DifferentCategoryView : std::ranges::view_base {
forward_iterator<int*> begin();
forward_iterator<int*> 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<int*> begin();
sentinel_wrapper<cpp17_input_iterator<int*>> end();
forward_iterator<const int*> begin() const;
forward_iterator<const int*> end() const;
};
void f() {
// Check that value_type is range_value_t and difference_type is range_difference_t
{
auto check = []<class Iterator> {
using StrideView = StrideViewFor<Iterator>;
using StrideIterator = StrideIteratorFor<Iterator>;
static_assert(std::is_same_v<typename StrideIterator::value_type, std::ranges::range_value_t<StrideView>>);
static_assert(
std::is_same_v<typename StrideIterator::difference_type, std::ranges::range_difference_t<StrideView>>);
};
check.operator()<cpp17_input_iterator<int*>>();
check.operator()<forward_iterator<int*>>();
check.operator()<bidirectional_iterator<int*>>();
check.operator()<random_access_iterator<int*>>();
check.operator()<contiguous_iterator<int*>>();
check.operator()<int*>();
}
// Check iterator_concept for various categories of ranges
{
static_assert(
std::is_same_v<StrideIteratorFor<cpp17_input_iterator<int*>>::iterator_concept, std::input_iterator_tag>);
static_assert(
std::is_same_v<StrideIteratorFor<forward_iterator<int*>>::iterator_concept, std::forward_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<ForwardIteratorWithInputCategory>::iterator_concept,
std::forward_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<bidirectional_iterator<int*>>::iterator_concept,
std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<random_access_iterator<int*>>::iterator_concept,
std::random_access_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<contiguous_iterator<int*>>::iterator_concept,
std::random_access_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<int*>::iterator_concept, std::random_access_iterator_tag>);
}
// Check iterator_category for various categories of ranges
{
static_assert(!HasIteratorCategory<StrideIteratorFor<cpp17_input_iterator<int*>>>);
static_assert(
std::is_same_v<StrideIteratorFor<forward_iterator<int*>>::iterator_category, std::forward_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<ForwardIteratorWithInputCategory>::iterator_category,
std::input_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<bidirectional_iterator<int*>>::iterator_category,
std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<random_access_iterator<int*>>::iterator_category,
std::random_access_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<contiguous_iterator<int*>>::iterator_category,
std::random_access_iterator_tag>);
static_assert(std::is_same_v<StrideIteratorFor<int*>::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<DifferentCategoryView>;
using NonConstIter = std::ranges::iterator_t<SV>;
using ConstIter = std::ranges::iterator_t<const SV>;
static_assert(std::is_same_v<NonConstIter::iterator_concept, std::forward_iterator_tag>);
static_assert(std::is_same_v<ConstIter::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<NonConstIter::iterator_category, std::forward_iterator_tag>);
static_assert(std::is_same_v<ConstIter::iterator_category, std::random_access_iterator_tag>);
}
// 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<ForwardWhenConstView>;
using NonConstIter = std::ranges::iterator_t<SV>;
using ConstIter = std::ranges::iterator_t<const SV>;
static_assert(!HasIteratorCategory<NonConstIter>);
static_assert(HasIteratorCategory<ConstIter>);
static_assert(std::is_same_v<ConstIter::iterator_category, std::forward_iterator_tag>);
}
}

View File

@@ -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<V>
// constexpr auto size() const requires sized_range<const V>
#include <cassert>
#include <ranges>
#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<BasicTestView<cpp17_input_iterator<int*>>>);
static_assert(!std::ranges::sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
// There is a size member function on a stride view over a view that
// *is* a sized range
static_assert(std::ranges::sized_range<BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/true>>);
static_assert(
std::ranges::sized_range<std::ranges::stride_view<BasicTestView<int*, sentinel_wrapper<int*>, /*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<decltype(iota)>);
auto strided = std::views::stride(iota, 3);
static_assert(std::ranges::sized_range<decltype(strided)>);
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<decltype(iota)>);
auto strided = std::views::stride(iota, 3);
static_assert(std::ranges::sized_range<decltype(strided)>);
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;
}

View File

@@ -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<V> stride() const noexcept;
#include <cassert>
#include <ranges>
#include "test_iterators.h"
#include "test_macros.h"
#include "types.h"
constexpr bool test() {
using View = BasicTestView<cpp17_input_iterator<int*>>;
int arr[] = {1, 2, 3};
View view(cpp17_input_iterator<int*>(arr), cpp17_input_iterator<int*>(arr + 3));
const std::ranges::stride_view<View> strided(view, 2);
static_assert(noexcept(strided.stride()));
ASSERT_SAME_TYPE(std::ranges::range_difference_t<View>, decltype(strided.stride()));
assert(strided.stride() == 2);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@@ -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 <concepts>
#include <cstddef>
#include <functional>
#include <iterator>
#include <ranges>
#include <utility>
#include "test_iterators.h"
#include "test_range.h"
// Concepts
template <typename Iter>
concept IterDifferable = std::invocable<std::minus<>, Iter, Iter>;
// Iterators
// The base for an iterator that keeps a count of the times that it is
// moved and copied.
template <class Derived, std::input_iterator Iter>
struct IterBase {
using value_type = typename std::iterator_traits<Iter>::value_type;
using difference_type = typename std::iterator_traits<Iter>::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 <class Derived, std::input_iterator Iter = int*, bool IsSized = false>
requires((!IsSized) || (IsSized && IterDifferable<Iter>))
struct InputIter : IterBase<Derived, Iter> {
using Base = IterBase<Derived, Iter>;
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<Derived&>(*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<SizedInputIter, int*, true> {
using InputIter::InputIter;
};
static_assert(std::input_iterator<SizedInputIter>);
static_assert(std::sized_sentinel_for<SizedInputIter, SizedInputIter>);
// 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 <std::input_iterator Iter = int*, bool IsIterSwappable = true, bool IsNoExceptIterMoveable = true>
struct IterMoveIterSwapTestRangeIterator
: InputIter<IterMoveIterSwapTestRangeIterator<Iter, IsIterSwappable, IsNoExceptIterMoveable>, Iter, false> {
int* counter_{nullptr};
using InputIter<IterMoveIterSwapTestRangeIterator<Iter, IsIterSwappable, IsNoExceptIterMoveable>, Iter, false>::
InputIter;
constexpr IterMoveIterSwapTestRangeIterator(Iter value, int* counter)
: InputIter<IterMoveIterSwapTestRangeIterator<Iter, IsIterSwappable, IsNoExceptIterMoveable>, 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 <typename Iter = int*, bool IsSwappable = true, bool IsNoExcept = true>
struct IterMoveIterSwapTestRange : std::ranges::view_base {
adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept> begin_;
adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept> end_;
constexpr IterMoveIterSwapTestRange(const Iter& begin, const Iter& end, int* counter)
: begin_(adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept>(begin, counter)),
end_(adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept>(end, counter)) {}
constexpr adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept> begin() const { return begin_; }
constexpr adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept> end() const { return end_; }
};
// Views
// Depending upon configuration, ViewOrRange is either a View or not.
template <bool IsView>
struct MaybeView {};
template <>
struct MaybeView<true> : std::ranges::view_base {};
template <std::input_iterator Iter,
std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
bool IsSized = false,
bool IsView = false,
bool IsCopyable = false >
requires((!IsSized) || (IsSized && IterDifferable<Iter>))
struct BasicTestViewOrRange : MaybeView<IsView> {
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 <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
requires((!IsSized) || (IsSized && IterDifferable<Iter>))
using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
sentinel_wrapper<cpp17_input_iterator<int*>>,
false>>);
static_assert(std::copyable<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>>
using CopyableView = MaybeCopyableAlwaysMoveableView<Iter, Sent>;
static_assert(std::copyable<CopyableView<cpp17_input_iterator<int*>>>);
template <class Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>>
using MoveOnlyView = MaybeCopyableAlwaysMoveableView<Iter, Sent, false>;
static_assert(!std::copyable<MoveOnlyView<cpp17_input_iterator<int*>>>);
template <bool IsSimple, bool IsConst = IsSimple, bool IsCommon = true, bool IsSized = false>
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<false, false, true>;
using UnSimpleConstView = MaybeConstCommonSimpleView<false, true, true>;
using UnsimpleUnCommonConstView = MaybeConstCommonSimpleView<false, true, false>;
using SimpleUnCommonConstView = MaybeConstCommonSimpleView<true, true, false>;
using SimpleCommonConstView = MaybeConstCommonSimpleView<true, true, true>;
// 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 <class Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>>
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<Iter>())) it_;
decltype(base(std::declval<Sent>())) sent_;
};
// Ranges
template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
requires((!IsSized) || (IsSized && IterDifferable<Iter>))
using BasicTestRange = BasicTestViewOrRange<Iter, Sent, IsSized, false>;
#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H

View File

@@ -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},