diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv --- a/libcxx/docs/Status/RangesViews.csv +++ b/libcxx/docs/Status/RangesViews.csv @@ -22,7 +22,7 @@ ,,,, ,,,, ,,,, -C++23,`repeat `_,Unassigned,No patch yet,Not started +C++23,`repeat `_,Yrong,`D141699 `_,✅ C++23,`cartesian_product `_,Unassigned,No patch yet,Not started C++23,`zip `_,Hui Xie,`D122806 `_,✅ C++23,`zip_transform `_,Hui Xie,No patch yet,Not started diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -518,6 +518,7 @@ __ranges/rbegin.h __ranges/ref_view.h __ranges/rend.h + __ranges/repeat_view.h __ranges/reverse_view.h __ranges/single_view.h __ranges/size.h diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h --- a/libcxx/include/__ranges/drop_view.h +++ b/libcxx/include/__ranges/drop_view.h @@ -30,6 +30,7 @@ #include <__ranges/iota_view.h> #include <__ranges/non_propagating_cache.h> #include <__ranges/range_adaptor.h> +#include <__ranges/repeat_view.h> #include <__ranges/size.h> #include <__ranges/subrange.h> #include <__ranges/view_interface.h> @@ -144,6 +145,12 @@ template inline constexpr bool __is_empty_view> = true; +template +inline constexpr bool __is_repeat_view = false; + +template +inline constexpr bool __is_repeat_view> = true; + template inline constexpr bool __is_passthrough_specialization = false; @@ -263,6 +270,42 @@ std::__to_unsigned_like(dist - clamped) );} + // [range.drop.overview]: the `repeat_view` "_RawRange models sized_range" case. + template > _Np, + class _RawRange = remove_cvref_t<_Range>, + class _Dist = range_difference_t<_Range>> + requires (!__is_empty_view<_RawRange> && + sized_range<_RawRange> && + __is_repeat_view<_RawRange>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Np&& __n) const noexcept( + noexcept(_RawRange(*ranges::begin(__range), + ranges::distance(__range) - std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n))))) + -> decltype(_RawRange(*ranges::begin(__range), + ranges::distance(__range) - + std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n)))) { + // Introducing local variables avoids calculating `min` and `distance` twice (at the cost of diverging from the + // expression used in the `noexcept` clause and the return statement). + auto dist = ranges::distance(__range); + auto clamped = std::min<_Dist>(dist, std::forward<_Np>(__n)); + return _RawRange(*ranges::begin(__range), + ranges::distance(__range) - std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n))); + } + + // [range.drop.overview]: the `repeat_view` "otherwise" case. + template > _Np, + class _RawRange = remove_cvref_t<_Range>, + class _Dist = range_difference_t<_Range>> + requires (!__is_empty_view<_RawRange> && + !sized_range<_RawRange> && + __is_repeat_view<_RawRange>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range, _Np&&) const + noexcept(noexcept(_LIBCPP_AUTO_CAST(std::forward<_Range>(__range)))) + -> decltype( _LIBCPP_AUTO_CAST(std::forward<_Range>(__range))) + { return _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)); } + // [range.drop.overview]: the "otherwise" case. template > _Np, class _RawRange = remove_cvref_t<_Range>> diff --git a/libcxx/include/__ranges/repeat_view.h b/libcxx/include/__ranges/repeat_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/repeat_view.h @@ -0,0 +1,231 @@ +// -*- 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_REPEAT_VIEW_H +#define _LIBCPP___RANGES_REPEAT_VIEW_H + +#include <__concepts/constructible.h> +#include <__concepts/semiregular.h> +#include <__config> +#include <__ranges/copyable_box.h> +#include <__ranges/iota_view.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/view_interface.h> +#include <__utility/forward.h> +#include <__utility/in_place.h> +#include <__utility/move.h> +#include <__utility/piecewise_construct.h> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +namespace ranges { + +template + requires __integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t> +class __repeat_view_iterator; + +template + requires(is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>> && + (__integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t>)) +class repeat_view : public view_interface> { + using __iterator = __repeat_view_iterator<_Tp, _Bound>; + + template + requires __integer_like<_BoundType> || same_as<_BoundType, unreachable_sentinel_t> + friend class __repeat_view_iterator; + +public: + _LIBCPP_HIDE_FROM_ABI repeat_view() + requires default_initializable<_Tp> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit repeat_view(const _Tp& __value, _Bound __bound = _Bound()) + : __value_(__value), __bound_(__bound) { + if constexpr (!same_as<_Bound, unreachable_sentinel_t>) + _LIBCPP_ASSERT(__bound_ >= 0, "The value of bound must greater equal than 0"); + } + + _LIBCPP_HIDE_FROM_ABI constexpr explicit repeat_view(_Tp&& __value, _Bound __bound = _Bound()) + : __value_(std::move(__value)), __bound_(__bound) { + if constexpr (!same_as<_Bound, unreachable_sentinel_t>) + _LIBCPP_ASSERT(__bound_ >= 0, "The value of bound must greater equal than 0"); + } + + template + requires constructible_from<_Tp, _TpArgs...> && constructible_from<_Bound, _BoundArgs...> + _LIBCPP_HIDE_FROM_ABI constexpr explicit repeat_view( + piecewise_construct_t, tuple<_TpArgs...> __value_args, tuple<_BoundArgs...> __bound_args = tuple<>{}) + : __value_(std::forward<_TpArgs>(__value_args)...), __bound_(std::forward<_BoundArgs>(__bound_args)...) {} + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() const { return __iterator(std::addressof(*__value_)); } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator end() const + requires(!same_as<_Bound, unreachable_sentinel_t>) + { + return __iterator(std::addressof(*__value_), __bound_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr unreachable_sentinel_t end() const noexcept { return unreachable_sentinel; } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires(!same_as<_Bound, unreachable_sentinel_t>) + { + return std::__to_unsigned_like(__bound_); + } + +private: + __copyable_box<_Tp> __value_ = _Tp(); + _LIBCPP_NO_UNIQUE_ADDRESS _Bound __bound_ = _Bound(); +}; + +template +repeat_view(_Tp, _Bound) -> repeat_view<_Tp, _Bound>; + +// [range.repeat.iterator] +template + requires __integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t> +class __repeat_view_iterator { + using _IndexT = conditional_t, ptrdiff_t, _Bound >; + + constexpr explicit __repeat_view_iterator(const _Tp* __value, _IndexT __bound = _IndexT()) + : __value_(__value), __current_(__bound) { + if constexpr (!same_as<_Bound, unreachable_sentinel_t>) + static_assert(__bound >= 0, "The value of bound must greater equal than 0"); + } + +public: + using iterator_concept = random_access_iterator_tag; + using iterator_category = random_access_iterator_tag; + using value_type = _Tp; + using difference_type = conditional_t<__signed_integer_like<_IndexT>, _IndexT, _IotaDiffT<_IndexT> >; + + _LIBCPP_HIDE_FROM_ABI __repeat_view_iterator() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const noexcept { return *__value_; } + + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator& operator++() { + ++__current_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator& operator--() { + --__current_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator operator--(int) { + auto tmp = *this; + --*this; + return tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator& operator+=(difference_type __n) { + if constexpr (!same_as<_Bound, unreachable_sentinel_t>) + static_assert(__current_ + __n >= 0, "The value of bound must greater equal than 0"); + __current_ += __n; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator& operator-=(difference_type __n) { + if constexpr (!same_as<_Bound, unreachable_sentinel_t>) + static_assert(__current_ - __n >= 0, "The value of bound must greater equal than 0"); + __current_ -= __n; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](difference_type __n) const noexcept { return *(*this + __n); } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __repeat_view_iterator& __x, const __repeat_view_iterator& __y) { + return __x.__current_ == __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto + operator<=>(const __repeat_view_iterator& __x, const __repeat_view_iterator& __y) { + return __x.__current_ <=> __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __repeat_view_iterator + operator+(__repeat_view_iterator __i, difference_type __n) { + __i += __n; + return __i; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __repeat_view_iterator + operator+(difference_type __n, __repeat_view_iterator __i) { + __i += __n; + return __i; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __repeat_view_iterator + operator-(__repeat_view_iterator __i, difference_type __n) { + __i -= __n; + return __i; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(const __repeat_view_iterator& __x, const __repeat_view_iterator& __y) { + return static_cast(__x.__current_) - static_cast(__y.__current_); + } + +private: + const _Tp* __value_ = nullptr; + _IndexT __current_ = _IndexT(); +}; + +namespace views { +namespace __repeat_view { + +template +concept __can_unbound_repeat_view = requires { repeat_view(std::declval<_Tp>()); }; + +template +concept __can_bound_repeat_view = requires { repeat_view(std::declval<_Tp>(), std::declval<_Bound>()); }; + +struct __fn : __range_adaptor_closure<__fn> { + template + requires __can_unbound_repeat_view<_Tp> + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __value) const { + return repeat_view(std::forward<_Tp>(__value)); + } + + template + requires __can_bound_repeat_view<_Tp, _Bound> + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __value, _Bound&& __bound) const { + return repeat_view(std::forward<_Tp>(__value), __bound); + } +}; +} // namespace __repeat_view + +inline namespace __cpo { +inline constexpr auto repeat = __repeat_view::__fn{}; +} // namespace __cpo + +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_REPEAT_VIEW_H diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h --- a/libcxx/include/__ranges/take_view.h +++ b/libcxx/include/__ranges/take_view.h @@ -31,6 +31,7 @@ #include <__ranges/enable_borrowed_range.h> #include <__ranges/iota_view.h> #include <__ranges/range_adaptor.h> +#include <__ranges/repeat_view.h> #include <__ranges/size.h> #include <__ranges/subrange.h> #include <__ranges/view_interface.h> @@ -216,6 +217,12 @@ template inline constexpr bool __is_iota_specialization> = true; +template +inline constexpr bool __is_repeat_specialization = false; + +template +inline constexpr bool __is_repeat_specialization> = true; + template struct __passthrough_type; @@ -298,6 +305,37 @@ *ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)) ); } + // [range.take.overview]: the `repeat_view` "_RawRange models sized_range" case. + template > _Np, + class _RawRange = remove_cvref_t<_Range>, + class _Dist = range_difference_t<_Range>> + requires(!__is_empty_view<_RawRange> && sized_range<_RawRange> && __is_repeat_specialization<_RawRange>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Np&& __n) const + noexcept(noexcept(ranges::repeat_view( + *ranges::begin(__range), + *ranges::begin(__range) + std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n))))) + -> decltype(ranges::repeat_view( + // Note: deliberately not forwarding `__range` to guard against double moves. + *ranges::begin(__range), + *ranges::begin(__range) + std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n)))) { + return ranges::repeat_view( + *ranges::begin(__range), + *ranges::begin(__range) + std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n))); + } + + // [range.take.overview]: the `repeat_view` "otherwise" case. + template > _Np, + class _RawRange = remove_cvref_t<_Range>, + class _Dist = range_difference_t<_Range>> + requires(!__is_empty_view<_RawRange> && !sized_range<_RawRange> && __is_repeat_specialization<_RawRange>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Np&&) const + noexcept(noexcept(_LIBCPP_AUTO_CAST(std::forward<_Range>(__range)))) + -> decltype(_LIBCPP_AUTO_CAST(std::forward<_Range>(__range))) { + return _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)); + } + // [range.take.overview]: the "otherwise" case. template > _Np, class _RawRange = remove_cvref_t<_Range>> diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1247,6 +1247,7 @@ module rbegin { private header "__ranges/rbegin.h" } module ref_view { private header "__ranges/ref_view.h" } module rend { private header "__ranges/rend.h" } + module repeat_view { private header "__ranges/repeat_view.h" } module reverse_view { private header "__ranges/reverse_view.h" } module single_view { private header "__ranges/single_view.h" } module size { private header "__ranges/size.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -271,6 +271,16 @@ (enable_borrowed_range && ...); namespace views { inline constexpr unspecified zip = unspecified; } // C++2b + + + // [range.repeat], repeat view + template + requires (is_object_v && same_as> + && (is-integer-like || same_as)) + class repeat_view; // C++2b + + namespace views { inline constexpr unspecified repeat = unspecified; } // C++2b + } namespace std { @@ -327,6 +337,7 @@ #include <__ranges/rbegin.h> #include <__ranges/ref_view.h> #include <__ranges/rend.h> +#include <__ranges/repeat_view.h> #include <__ranges/reverse_view.h> #include <__ranges/single_view.h> #include <__ranges/size.h> diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/begin.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/begin.pass.cpp @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator begin() const; + +#include +#include +#include + +#include "test_macro.h" +#include "types.h" + +constexpr void testType() { + { + std::ranges::repeat_view repeat(T{0}); + assert(*repeat.begin() = T{0}); + } + { + std::ranges::repeat_view repeat(T{10}); + assert(*repeat.begin() == T{10}); + assert(*std::move(repeat).begin() == T{10}); + } + { + const std::ranges::repeat_view repeat(T{0}); + assert(*repeat.begin() == T{0}); + } + { + const std::ranges::repeat_view repeat(T{10}); + assert(*repeat.begin() == T{10}); + } +} + +constexpr bool test() { + testType(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.default.pass.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// repeat_view() requires default_initializable = default; + +#include +#include +#include + +struct DefaultInt42 { + int value_ = 42; +}; + +constexpr bool test() { + std::ranges::repeat_view repeat(DefaultInt42{}); + assert((*repeat.begin()).value_ == 42); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/end.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/end.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto end() const; +// constexpr iterator end() const requires same_as; + +#include +#include +#include + +#include "test_macros.h" + +constexpr void testUnBoundTypes() { + { + std::ranges::repeat_view repeat(T{0}); + assert(repeat.begin() != repeat.end()); + assert(repeat.end() == std::unreachable_sentinel); + ASSERT_NOEXCEPT(repeat.end()); + ASSERT_SAME_TYPE(decltype(repeat.end()), std::unreachable_sentinel_t); + } + { + const std::ranges::repeat_view repeat(T{0}); + assert(repeat.begin() != repeat.end()); + assert(repeat.end() == std::unreachable_sentinel); + ASSERT_NOEXCEPT(repeat.end()); + ASSERT_SAME_TYPE(decltype(repeat.end()), std::unreachable_sentinel_t); + } + { + std::ranges::repeat_view repeat(T{0}); + assert(std::move(repeat).begin() != std::move(repeat).end()); + assert(std::move(repeat).end() == std::unreachable_sentinel); + ASSERT_NOEXCEPT(std::move(repeat).end()); + ASSERT_SAME_TYPE(decltype(std::move(repeat).end()), std::unreachable_sentinel_t); + } + { + const std::ranges::repeat_view repeat(T{0}); + assert(std::move(repeat).begin() != std::move(repeat).end()); + assert(std::move(repeat).end() == std::unreachable_sentinel); + ASSERT_NOEXCEPT(std::move(repeat).end()); + ASSERT_SAME_TYPE(decltype(std::move(repeat).end()), std::unreachable_sentinel_t); + } +} + +constexpr void testBoundTypes() { + { + std::ranges::repeat_view repeat(T{0}, 10); + assert(std::ranges::next(repeat.begin(), 10) == repeat.end()); + ASSERT_NOT_NOEXCEPT(repeat.end()); + ASSERT_SAME_TYPE(decltype(repeat.end()), std::ranges::iterator_t); + } + { + const std::ranges::repeat_view repeat(T{0}, 10); + assert(std::ranges::next(repeat.begin(), 10) == repeat.end()); + ASSERT_NOT_NOEXCEPT(repeat.end()); + ASSERT_SAME_TYPE(decltype(repeat.end()), std::ranges::iterator_t); + } + { + std::ranges::repeat_view repeat(T{0}, 10); + assert(std::ranges::next(std::move(repeat).begin(), 10) == std::move(repeat).end()); + ASSERT_NOT_NOEXCEPT(std::move(repeat).end()); + ASSERT_SAME_TYPE(decltype(std::move(repeat).end()), std::ranges::iterator_t); + } + { + const std::ranges::repeat_view repeat(T{0}, 10); + assert(std::ranges::next(std::move(repeat).begin(), 10) == std::move(repeat).end()); + ASSERT_NOT_NOEXCEPT(std::move(repeat).end()); + ASSERT_SAME_TYPE(decltype(std::move(repeat).end()), std::ranges::iterator_t); + } +} + +constexpr bool test() { + testBoundTypes(); + testUnBoundTypes(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -573,6 +573,11 @@ "unimplemented": True, }, { "name": "__cpp_lib_ranges_zip", + "values": { "c++2b": 202207 }, + "headers": ["ranges", "tuple", "utility"], + "unimplemented": True, + }, { + "name": "__cpp_lib_ranges_repeat", "values": { "c++2b": 202110 }, "headers": ["ranges", "tuple", "utility"], "unimplemented": True,