diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 268de27b97a7..36048165003d 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -38,8 +38,8 @@ What's New in Libc++ 22.0.0? Implemented Papers ------------------ -- P2321R2: ``zip`` (`Github `__) (The paper is partially implemented. ``zip_transform_view`` - and `adjacent_view` are implemented in this release) +- P2321R2: ``zip`` (`Github `__) (The paper is partially implemented. ``zip_transform_view``, + `adjacent_view`, and ``adjacent_transform_view`` are implemented in this release) - P2988R12: ``std::optional`` (`Github `__) - P3044R2: sub-``string_view`` from ``string`` (`Github `__) - P3223R2: Making ``std::istream::ignore`` less surprising (`Github `__) diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 9df40eab678a..eb423ae923b4 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -702,6 +702,7 @@ set(files __random/uniform_real_distribution.h __random/weibull_distribution.h __ranges/access.h + __ranges/adjacent_transform_view.h __ranges/adjacent_view.h __ranges/all.h __ranges/as_rvalue_view.h diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h new file mode 100644 index 000000000000..11b1176824fa --- /dev/null +++ b/libcxx/include/__ranges/adjacent_transform_view.h @@ -0,0 +1,406 @@ +// -*- 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_ADJACENT_TRANSFORM_VIEW_H +#define _LIBCPP___RANGES_ADJACENT_TRANSFORM_VIEW_H + +#include <__config> + +#include <__algorithm/min.h> +#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 <__concepts/invocable.h> +#include <__cstddef/size_t.h> +#include <__functional/bind_back.h> +#include <__functional/invoke.h> +#include <__functional/operations.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/next.h> +#include <__iterator/prev.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/adjacent_view.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/empty_view.h> +#include <__ranges/movable_box.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/size.h> +#include <__ranges/view_interface.h> +#include <__ranges/zip_transform_view.h> +#include <__type_traits/common_type.h> +#include <__type_traits/decay.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/is_object.h> +#include <__type_traits/is_referenceable.h> +#include <__type_traits/make_unsigned.h> +#include <__type_traits/maybe_const.h> +#include <__utility/declval.h> +#include <__utility/forward.h> +#include <__utility/in_place.h> +#include <__utility/integer_sequence.h> +#include <__utility/move.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 +struct __apply_n { + template + static auto __apply(index_sequence<_Is...>) -> invoke_result_t<_Fn, decltype((void)_Is, std::declval<_Tp>())...>; + + template + static auto operator()(_Tp&&) -> decltype(__apply<_Tp>(make_index_sequence<_Np>{})); +}; + +template + requires view<_View> && (_Np > 0) && is_object_v<_Fn> && + regular_invocable<__apply_n<_Fn&, _Np>, range_reference_t<_View>> && + __referenceable, range_reference_t<_View>>> +class adjacent_transform_view : public view_interface> { +private: + _LIBCPP_NO_UNIQUE_ADDRESS adjacent_view<_View, _Np> __inner_; + _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Fn> __fun_; + + using _InnerView _LIBCPP_NODEBUG = adjacent_view<_View, _Np>; + + template + using __inner_iterator _LIBCPP_NODEBUG = iterator_t<__maybe_const<_Const, _InnerView>>; + + template + using __inner_sentinel _LIBCPP_NODEBUG = sentinel_t<__maybe_const<_Const, _InnerView>>; + + template + class __iterator; + + template + class __sentinel; + +public: + _LIBCPP_HIDE_FROM_ABI adjacent_transform_view() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit adjacent_transform_view(_View __base, _Fn __fun) + : __inner_(std::move(__base)), __fun_(std::in_place, std::move(__fun)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __inner_.base(); + } + _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator(*this, __inner_.begin()); } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range && regular_invocable<__apply_n, range_reference_t> + { + return __iterator(*this, __inner_.begin()); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() { + if constexpr (common_range<_InnerView>) { + return __iterator(*this, __inner_.end()); + } else { + return __sentinel(__inner_.end()); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires range && regular_invocable<__apply_n, range_reference_t> + { + if constexpr (common_range) { + return __iterator(*this, __inner_.end()); + } else { + return __sentinel(__inner_.end()); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_InnerView> + { + return __inner_.size(); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return __inner_.size(); + } +}; + +template + requires view<_View> && (_Np > 0) && is_object_v<_Fn> && + regular_invocable<__apply_n<_Fn&, _Np>, range_reference_t<_View>> && + __referenceable, range_reference_t<_View>>> +template +class adjacent_transform_view<_View, _Fn, _Np>::__iterator { + friend adjacent_transform_view; + + using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, adjacent_transform_view>; + using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; + + _Parent* __parent_ = nullptr; + __inner_iterator<_Const> __inner_; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, __inner_iterator<_Const> __inner) + : __parent_(std::addressof(__parent)), __inner_(std::move(__inner)) {} + + static consteval auto __get_iterator_category() { + using _Cat = iterator_traits>::iterator_category; + if constexpr (!is_reference_v< + invoke_result_t<__apply_n<__maybe_const<_Const, _Fn>&, _Np>, range_reference_t<_Base>>>) + return input_iterator_tag{}; + else if constexpr (derived_from<_Cat, random_access_iterator_tag>) + return random_access_iterator_tag{}; + else if constexpr (derived_from<_Cat, bidirectional_iterator_tag>) + return bidirectional_iterator_tag{}; + else if constexpr (derived_from<_Cat, forward_iterator_tag>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + + template + static consteval bool __noexcept_dereference(index_sequence<_Is...>) { + return noexcept(std::invoke( + std::declval<__maybe_const<_Const, _Fn>&>(), ((void)_Is, *std::declval const&>())...)); + } + +public: + using iterator_category = decltype(__get_iterator_category()); + using iterator_concept = typename __inner_iterator<_Const>::iterator_concept; + using value_type = + remove_cvref_t&, _Np>, range_reference_t<_Base>>>; + using difference_type = range_difference_t<_Base>; + + _LIBCPP_HIDE_FROM_ABI __iterator() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator __i) + requires _Const && convertible_to<__inner_iterator, __inner_iterator> + : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const + noexcept(__noexcept_dereference(make_index_sequence<_Np>{})) { + return std::apply( + [&](const auto&... __iters) -> decltype(auto) { return std::invoke(*__parent_->__fun_, *__iters...); }, + __adjacent_view_iter_access::__get_current(__inner_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + ++__inner_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) { + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() + requires bidirectional_range<_Base> + { + --__inner_; + 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 __x) + requires random_access_range<_Base> + { + __inner_ += __x; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x) + requires random_access_range<_Base> + { + __inner_ -= __x; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const + requires random_access_range<_Base> + { + return std::apply( + [&](const auto&... __iters) -> decltype(auto) { return std::invoke(*__parent_->__fun_, __iters[__n]...); }, + __adjacent_view_iter_access::__get_current(__inner_)); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) { + return __x.__inner_ == __y.__inner_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __x.__inner_ < __y.__inner_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __x.__inner_ > __y.__inner_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __x.__inner_ <= __y.__inner_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __x.__inner_ >= __y.__inner_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> && three_way_comparable<__inner_iterator<_Const>> + { + return __x.__inner_ <=> __y.__inner_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + return __iterator(*__i.__parent_, __i.__inner_ + __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i) + requires random_access_range<_Base> + { + return __iterator(*__i.__parent_, __i.__inner_ + __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + return __iterator(*__i.__parent_, __i.__inner_ - __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y) + requires sized_sentinel_for<__inner_iterator<_Const>, __inner_iterator<_Const>> + { + return __x.__inner_ - __y.__inner_; + } +}; + +template + requires view<_View> && (_Np > 0) && is_object_v<_Fn> && + regular_invocable<__apply_n<_Fn&, _Np>, range_reference_t<_View>> && + __referenceable, range_reference_t<_View>>> +template +class adjacent_transform_view<_View, _Fn, _Np>::__sentinel { + friend adjacent_transform_view; + + __inner_sentinel<_Const> __inner_; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(__inner_sentinel<_Const> __inner) + : __inner_(std::move(__inner)) {} + +public: + _LIBCPP_HIDE_FROM_ABI __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel __i) + requires _Const && convertible_to<__inner_sentinel, __inner_sentinel<_Const>> + : __inner_(std::move(__i.__inner_)) {} + + template + requires sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__inner_ == __y.__inner_; + } + + template + requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>> + operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__inner_ - __y.__inner_; + } + + template + requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>> + operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) { + return __x.__inner_ - __y.__inner_; + } +}; + +namespace views { +namespace __adjacent_transform { + +template +struct __fn : __range_adaptor_closure<__fn<_Np>> { + template + requires(_Np == 0 && forward_range<_Range &&>) + _LIBCPP_HIDE_FROM_ABI static constexpr auto + operator()(_Range&&, _Fn&& __fn) noexcept(noexcept(views::zip_transform(std::forward<_Fn>(__fn)))) + -> decltype(views::zip_transform(std::forward<_Fn>(__fn))) { + return views::zip_transform(std::forward<_Fn>(__fn)); + } + + template + _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept( + noexcept(adjacent_transform_view, decay_t<_Fn>, _Np>( + std::forward<_Range>(__range), std::forward<_Fn>(__fn)))) + -> decltype(adjacent_transform_view, decay_t<_Fn>, _Np>( + std::forward<_Range>(__range), std::forward<_Fn>(__fn))) { + return adjacent_transform_view, decay_t<_Fn>, _Np>( + std::forward<_Range>(__range), std::forward<_Fn>(__fn)); + } + + template + requires constructible_from, _Fn> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __f) const + noexcept(is_nothrow_constructible_v, _Fn>) { + return __pipeable(std::__bind_back(*this, std::forward<_Fn>(__f))); + } +}; + +} // namespace __adjacent_transform +inline namespace __cpo { +template +inline constexpr auto adjacent_transform = __adjacent_transform::__fn<_Np>{}; +inline constexpr auto pairwise_transform = adjacent_transform<2>; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_ADJACENT_TRANSFORM_VIEW_H diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h index ab9696951303..40474b85c794 100644 --- a/libcxx/include/__ranges/adjacent_view.h +++ b/libcxx/include/__ranges/adjacent_view.h @@ -141,10 +141,18 @@ public: } }; +struct __adjacent_view_iter_access { + template + _LIBCPP_HIDE_FROM_ABI constexpr static auto& __get_current(_Iter& __it) noexcept { + return __it.__current_; + } +}; + template requires view<_View> && (_Np > 0) template class adjacent_view<_View, _Np>::__iterator { + friend __adjacent_view_iter_access; friend adjacent_view; using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; array, _Np> __current_ = array, _Np>(); diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index ce168f77dfea..4434a5446e35 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1852,6 +1852,7 @@ module std [system] { module ranges { module access { header "__ranges/access.h" } + module adjacent_transform_view { header "__ranges/adjacent_transform_view.h" } module adjacent_view { header "__ranges/adjacent_view.h" } module all { header "__ranges/all.h" diff --git a/libcxx/include/ranges b/libcxx/include/ranges index aa7f0b36c387..9f725b12ac6c 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -372,6 +372,17 @@ namespace std::ranges { inline constexpr auto pairwise = adjacent<2>; } + // [range.adjacent.transform], adjacent transform view + template + requires see below + class adjacent_transform_view; + + namespace views { + template + constexpr unspecified adjacent_transform = unspecified; + inline constexpr auto pairwise_transform = adjacent_transform<2>; + } + // [range.as.rvalue] template requires input_range @@ -467,6 +478,7 @@ namespace std { # endif # if _LIBCPP_STD_VER >= 23 +# include <__ranges/adjacent_transform_view.h> # include <__ranges/adjacent_view.h> # include <__ranges/as_rvalue_view.h> # include <__ranges/chunk_by_view.h> diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index 67e571b9567d..5caa2c052c00 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -308,9 +308,6 @@ export namespace std { using std::ranges::views::pairwise; } // namespace views -#endif // _LIBCPP_STD_VER >= 23 - -#if 0 using std::ranges::adjacent_transform_view; namespace views { @@ -318,6 +315,9 @@ export namespace std { using std::ranges::views::pairwise_transform; } // namespace views +#endif // _LIBCPP_STD_VER >= 23 + +#if 0 using std::ranges::chunk_view; using std::ranges::chunk_view; diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/adaptor.pass.cpp new file mode 100644 index 000000000000..dc94f6af64ec --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/adaptor.pass.cpp @@ -0,0 +1,275 @@ +//===----------------------------------------------------------------------===// +// +// 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 std::views::adjacent_transform + +#include +#include +#include +#include +#include +#include + +#include "helpers.h" +#include "../range_adaptor_types.h" + +struct Fn { + int operator()(auto...) const; +}; + +struct VoidFn { + void operator()(auto...) const; +}; + +struct StringFn { + template + requires(sizeof...(Ts) > 0 && (std::is_same_v && ...)) + int operator()(Ts...) const; +}; + +template +constexpr void test_constraints() { + // needs to be a range + static_assert(std::is_invocable_v)), std::ranges::empty_view, Fn>); + static_assert(!std::is_invocable_v)), int, Fn>); + + // underlying needs to be forward_range + static_assert(std::is_invocable_v)), ForwardSizedView, Fn>); + static_assert(!std::is_invocable_v)), InputCommonView, Fn>); + + // function needs to be callable with N range references + static_assert(std::is_invocable_v)), ForwardSizedView, Fn>); + static_assert(!std::is_invocable_v)), ForwardSizedView, StringFn>); + + // results need to be referenceable + static_assert(std::is_invocable_v)), ForwardSizedView, Fn>); + static_assert(!std::is_invocable_v)), ForwardSizedView, VoidFn>); +} + +constexpr void test_pairwise_transform_constraints() { + // needs to be a range + static_assert(std::is_invocable_v, Fn>); + static_assert(!std::is_invocable_v); + + // underlying needs to be forward_range + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + + // function needs to be callable with N range references + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + + // results need to be referenceable + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); +} + +constexpr void test_all_constraints() { + test_pairwise_transform_constraints(); + test_constraints<0>(); + test_constraints<1>(); + test_constraints<2>(); + test_constraints<3>(); + test_constraints<5>(); +} + +constexpr void test_zero_case() { + // N == 0 is a special case that always results in an empty range + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + std::same_as> decltype(auto) v = + std::views::adjacent_transform<0>(ContiguousCommonView{buffer}, Fn{}); + assert(std::ranges::size(v) == 0); +} + +struct MoveOnlyView : ForwardSizedView { + using ForwardSizedView::ForwardSizedView; + + constexpr MoveOnlyView(MoveOnlyView&&) = default; + constexpr MoveOnlyView(const MoveOnlyView&) = delete; + + constexpr MoveOnlyView& operator=(MoveOnlyView&&) = default; + constexpr MoveOnlyView& operator=(const MoveOnlyView&) = delete; +}; + +template +constexpr void test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + Validator validator{}; + + // Test `views::adjacent_transform(r)` + { + using View = std::ranges::adjacent_transform_view; + std::same_as decltype(auto) v = std::views::adjacent_transform(ContiguousCommonView{buffer}, Fn{}); + assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0)); + auto it = v.begin(); + validator(buffer, *it, 0); + } + + // Test `views::adjacent_transform(move only view)` + { + using View = std::ranges::adjacent_transform_view; + std::same_as decltype(auto) v = std::views::adjacent_transform(MoveOnlyView{buffer}, Fn{}); + assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0)); + auto it = v.begin(); + validator(buffer, *it, 0); + } + + // Test `r | views::adjacent_transform` + { + using View = std::ranges::adjacent_transform_view; + std::same_as decltype(auto) v = ContiguousCommonView{buffer} | std::views::adjacent_transform(Fn{}); + assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0)); + auto it = v.begin(); + validator(buffer, *it, 0); + } + + // Test `move only view | views::adjacent_transform` + { + using View = std::ranges::adjacent_transform_view; + std::same_as decltype(auto) v = MoveOnlyView{buffer} | std::views::adjacent_transform(Fn{}); + assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0)); + auto it = v.begin(); + validator(buffer, *it, 0); + } + + // Test adjacent_transform | adjacent_transform + { + using View = std::ranges:: + adjacent_transform_view, MakeTuple, N>; + + auto twice = std::views::adjacent_transform(MakeTuple{}) | std::views::adjacent_transform(MakeTuple{}); + std::same_as decltype(auto) v = ContiguousCommonView{buffer} | twice; + assert(std::ranges::size(v) == (N <= 5 ? 10 - 2 * N : 0)); + + if (std::ranges::size(v) == 0) + return; + + auto it = v.begin(); + auto nestedTuple = *it; + + auto innerFirstTuple = std::get<0>(nestedTuple); + + assert(std::get<0>(innerFirstTuple) == buffer[0]); + if constexpr (N >= 2) + assert(std::get<1>(innerFirstTuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<2>(innerFirstTuple) == buffer[2]); + if constexpr (N >= 4) + assert(std::get<3>(innerFirstTuple) == buffer[3]); + if constexpr (N >= 5) + assert(std::get<4>(innerFirstTuple) == buffer[4]); + + if constexpr (N >= 2) { + auto innerSecondTuple = std::get<1>(nestedTuple); + assert(std::get<0>(innerSecondTuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<1>(innerSecondTuple) == buffer[2]); + if constexpr (N >= 4) + assert(std::get<2>(innerSecondTuple) == buffer[3]); + if constexpr (N >= 5) + assert(std::get<3>(innerSecondTuple) == buffer[4]); + } + } +} + +constexpr void test_pairwise_transform() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + // Test `views::pairwise_transform(r)` + using View = std::ranges::adjacent_transform_view; + std::same_as decltype(auto) v = std::views::pairwise_transform(ContiguousCommonView{buffer}, MakeTuple{}); + assert(std::ranges::size(v) == 7); + auto it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + assert(std::get<1>(tuple) == buffer[1]); + } + + { + // Test `views::pairwise_transform(move only view)` + using View = std::ranges::adjacent_transform_view; + std::same_as decltype(auto) v = std::views::pairwise_transform(MoveOnlyView{buffer}, MakeTuple{}); + assert(std::ranges::size(v) == 7); + auto it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + assert(std::get<1>(tuple) == buffer[1]); + } + { + // Test `r | views::pairwise_transform` + using View = std::ranges::adjacent_transform_view; + std::same_as decltype(auto) v = ContiguousCommonView{buffer} | std::views::pairwise_transform(MakeTuple{}); + assert(std::ranges::size(v) == 7); + auto it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + assert(std::get<1>(tuple) == buffer[1]); + } + { + // Test `move only view | views::pairwise_transform` + using View = std::ranges::adjacent_transform_view; + std::same_as decltype(auto) v = MoveOnlyView{buffer} | std::views::pairwise_transform(MakeTuple{}); + assert(std::ranges::size(v) == 7); + auto it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + assert(std::get<1>(tuple) == buffer[1]); + } + { + // Test pairwise_transform | pairwise_transform + using View = std::ranges:: + adjacent_transform_view, MakeTuple, 2>; + + auto twice = std::views::pairwise_transform(MakeTuple{}) | std::views::pairwise_transform(MakeTuple{}); + std::same_as decltype(auto) v = ContiguousCommonView{buffer} | twice; + assert(std::ranges::size(v) == 6); + + auto it = v.begin(); + auto nestedTuple = *it; + + auto innerFirstTuple = std::get<0>(nestedTuple); + + assert(std::get<0>(innerFirstTuple) == buffer[0]); + assert(std::get<1>(innerFirstTuple) == buffer[1]); + + auto innerSecondTuple = std::get<1>(nestedTuple); + assert(std::get<0>(innerSecondTuple) == buffer[1]); + assert(std::get<1>(innerSecondTuple) == buffer[2]); + } +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); +} + +constexpr bool test() { + test_all_constraints(); + test_zero_case(); + test_pairwise_transform(); + test<1>(); + test<2>(); + test<3>(); + test<5>(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/base.pass.cpp new file mode 100644 index 000000000000..8815fbe66c83 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/base.pass.cpp @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// 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 View base() const& requires copy_constructible; +// constexpr View base() &&; + +#include + +#include +#include +#include + +#include "helpers.h" + +struct Range : std::ranges::view_base { + template + constexpr explicit Range(int (&buffer)[N]) : begin_(&buffer[0]), end_(&buffer[0] + N) {} + constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {} + constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {} + Range& operator=(Range const&) = default; + Range& operator=(Range&&) = default; + constexpr int* begin() const { return begin_; } + constexpr int* end() const { return end_; } + + int* begin_; + int* end_; + bool wasCopyInitialized = false; + bool wasMoveInitialized = false; +}; + +static_assert(std::ranges::view); +static_assert(std::ranges::forward_range); + +struct NonCopyableRange : std::ranges::view_base { + explicit NonCopyableRange(int*, int*); + NonCopyableRange(NonCopyableRange const&) = delete; + NonCopyableRange(NonCopyableRange&&) = default; + NonCopyableRange& operator=(NonCopyableRange const&) = default; + NonCopyableRange& operator=(NonCopyableRange&&) = default; + int* begin() const; + int* end() const; +}; + +static_assert(!std::copy_constructible); + +template +concept CanCallBaseOn = requires(T t) { std::forward(t).base(); }; + +template +constexpr void test() { + int buff[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + // Check the const& overload + { + Range range(buff); + auto view = range | std::views::adjacent_transform(Fn{}); + + std::same_as decltype(auto) result = view.base(); + assert(result.wasCopyInitialized); + assert(result.begin() == buff); + assert(result.end() == buff + 9); + } + + // Check the && overload + { + Range range(buff); + auto view = range | std::views::adjacent_transform(Fn{}); + std::same_as decltype(auto) result = std::move(view).base(); + assert(result.wasMoveInitialized); + assert(result.begin() == buff); + assert(result.end() == buff + 9); + } + + // Ensure the const& overload is not considered when the base is not copy-constructible + { + static_assert(!CanCallBaseOn const&>); + static_assert(!CanCallBaseOn&>); + static_assert(!CanCallBaseOn const&&>); + static_assert(CanCallBaseOn&&>); + static_assert(CanCallBaseOn>); + } +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/begin.pass.cpp new file mode 100644 index 000000000000..1c4a49da2b8a --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/begin.pass.cpp @@ -0,0 +1,133 @@ +//===----------------------------------------------------------------------===// +// +// 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(); +// constexpr auto begin() const requires range && +// regular_invocable, N)...> + +#include + +#include +#include +#include +#include +#include + +#include "helpers.h" +#include "../range_adaptor_types.h" + +template +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template +concept HasBegin = requires(T& t) { t.begin(); }; + +struct NoConstBeginView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +struct OnlyNonConstFn { + template + requires((!std::is_const_v>) && ...) + int operator()(T&&...) const; +}; + +template +constexpr void test_one() { + using View = std::ranges::adjacent_transform_view; + Validator validator{}; + { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + View v(Range{buffer}, Fn{}); + + auto it = v.begin(); + validator(buffer, *it, 0); + + auto cit = std::as_const(v).begin(); + validator(buffer, *cit, 0); + } + + { + // empty range + std::array buffer = {}; + View v(Range{buffer.data(), 0}, Fn{}); + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + assert(it == v.end()); + assert(cit == std::as_const(v).end()); + } + + if constexpr (N > 2) { + // N greater than range size + int buffer[2] = {1, 2}; + View v(Range{buffer}, Fn{}); + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + assert(it == v.end()); + assert(cit == std::as_const(v).end()); + } +} + +template +constexpr void test_simple() { + test_one(); + + using View = std::ranges::adjacent_transform_view; + + // non-const begin always exists and return iterator + static_assert(!std::is_same_v, std::ranges::iterator_t>); +} + +template +constexpr void test_non_simple() { + test_one(); + + using View = std::ranges::adjacent_transform_view; + static_assert(!std::is_same_v, std::ranges::iterator_t>); +} + +template +constexpr void test() { + test_simple>(); + test_simple>(); + test_simple>(); + test_simple>(); + + test_non_simple>(); + test_non_simple>(); + test_non_simple>(); + test_non_simple>(); + + // Test with view that doesn't support const begin() + using ViewWithNoConstBegin = std::ranges::adjacent_transform_view; + static_assert(HasBegin); + static_assert(!HasConstBegin); + + using OnlyNonConstView = std::ranges::adjacent_transform_view; + static_assert(HasBegin); + static_assert(!HasConstBegin); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/ctor.default.pass.cpp new file mode 100644 index 000000000000..b4898dfaf7f7 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/ctor.default.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// adjacent_transform_view() = default; + +#include + +#include +#include +#include + +#include "helpers.h" + +constexpr int buff[] = {1, 2, 3, 4, 5}; + +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() : begin_(buff), end_(buff + std::ranges::size(buff)) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + +private: + int const* begin_; + int const* end_; +}; + +struct NoDefaultCtrView : std::ranges::view_base { + NoDefaultCtrView() = delete; + int* begin() const; + int* end() const; +}; + +static_assert( + std::is_default_constructible_v>); +static_assert(std::is_default_constructible_v>); +static_assert( + std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); + +template +constexpr void test() { + { + using View = std::ranges::adjacent_transform_view; + View v = View(); // the default constructor is not explicit + assert(v.size() == std::ranges::size(buff) - (N - 1)); + Validator validator{}; + auto it = v.begin(); + validator(buff, *it, 0); + } +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/ctor.views.pass.cpp new file mode 100644 index 000000000000..bee50ef768bb --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/ctor.views.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr explicit adjacent_transform_view(View, F) + +#include +#include +#include + +#include "../range_adaptor_types.h" +#include "helpers.h" + +template +void conversion_test(T); + +template +concept implicitly_constructible_from = requires(Args&&... args) { conversion_test({std::move(args)...}); }; + +// test constructor is explicit +static_assert( + std::constructible_from, SimpleCommon, MakeTuple>); +static_assert(!implicitly_constructible_from, + SimpleCommon, + MakeTuple>); + +static_assert(std::constructible_from, SimpleCommon, Tie>); +static_assert( + !implicitly_constructible_from, SimpleCommon, Tie>); + +struct MoveAwareView : std::ranges::view_base { + int moves = 0; + constexpr MoveAwareView() = default; + constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; } + constexpr MoveAwareView& operator=(MoveAwareView&& other) { + moves = other.moves + 1; + other.moves = 0; + return *this; + } + constexpr const int* begin() const { return &moves; } + constexpr const int* end() const { return &moves + 1; } +}; + +template +constexpr void test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + std::ranges::adjacent_transform_view v{View{buffer}, Fn{}}; + Validator validator{}; + auto it = v.begin(); + validator(buffer, *it, 0); +} + +template +constexpr void test() { + { + // arguments are moved once + MoveAwareView mv; + std::ranges::adjacent_transform_view v{std::move(mv), GetFirst{}}; + auto& first = *v.begin(); + // one move from the local variable to parameter, one move from parameter to adjacent_view, and one move to adjacent_transform_view + assert(first == 3); + } + + test(); + test(); + test(); + test(); +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/end.pass.cpp new file mode 100644 index 000000000000..658588afbc09 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/end.pass.cpp @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto end() +// constexpr auto end() const requires range && +// regular_invocable, N)...> + +#include +#include +#include +#include +#include + +#include "../range_adaptor_types.h" +#include "helpers.h" + +template +concept HasConstEnd = requires(const T& ct) { ct.end(); }; + +template +concept HasEnd = requires(T& t) { t.end(); }; + +struct NoConstEndView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +struct OnlyNonConstFn { + template + requires((!std::is_const_v>) && ...) + int operator()(T&&...) const; +}; + +template +constexpr void test_one() { + { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + std::ranges::adjacent_transform_view v{R{buffer}, Fn{}}; + + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + auto endIt = v.end(); + auto cendIt = std::as_const(v).end(); + assert(it != endIt); + assert(cit != cendIt); + assert(it + (8 - (N - 1)) == endIt); + assert(cit + (8 - (N - 1)) == cendIt); + } + { + // empty range + std::array buffer = {}; + std::ranges::adjacent_transform_view v{R{buffer.data(), 0}, Fn{}}; + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + auto endIt = v.end(); + auto cendIt = std::as_const(v).end(); + assert(it == endIt); + assert(cit == cendIt); + } + if constexpr (N > 2) { + // N greater than range size + int buffer[2] = {1, 2}; + std::ranges::adjacent_transform_view v{R{buffer}, Fn{}}; + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + auto endIt = v.end(); + auto cendIt = std::as_const(v).end(); + assert(it == endIt); + assert(cit == cendIt); + } +} + +template +constexpr void test_simple_common_types() { + using NonConstView = std::ranges::adjacent_transform_view; + using ConstView = const NonConstView; + + static_assert(std::ranges::common_range); + static_assert(std::ranges::common_range); + + // non-const end always exists and returns iterator + static_assert(!std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test_simple_non_common_types() { + using NonConstView = std::ranges::adjacent_transform_view; + using ConstView = const NonConstView; + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + + // non-const end always exists and returns sentinel + static_assert(!std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test_non_simple_common_types() { + using NonConstView = std::ranges::adjacent_transform_view; + using ConstView = const NonConstView; + + static_assert(std::ranges::common_range); + static_assert(std::ranges::common_range); + static_assert(!std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test_non_simple_non_common_types() { + using NonConstView = std::ranges::adjacent_transform_view; + using ConstView = const NonConstView; + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + static_assert(!std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test_forward_only() { + using NonConstView = std::ranges::adjacent_transform_view; + using ConstView = const NonConstView; + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + static_assert(!std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test() { + test_simple_common_types(); + test_simple_non_common_types(); + test_non_simple_common_types(); + test_non_simple_non_common_types(); +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); + + // Test with view that doesn't support const end() + using ViewWithNoConstBegin = std::ranges::adjacent_transform_view; + static_assert(HasEnd); + static_assert(!HasConstEnd); + + using OnlyNonConstView = std::ranges::adjacent_transform_view; + static_assert(HasEnd); + static_assert(!HasConstEnd); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/general.pass.cpp new file mode 100644 index 000000000000..04afe4284e40 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/general.pass.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Some basic examples of how adjacent_transform_view might be used in the wild. This is a general +// collection of sample algorithms and functions that try to mock general usage of +// this view. + +#include +#include +#include + +#include +#include + +constexpr bool test() { + std::vector v = {1, 2, 3, 4}; + std::vector expected = {2, 6, 12}; + + { + auto expected_index = 0; + for (auto x : v | std::views::adjacent_transform<2>(std::multiplies())) { + assert(x == expected[expected_index++]); + } + } + { + auto expected_index = 0; + for (auto x : v | std::views::pairwise_transform(std::multiplies())) { + assert(x == expected[expected_index++]); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h new file mode 100644 index 000000000000..39f2f856c149 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h @@ -0,0 +1,108 @@ +//===----------------------------------------------------------------------===// +// +// 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_ADJACENT_TRANSFORM_HELPERS_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADJACENT_TRANSFORM_HELPERS_H + +#include +#include +#include +#include + +// intentionally not using meta programming for the expected tuple types + +struct Multiply { + template + constexpr auto operator()(Ts... args) const { + return (1 * ... * args); + } +}; + +struct MakeTuple { + constexpr auto operator()(auto&&... args) const { return std::make_tuple(std::forward(args)...); } +}; + +struct Tie { + constexpr auto operator()(auto&&... args) const { return std::tie(std::forward(args)...); } +}; + +struct GetFirst { + constexpr decltype(auto) operator()(auto&& first, auto&&...) const { return std::forward(first); } +}; + +template +struct ValidateTieFromIndex { + constexpr void operator()(auto&& buffer, auto&& tuple, std::size_t idx) const { + assert(&std::get<0>(tuple) == &buffer[idx]); + if constexpr (N >= 2) + assert(&std::get<1>(tuple) == &buffer[idx + 1]); + if constexpr (N >= 3) + assert(&std::get<2>(tuple) == &buffer[idx + 2]); + if constexpr (N >= 4) + assert(&std::get<3>(tuple) == &buffer[idx + 3]); + if constexpr (N >= 5) + assert(&std::get<4>(tuple) == &buffer[idx + 4]); + } +}; + +template +struct ValidateTupleFromIndex { + constexpr void operator()(auto&& buffer, auto&& tuple, std::size_t idx) const { + assert(std::get<0>(tuple) == buffer[idx]); + if constexpr (N >= 2) + assert(std::get<1>(tuple) == buffer[idx + 1]); + if constexpr (N >= 3) + assert(std::get<2>(tuple) == buffer[idx + 2]); + if constexpr (N >= 4) + assert(std::get<3>(tuple) == buffer[idx + 3]); + if constexpr (N >= 5) + assert(std::get<4>(tuple) == buffer[idx + 4]); + } +}; + +template +struct ValidateGetFirstFromIndex { + constexpr void operator()(auto&& buffer, auto&& result, std::size_t idx) const { assert(&result == &buffer[idx]); } +}; + +template +struct ValidateMultiplyFromIndex { + constexpr void operator()(auto&& buffer, auto&& result, std::size_t idx) const { + auto expected = std::accumulate(buffer + idx, buffer + idx + N, 1, std::multiplies<>()); + assert(result == expected); + } +}; + +template +struct ExpectedTupleType; + +template +struct ExpectedTupleType<1, T> { + using type = std::tuple; +}; +template +struct ExpectedTupleType<2, T> { + using type = std::tuple; +}; +template +struct ExpectedTupleType<3, T> { + using type = std::tuple; +}; +template +struct ExpectedTupleType<4, T> { + using type = std::tuple; +}; +template +struct ExpectedTupleType<5, T> { + using type = std::tuple; +}; + +template +using expectedTupleType = typename ExpectedTupleType::type; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADJACENT_TRANSFORM_HELPERS_H diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/arithmetic.pass.cpp new file mode 100644 index 000000000000..294550d6ef5c --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/arithmetic.pass.cpp @@ -0,0 +1,164 @@ +//===----------------------------------------------------------------------===// +// +// 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 x) +// requires random_access_range; +// constexpr iterator& operator-=(difference_type x) +// requires random_access_range; +// friend constexpr iterator operator+(const iterator& i, difference_type n) +// requires random_access_range; +// friend constexpr iterator operator+(difference_type n, const iterator& i) +// requires random_access_range; +// friend constexpr iterator operator-(const iterator& i, difference_type n) +// requires random_access_range; +// friend constexpr difference_type operator-(const iterator& x, const iterator& y) +// requires sized_sentinel_for, iterator_t>; + +#include +#include + +#include +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +template +concept canPlusEqual = requires(T& t, U& u) { t += u; }; + +template +concept canMinusEqual = requires(T& t, U& u) { t -= u; }; + +template +constexpr void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + Validator validator{}; + + R rng{buffer}; + { + // operator+(x, n) and operator+= + std::ranges::adjacent_transform_view v(rng, Fn{}); + auto it1 = v.begin(); + + validator(buffer, *it1, 0); + + auto it2 = it1 + 3; + validator(buffer, *it2, 3); + + auto it3 = 3 + it1; + validator(buffer, *it3, 3); + + it1 += 3; + assert(it1 == it2); + validator(buffer, *it1, 3); + } + + { + // operator-(x, n) and operator-= + std::ranges::adjacent_transform_view v(rng, Fn{}); + auto it1 = v.end(); + + auto it2 = it1 - 3; + validator(buffer, *it2, 7 - N); + + it1 -= 3; + assert(it1 == it2); + validator(buffer, *it1, 7 - N); + } + + { + // operator-(x, y) + std::ranges::adjacent_transform_view v(rng, Fn{}); + assert((v.end() - v.begin()) == (10 - N)); + + auto it1 = v.begin() + 2; + auto it2 = v.end() - 1; + assert((it1 - it2) == static_cast(N) - 7); + } + + { + // empty range + std::ranges::adjacent_transform_view v(R{buffer, 0}, Fn{}); + assert((v.end() - v.begin()) == 0); + } + + { + // N > size of range + std::ranges::adjacent_transform_view v(R{buffer, 2}, Fn{}); + assert((v.end() - v.begin()) == 0); + } +} + +template +constexpr void test() { + test(); + test(); + + { + // Non random access but sized sentinel + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + using View = std::ranges::adjacent_transform_view; + using Iter = std::ranges::iterator_t; + using Diff = std::iter_difference_t; + + static_assert(!std::invocable, Iter, Diff>); + static_assert(!std::invocable, Diff, Iter>); + static_assert(!canPlusEqual); + static_assert(!std::invocable, Iter, Diff>); + static_assert(!canMinusEqual); + static_assert(std::invocable, Iter, Iter>); + + View v(ForwardSizedView{buffer}, Fn{}); + auto it1 = v.begin(); + auto it2 = v.end(); + assert((it2 - it1) == (10 - N)); + } + + { + // Non random access and non-sized sentinel + using View = std::ranges::adjacent_transform_view; + using Iter = std::ranges::iterator_t; + using Diff = std::iter_difference_t; + + static_assert(!std::invocable, Iter, Diff>); + static_assert(!std::invocable, Diff, Iter>); + static_assert(!canPlusEqual); + static_assert(!std::invocable, Iter, Diff>); + static_assert(!canMinusEqual); + static_assert(!std::invocable, Iter, Iter>); + } +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/compare.pass.cpp new file mode 100644 index 000000000000..9908f7a17462 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/compare.pass.cpp @@ -0,0 +1,162 @@ +//===----------------------------------------------------------------------===// +// +// 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) +// requires random_access_range; +// friend constexpr bool operator>(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator<=(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator>=(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr auto operator<=>(const iterator& x, const iterator& y) +// requires random_access_range && +// three_way_comparable>; + +#include +#include + +#include "test_iterators.h" +#include "test_range.h" + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +constexpr void compareOperatorTest(auto&& iter1, auto&& iter2) { + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); +} + +constexpr void inequalityOperatorsDoNotExistTest(auto&& iter1, auto&& iter2) { + using Iter1 = decltype(iter1); + using Iter2 = decltype(iter2); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); +} + +template +constexpr void test() { + { + // Test a new-school iterator with operator<=>; the iterator should also have operator<=>. + using It = three_way_contiguous_iterator; + using SubRange = std::ranges::subrange; + static_assert(std::three_way_comparable); + using R = std::ranges::adjacent_transform_view; + static_assert(std::three_way_comparable>); + + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + auto r = R{SubRange(It(buffer), It(buffer + 8)), Fn{}}; + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); + } + + { + // Test an old-school iterator with no operator<=>; the adjacent_transform iterator shouldn't have + // operator<=> either. + using It = random_access_iterator; + using SubRange = std::ranges::subrange; + static_assert(!std::three_way_comparable); + using R = std::ranges::adjacent_transform_view; + static_assert(!std::three_way_comparable>); + + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + auto r = R{SubRange(It(buffer), It(buffer + 8)), Fn{}}; + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + } + + { + // non random_access_range + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + std::ranges::adjacent_transform_view v(BidiCommonView{buffer}, Fn{}); + using View = decltype(v); + static_assert(!std::ranges::random_access_range); + + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 != it2); + + // advance it1 to the end + std::ranges::advance(it1, 9 - N); + assert(it1 == it2); + + inequalityOperatorsDoNotExistTest(it1, it2); + } + + { + // empty range + auto v = std::views::empty | std::views::adjacent_transform(Fn{}); + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 == it2); + } + + { + // N > size of range + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + auto v = std::ranges::adjacent_transform_view(ContiguousCommonView{buffer}, Fn{}); + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 == it2); + } +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/ctor.default.pass.cpp new file mode 100644 index 000000000000..a568921511eb --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/ctor.default.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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() = default; + +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +struct IterDefaultCtrView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +template +constexpr void test() { + using View = std::ranges::adjacent_transform_view; + using Iter = std::ranges::iterator_t; + { + Iter iter1; + Iter iter2 = {}; + assert(iter1 == iter2); + } +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/ctor.other.pass.cpp new file mode 100644 index 000000000000..36c2456e459d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/ctor.other.pass.cpp @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr iterator(iterator i) +// requires Const && convertible_to, inner-iterator>; + +#include + +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +using ConstIterIncompatibleView = + BasicView, + forward_iterator, + random_access_iterator, + random_access_iterator>; +static_assert(!std::convertible_to, + std::ranges::iterator_t>); + +template +constexpr void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + Validator validator{}; + { + std::ranges::adjacent_transform_view v(NonSimpleCommon{buffer}, Fn{}); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + + static_assert(!std::is_same_v); + // We cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from); + + assert(iter1 == iter2); + + validator(buffer, *iter1, 0); + validator(buffer, *iter2, 0); + } + + { + // underlying non-const to const not convertible + std::ranges::adjacent_transform_view v(ConstIterIncompatibleView{buffer}, Tie{}); + auto iter1 = v.begin(); + auto iter2 = std::as_const(v).begin(); + + static_assert(!std::is_same_v); + + static_assert(!std::constructible_from); + static_assert(!std::constructible_from); + } +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/decrement.pass.cpp new file mode 100644 index 000000000000..25c2fbbc3a53 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/decrement.pass.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// 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--() requires bidirectional_range; +// constexpr iterator operator--(int) requires bidirectional_range; + +#include +#include +#include +#include +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +template +concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; }; + +template +constexpr void test_one() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + Validator validator{}; + + auto v = R(buffer) | std::views::adjacent_transform(Fn{}); + + auto it = v.begin(); + using Iter = decltype(it); + + std::ranges::advance(it, v.end()); + + --it; + validator(buffer, *it, 9 - N); + + static_assert(std::is_same_v); + std::same_as decltype(auto) it_ref = --it; + assert(&it_ref == &it); + + validator(buffer, *it, 8 - N); + + std::same_as decltype(auto) tmp = it--; + + validator(buffer, *tmp, 8 - N); + validator(buffer, *it, 7 - N); + + // Decrement to the beginning + for (int i = 6 - N; i >= 0; --i) { + --it; + validator(buffer, *it, i); + } + assert(it == v.begin()); +} + +template +constexpr void test() { + test_one(); + test_one(); + test_one(); + + // Non-bidirectional base range + { + using View = std::ranges::adjacent_transform_view; + using Iter = std::ranges::iterator_t; + + static_assert(!canDecrement); + } +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/deref.pass.cpp new file mode 100644 index 000000000000..b7a1507a4203 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/deref.pass.cpp @@ -0,0 +1,143 @@ +//===----------------------------------------------------------------------===// +// +// 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 noexcept(see below); + +#include +#include +#include +#include +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +template +concept DerefNoexcept = requires(const Iter iter) { + { *iter } noexcept; +}; + +static_assert(DerefNoexcept); + +struct NoExceptInvocable { + int operator()(auto...) const noexcept; +}; + +struct ThrowingInvocable { + int operator()(auto...) const; +}; + +template +constexpr void test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + // Remarks: Let Is be the pack 0, 1, ..., (N - 1). The exception specification is equivalent to: + // noexcept(invoke(*parent_->fun_, *std::get(inner_.current_)...)) + { + using View = std::ranges::adjacent_transform_view; + static_assert(DerefNoexcept>); + } + { + using View = std::ranges::adjacent_transform_view; + static_assert(!DerefNoexcept>); + } + { + // underlying iter deref not noexcept + static_assert(!DerefNoexcept>); + using View = std::ranges::adjacent_transform_view; + static_assert(!DerefNoexcept>); + } + + { + // make_tuple + auto v = buffer | std::views::adjacent_transform(MakeTuple{}); + std::same_as> decltype(auto) res = *v.begin(); + ValidateTupleFromIndex{}(buffer, res, 0); + } + + { + // tie + auto v = buffer | std::views::adjacent_transform(Tie{}); + std::same_as> decltype(auto) res = *v.begin(); + ValidateTieFromIndex{}(buffer, res, 0); + } + + { + // get<0> + auto v = buffer | std::views::adjacent_transform(GetFirst{}); + std::same_as decltype(auto) res = *v.begin(); + assert(&res == &buffer[0]); + } + + { + // Multiply + auto v = buffer | std::views::adjacent_transform(Multiply{}); + std::same_as decltype(auto) res = *v.begin(); + auto expected = std::accumulate(buffer, buffer + N, 1, std::multiplies<>()); + assert(res == expected); + } + + { + // operator* is const + auto v = buffer | std::views::adjacent_transform(GetFirst{}); + const auto it = v.begin(); + std::same_as decltype(auto) res = *it; + assert(&res == &buffer[0]); + } + + { + // underlying range with prvalue range_reference_t + auto v = std::views::iota(0, 8) | std::views::adjacent_transform(MakeTuple{}); + std::same_as> decltype(auto) res = *v.begin(); + assert(std::get<0>(res) == 0); + if constexpr (N >= 2) + assert(std::get<1>(res) == 1); + if constexpr (N >= 3) + assert(std::get<2>(res) == 2); + if constexpr (N >= 4) + assert(std::get<3>(res) == 3); + if constexpr (N >= 5) + assert(std::get<4>(res) == 4); + } + + { + // const-correctness + const std::array bufferConst = {1, 2, 3, 4, 5, 6, 7, 8}; + auto v = bufferConst | std::views::adjacent_transform(Tie{}); + std::same_as> decltype(auto) res = *v.begin(); + assert(&std::get<0>(res) == &bufferConst[0]); + if constexpr (N >= 2) + assert(&std::get<1>(res) == &bufferConst[1]); + if constexpr (N >= 3) + assert(&std::get<2>(res) == &bufferConst[2]); + if constexpr (N >= 4) + assert(&std::get<3>(res) == &bufferConst[3]); + if constexpr (N >= 5) + assert(&std::get<4>(res) == &bufferConst[4]); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/increment.pass.cpp new file mode 100644 index 000000000000..df9e0e3a9995 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/increment.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr iterator& operator++(); +// constexpr iterator operator++(int); + +#include +#include +#include +#include +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +template +constexpr void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + Validator validator{}; + + auto v = R(buffer) | std::views::adjacent_transform(Fn{}); + auto it = v.begin(); + using Iter = decltype(it); + + validator(buffer, *it, 0); + + std::same_as decltype(auto) it_ref = ++it; + assert(&it_ref == &it); + + validator(buffer, *it, 1); + + static_assert(std::is_same_v); + auto original = it; + std::same_as decltype(auto) copy = it++; + assert(original == copy); + + validator(buffer, *copy, 1); + validator(buffer, *it, 2); + + // Increment to the end + for (std::size_t i = 3; i != 9 - N; ++i) { + ++it; + validator(buffer, *it, i); + } + + ++it; + assert(it == v.end()); +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp new file mode 100644 index 000000000000..f8f20919fbf4 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// +// 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 traits and member typedefs in adjacent_transform_view::iterator. + +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +template +struct DiffTypeIter : random_access_iterator { + using value_type = int; + using difference_type = T; + + int& operator*() const; + DiffTypeIter& operator++(); + DiffTypeIter operator++(int); + friend constexpr bool operator==(DiffTypeIter, DiffTypeIter) = default; +}; + +template +struct DiffTypeView : std::ranges::view_base { + DiffTypeIter begin() const; + DiffTypeIter end() const; +}; + +struct Foo {}; + +struct ConstVeryDifferentRange { + int* begin(); + int* end(); + + forward_iterator begin() const; + forward_iterator end() const; +}; + +template +void test() { + int buffer[] = {1, 2, 3, 4}; + + { + // Base contiguous range + std::ranges::adjacent_transform_view, Fn, N> v(buffer, Fn{}); + using Iter = decltype(v.begin()); + using Cat = std::conditional_t; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + { + // Base random access + std::ranges::adjacent_transform_view v(SizedRandomAccessView{buffer}, Fn{}); + using Iter = decltype(v.begin()); + using Cat = std::conditional_t; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + { + // Base bidirectional + std::ranges::adjacent_transform_view v(BidiCommonView{buffer}, Fn{}); + using Iter = decltype(v.begin()); + using Cat = std::conditional_t; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + { + // Base forward + std::ranges::adjacent_transform_view v(ForwardSizedView{buffer}, Fn{}); + using Iter = decltype(v.begin()); + using Cat = std::conditional_t; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + { + // difference_type + std::ranges::adjacent_transform_view, Fn, N> v{DiffTypeView{}, Fn{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v); + } + + { + // const-iterator different from iterator + auto v = ConstVeryDifferentRange{} | std::views::adjacent_transform<2>(MakeTuple{}); + using Iter = decltype(v.begin()); + using ConstIter = decltype(std::as_const(v).begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + } +} + +void test() { + test, true>(); + test, true>(); + test, true>(); + + test, true>(); + test, true>(); + test, true>(); + + test(); + test(); + test(); + + test(); + test(); + test(); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/singular.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/singular.pass.cpp new file mode 100644 index 000000000000..f51058ccc488 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/singular.pass.cpp @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: no-exceptions + +// If the invocation of any non-const member function of `iterator` exits via an +// exception, the iterator acquires a singular value. + +#include + +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +struct ThrowOnDecrementIterator { + int* it_; + + using value_type = int; + using difference_type = std::intptr_t; + + ThrowOnDecrementIterator() = default; + explicit ThrowOnDecrementIterator(int* it) : it_(it) {} + + ThrowOnDecrementIterator& operator++() { + ++it_; + return *this; + } + ThrowOnDecrementIterator operator++(int) { + auto tmp = *this; + ++it_; + return tmp; + } + + ThrowOnDecrementIterator& operator--() { throw 5; } + ThrowOnDecrementIterator operator--(int) { throw 5; } + + int& operator*() const { return *it_; } + + friend bool operator==(ThrowOnDecrementIterator const&, ThrowOnDecrementIterator const&) = default; +}; + +struct ThrowOnIncrementView : IntBufferView { + ThrowOnDecrementIterator begin() const { return ThrowOnDecrementIterator{buffer_}; } + ThrowOnDecrementIterator end() const { return ThrowOnDecrementIterator{buffer_ + size_}; } +}; + +template +void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + Validator validator{}; + { + // adjacent_transform_view iterator should be able to be destroyed after member function throws + auto v = ThrowOnIncrementView{buffer} | std::views::adjacent_transform(Fn{}); + auto it = v.begin(); + ++it; + try { + --it; + assert(false); // should not be reached as the above expression should throw. + } catch (int e) { + assert(e == 5); + } + } + + { + // adjacent_transform_view iterator should be able to be assigned after member function throws + auto v = ThrowOnIncrementView{buffer} | std::views::adjacent_transform(Fn{}); + auto it = v.begin(); + ++it; + try { + --it; + assert(false); // should not be reached as the above expression should throw. + } catch (int e) { + assert(e == 5); + } + it = v.begin(); + validator(buffer, *it, 0); + } +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); +} + +void test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/subscript.pass.cpp new file mode 100644 index 000000000000..976bae0c7123 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/subscript.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr decltype(auto) operator[](difference_type n) const requires +// all_random_access + +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +template +constexpr void test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + // random_access_range + std::ranges::adjacent_transform_view v(SizedRandomAccessView{buffer}, Fn{}); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + + static_assert(std::is_same_v); + } + + { + // contiguous_range + std::ranges::adjacent_transform_view v(ContiguousCommonView{buffer}, Fn{}); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + + static_assert(std::is_same_v); + } + + { + // non random_access_range + std::ranges::adjacent_transform_view v(BidiCommonView{buffer}, Fn{}); + auto iter = v.begin(); + const auto canSubscript = [](auto&& it) { return requires { it[0]; }; }; + static_assert(!canSubscript(iter)); + } +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/range.concept.compile.pass.cpp new file mode 100644 index 000000000000..a02951a6c2e3 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/range.concept.compile.pass.cpp @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// 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 if adjacent_transform_view models input_range, forward_range, bidirectional_range, +// random_access_range, contiguous_range, common_range, sized_range + +#include +#include +#include +#include +#include + +#include "helpers.h" +#include "../range_adaptor_types.h" + +template +constexpr void testConcept() { + int buffer[3] = {1, 2, 3}; + { + std::ranges::adjacent_transform_view v(ContiguousCommonView{buffer}, Fn{}); + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::adjacent_transform_view v{ContiguousNonCommonView{buffer}, Fn{}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::adjacent_transform_view v{ContiguousNonCommonSized{buffer}, Fn{}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::adjacent_transform_view v{SizedRandomAccessView{buffer}, Fn{}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::adjacent_transform_view v{NonSizedRandomAccessView{buffer}, Fn{}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::adjacent_transform_view v{BidiCommonView{buffer}, Fn{}}; + using View = decltype(v); + static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::random_access_range); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::adjacent_transform_view v{BidiNonCommonView{buffer}, Fn{}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::adjacent_transform_view v{ForwardSizedView{buffer}, Fn{}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::adjacent_transform_view v{ForwardSizedNonCommon{buffer}, Fn{}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::sized_range); + } +} + +template +constexpr bool testConcept() { + testConcept(); + testConcept(); + testConcept(); + testConcept(); + + return true; +} + +static_assert(testConcept<1>()); +static_assert(testConcept<2>()); +static_assert(testConcept<3>()); +static_assert(testConcept<5>()); + +using OutputIter = cpp17_output_iterator; +static_assert(std::output_iterator); + +struct OutputView : std::ranges::view_base { + OutputIter begin() const; + sentinel_wrapper end() const; +}; +static_assert(std::ranges::output_range); +static_assert(!std::ranges::input_range); + +template +concept adjacent_transform_viewable = requires { typename std::ranges::adjacent_transform_view; }; + +static_assert(adjacent_transform_viewable); +static_assert(adjacent_transform_viewable); +static_assert(adjacent_transform_viewable); + +// output_range is not supported +static_assert(!adjacent_transform_viewable); + +// input only range is not supported +static_assert(!adjacent_transform_viewable); +static_assert(!adjacent_transform_viewable); + +// Fn is not callble with the correct types +struct FnNotCallable { + void operator()() const {} +}; +static_assert(!adjacent_transform_viewable); + +// function that returns void is not supported +struct FnReturnsVoid { + template + void operator()(Args&&...) const {} +}; +static_assert(!adjacent_transform_viewable); diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/ctor.default.pass.cpp new file mode 100644 index 000000000000..7eef18308499 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/ctor.default.pass.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// sentinel() = default; + +#include +#include +#include + +#include "../helpers.h" + +struct PODSentinel { + bool b; // deliberately uninitialised + + friend constexpr bool operator==(int*, const PODSentinel& s) { return s.b; } +}; + +struct Range : std::ranges::view_base { + int* begin() const; + PODSentinel end(); +}; + +template +constexpr void test() { + { + using R = std::ranges::adjacent_transform_view; + using Sentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v>); + + std::ranges::iterator_t it; + + Sentinel s1; + assert(it != s1); // PODSentinel.b is initialised to false + + Sentinel s2 = {}; + assert(it != s2); // PODSentinel.b is initialised to false + } +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/ctor.other.pass.cpp new file mode 100644 index 000000000000..d06534ea52d5 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/ctor.other.pass.cpp @@ -0,0 +1,125 @@ +//===----------------------------------------------------------------------===// +// +// 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 sentinel(sentinel s); +// requires Const && convertible_to, inner-sentinel>; + +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +template +struct convertible_sentinel_wrapper { + explicit convertible_sentinel_wrapper() = default; + constexpr convertible_sentinel_wrapper(const T& it) : it_(it) {} + + template + requires std::convertible_to + constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper& other) : it_(other.it_) {} + + constexpr friend bool operator==(convertible_sentinel_wrapper const& self, const T& other) { + return self.it_ == other; + } + T it_; +}; + +struct SentinelConvertibleView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr int* begin() { return buffer_; } + constexpr const int* begin() const { return buffer_; } + constexpr convertible_sentinel_wrapper end() { return convertible_sentinel_wrapper(buffer_ + size_); } + constexpr convertible_sentinel_wrapper end() const { + return convertible_sentinel_wrapper(buffer_ + size_); + } +}; + +static_assert(!std::ranges::common_range); +static_assert(std::convertible_to, + std::ranges::sentinel_t>); +static_assert(!simple_view); + +struct SentinelNonConvertibleView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr int* begin() { return buffer_; } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper end() { return sentinel_wrapper(buffer_ + size_); } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(buffer_ + size_); } +}; + +static_assert(!std::ranges::common_range); +static_assert(!std::convertible_to, + std::ranges::sentinel_t>); +static_assert(!simple_view); + +template +constexpr void test() { + using View = std::ranges::adjacent_transform_view; + static_assert(!std::ranges::common_range); + + using Sent = std::ranges::sentinel_t; + using ConstSent = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + { + // implicitly convertible + static_assert(std::convertible_to); + } + { + // !Const + static_assert(!std::convertible_to); + } + { + // !convertible_to, iterator_t> + using V2 = std::ranges::adjacent_transform_view; + static_assert(!std::convertible_to, std::ranges::sentinel_t>); + } + + { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + View v{SentinelConvertibleView{buffer}, Fn{}}; + Sent sent1 = v.end(); + ConstSent sent2 = sent1; + + assert(v.begin() != sent2); + assert(std::as_const(v).begin() != sent2); + assert(v.begin() + (10 - N) == sent2); + assert(std::as_const(v).begin() + (10 - N) == sent2); + } +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/eq.pass.cpp new file mode 100644 index 000000000000..d7510a099c80 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/eq.pass.cpp @@ -0,0 +1,206 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// template +// requires sentinel_for, inner-iterator> +// friend constexpr bool operator==(const iterator& x, const sentinel& y); + +#include +#include +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" +#include "test_iterators.h" +#include "test_range.h" + +using Iterator = random_access_iterator; +using ConstIterator = contiguous_iterator; + +template +struct ComparableSentinel { + using Iter = std::conditional_t; + Iter iter_; + + explicit ComparableSentinel() = default; + constexpr explicit ComparableSentinel(const Iter& it) : iter_(it) {} + + constexpr friend bool operator==(const Iterator& i, const ComparableSentinel& s) { return base(i) == base(s.iter_); } + + constexpr friend bool operator==(const ConstIterator& i, const ComparableSentinel& s) { + return base(i) == base(s.iter_); + } +}; + +struct ComparableView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr auto begin() { return Iterator(buffer_); } + constexpr auto begin() const { return ConstIterator(buffer_); } + constexpr auto end() { return ComparableSentinel(Iterator(buffer_ + size_)); } + constexpr auto end() const { return ComparableSentinel(ConstIterator(buffer_ + size_)); } +}; + +struct ConstIncompatibleView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr random_access_iterator begin() { return random_access_iterator(buffer_); } + constexpr contiguous_iterator begin() const { return contiguous_iterator(buffer_); } + constexpr sentinel_wrapper> end() { + return sentinel_wrapper>(random_access_iterator(buffer_ + size_)); + } + constexpr sentinel_wrapper> end() const { + return sentinel_wrapper>(contiguous_iterator(buffer_ + size_)); + } +}; + +template +constexpr bool test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + { + // underlying simple-view: const and non-const have the same iterator/sentinel type + using View = std::ranges::adjacent_transform_view; + static_assert(!std::ranges::common_range); + + // but adjacent_transform_view is never a simple-view + static_assert(!simple_view); + + View v{SimpleNonCommon(buffer), Fn{}}; + + assert(v.begin() != v.end()); + assert(v.begin() + 1 != v.end()); + assert(v.begin() + 2 != v.end()); + assert(v.begin() + 3 != v.end()); + assert(v.begin() + (10 - N) == v.end()); + } + + { + // !simple-view: const and non-const have different iterator/sentinel types + using View = std::ranges::adjacent_transform_view; + static_assert(!std::ranges::common_range); + static_assert(!simple_view); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(weakly_equality_comparable_with); + static_assert(!weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + + View v{NonSimpleNonCommon(buffer), Fn{}}; + + assert(v.begin() != v.end()); + assert(v.begin() + (10 - N) == v.end()); + + assert(v.begin() != std::as_const(v).end()); + assert(v.begin() + (10 - N) == std::as_const(v).end()); + // the above works because + static_assert(std::convertible_to); + + assert(std::as_const(v).begin() != std::as_const(v).end()); + assert(std::as_const(v).begin() + (10 - N) == std::as_const(v).end()); + } + + { + // underlying const/non-const sentinel can be compared with both const/non-const iterator + using View = std::ranges::adjacent_transform_view; + static_assert(!std::ranges::common_range); + static_assert(!simple_view); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + + View v{ComparableView(buffer), Fn{}}; + + assert(v.begin() != v.end()); + assert(v.begin() + (10 - N) == v.end()); + + static_assert(!std::convertible_to); + + assert(v.begin() != std::as_const(v).end()); + assert(v.begin() + (10 - N) == std::as_const(v).end()); + + assert(std::as_const(v).begin() != v.end()); + assert(std::as_const(v).begin() + (10 - N) == v.end()); + + assert(std::as_const(v).begin() != std::as_const(v).end()); + assert(std::as_const(v).begin() + (10 - N) == std::as_const(v).end()); + } + + { + // underlying const/non-const sentinel cannot be compared with non-const/const iterator + + using View = std::ranges::adjacent_transform_view; + static_assert(!std::ranges::common_range); + static_assert(!simple_view); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(weakly_equality_comparable_with); + static_assert(!weakly_equality_comparable_with); + static_assert(!weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + + View v{ConstIncompatibleView{buffer}, Fn{}}; + + assert(v.begin() != v.end()); + assert(v.begin() + (10 - N) == v.end()); + + assert(std::as_const(v).begin() != std::as_const(v).end()); + assert(std::as_const(v).begin() + (10 - N) == std::as_const(v).end()); + } + + return true; +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/minus.pass.cpp new file mode 100644 index 000000000000..bed863a65d47 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/sentinel/minus.pass.cpp @@ -0,0 +1,195 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// template +// requires sized_sentinel_for, inner-iterator> +// friend constexpr range_difference_t> +// operator-(const iterator& x, const sentinel& y); + +// template +// requires sized_sentinel_for, inner-iterator> +// friend constexpr range_difference_t> +// operator-(const sentinel& x, const iterator& y); + +#include +#include +#include +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +// clang-format off +template +concept HasMinus = std::invocable,const T&, const U&>; + +template +concept SentinelHasMinus = HasMinus, std::ranges::iterator_t>; +// clang-format on + +template +constexpr void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + { + // underlying simple-view + using View = std::ranges::adjacent_transform_view; + static_assert(!std::ranges::common_range); + // but adjacent_transform_view is never a simple-view + static_assert(!simple_view); + + View v{ForwardSizedNonCommon(buffer), Fn{}}; + + auto it = v.begin(); + auto st = v.end(); + assert(st - it == (10 - N)); + assert(st - std::ranges::next(it, 1) == (9 - N)); + + assert(it - st == (static_cast(N) - 10)); + assert(std::ranges::next(it, 1) - st == (static_cast(N) - 9)); + static_assert(SentinelHasMinus); + } + + { + // empty range + using View = std::ranges::adjacent_transform_view; + View v{ForwardSizedNonCommon(buffer, 0), Fn{}}; + + auto it = v.begin(); + auto st = v.end(); + assert(st - it == 0); + assert(it - st == 0); + } + + { + // N > size of underlying range + using View = std::ranges::adjacent_transform_view; + View v{ForwardSizedNonCommon(buffer, 3), Fn{}}; + + auto it = v.begin(); + auto st = v.end(); + assert(st - it == 0); + assert(it - st == 0); + } + + { + // underlying sentinel does not model sized_sentinel_for + using View = std::ranges::adjacent_transform_view; + static_assert(!std::ranges::common_range); + static_assert(!SentinelHasMinus); + } + + { + // const incompatible: + // underlying const sentinels cannot subtract underlying iterators + // underlying sentinels cannot subtract underlying const iterators + using View = std::ranges::adjacent_transform_view; + static_assert(!std::ranges::common_range); + static_assert(!simple_view); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + + View v{NonSimpleForwardSizedNonCommon{buffer}, Fn{}}; + + auto it = v.begin(); + auto const_it = std::as_const(v).begin(); + auto st = v.end(); + auto const_st = std::as_const(v).end(); + + int n = N; + + assert(it - st == (n - 10)); + assert(st - it == (10 - n)); + assert(const_it - const_st == (n - 10)); + assert(const_st - const_it == (10 - n)); + + static_assert(!HasMinus); + static_assert(!HasMinus); + static_assert(!HasMinus); + static_assert(!HasMinus); + } + + { + // const compatible allow non-const to const conversion + using View = std::ranges::adjacent_transform_view; + static_assert(!std::ranges::common_range); + static_assert(!simple_view); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + + View v{ConstCompatibleForwardSized{buffer}, Fn{}}; + + auto it = v.begin(); + auto const_it = std::as_const(v).begin(); + auto st = v.end(); + auto const_st = std::as_const(v).end(); + + int n = N; + + assert(it - st == (n - 10)); + assert(st - it == (10 - n)); + assert(const_it - const_st == (n - 10)); + assert(const_st - const_it == (10 - n)); + assert(it - const_st == (n - 10)); + assert(const_st - it == (10 - n)); + assert(const_it - st == (n - 10)); + assert(st - const_it == (10 - n)); + } +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/size.pass.cpp new file mode 100644 index 000000000000..12cdf52d711b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/size.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto size() requires sized_range +// constexpr auto size() const requires sized_range + +#include + +#include "test_macros.h" +#include "helpers.h" +#include "../range_adaptor_types.h" + +int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; +struct View : std::ranges::view_base { + std::size_t size_ = 0; + constexpr View(std::size_t s) : size_(s) {} + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + size_; } +}; + +struct SizedNonConst : std::ranges::view_base { + using iterator = forward_iterator; + std::size_t size_ = 0; + constexpr SizedNonConst(std::size_t s) : size_(s) {} + constexpr auto begin() const { return iterator{buffer}; } + constexpr auto end() const { return iterator{buffer + size_}; } + constexpr std::size_t size() { return size_; } +}; + +struct StrangeSizeView : std::ranges::view_base { + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + 8; } + + constexpr auto size() { return 5; } + constexpr auto size() const { return 6; } +}; + +// Test with different values of N for a sized view +template +constexpr void test_sized_view() { + std::ranges::adjacent_transform_view v(View(8), Fn{}); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::sized_range); + + auto expected_size = 8 - (N - 1); + assert(v.size() == expected_size); + assert(std::as_const(v).size() == expected_size); +} + +// Test with different values of N for a non-const sized view +template +constexpr void test_nonconst_sized() { + std::ranges::adjacent_transform_view v(SizedNonConst(5), Fn{}); + static_assert(std::ranges::sized_range); + static_assert(!std::ranges::sized_range); + + auto expected_size = 5 - (N - 1); + assert(v.size() == expected_size); +} + +// Test with different values of N for a view with different const/non-const sizes +template +constexpr void test_strange_size() { + std::ranges::adjacent_transform_view v(StrangeSizeView{}, Fn{}); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::sized_range); + + assert(v.size() == 5 - (N - 1)); + assert(std::as_const(v).size() == 6 - (N - 1)); +} + +template +constexpr void test_empty_range() { + std::ranges::adjacent_transform_view v(View(0), Fn{}); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::sized_range); + + assert(v.size() == 0); + assert(std::as_const(v).size() == 0); +} + +template +constexpr void test_N_greater_than_size() { + if constexpr (N > 2) { + std::ranges::adjacent_transform_view v(View(2), Fn{}); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::sized_range); + assert(v.size() == 0); + assert(std::as_const(v).size() == 0); + } +} + +template +constexpr void test() { + test_sized_view(); + test_nonconst_sized(); + test_strange_size(); + test_empty_range(); + test_N_greater_than_size(); +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp b/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp index 43235e23a6a9..6387d0d57aa7 100644 --- a/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp +++ b/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp @@ -57,6 +57,7 @@ constexpr bool test() { #if TEST_STD_VER >= 23 testOne>(); + testOne>(); testOne>(); testOne>(); testOne>();