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 --- a/libcxx/include/__ranges/repeat_view.h +++ b/libcxx/include/__ranges/repeat_view.h @@ -28,22 +28,25 @@ # pragma GCC system_header #endif -#define _LIBCPP_STD_VER 23 - _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER > 17 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> { -private: - struct __iterator; + using __iterator = __repeat_view_iterator<_Tp, _Bound>; - __copyable_box<_Tp> __value_; - _Bound __bound_ = _Bound(); + template + requires __integer_like<_BoundType> || same_as<_BoundType, unreachable_sentinel_t> + friend class __repeat_view_iterator; public: _LIBCPP_HIDE_FROM_ABI repeat_view() @@ -71,7 +74,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() const { return __iterator(std::addressof(*__value_)); } _LIBCPP_HIDE_FROM_ABI constexpr __iterator end() const - requires(!same_as) + requires(!same_as<_Bound, unreachable_sentinel_t>) { return __iterator(std::addressof(*__value_), __bound_); } @@ -83,22 +86,22 @@ { 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 +template requires __integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t> -struct repeat_view<_Tp, Bound>::__iterator { -private: +class __repeat_view_iterator { using _IndexT = conditional_t, ptrdiff_t, _Bound >; - const _Tp* __value_ = nullptr; - _IndexT __current_ = _IndexT(); - - constexpr explicit __iterator(const _Tp* __value, _IndexT __bound = _IndexT()) + 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"); @@ -110,94 +113,105 @@ using value_type = _Tp; using difference_type = conditional_t<__signed_integer_like<_IndexT>, _IndexT, _IotaDiffT<_IndexT> >; - __iterator() = default; + _LIBCPP_HIDE_FROM_ABI __repeat_view_iterator() = default; - constexpr const _Tp& operator*() const noexcept { return *__value_; } + _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const noexcept { return *__value_; } - constexpr __iterator& operator++() { + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator& operator++() { ++__current_; return *this; } - constexpr __iterator operator++(int) { + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator operator++(int) { auto tmp = *this; ++*this; return tmp; } - constexpr __iterator& operator--() { + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator& operator--() { --__current_; return *this; } - constexpr __iterator operator--(int) { + _LIBCPP_HIDE_FROM_ABI constexpr __repeat_view_iterator operator--(int) { auto tmp = *this; --*this; return tmp; } - constexpr __iterator& operator+=(difference_type __n) { + _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; } - constexpr __iterator& operator-=(difference_type __n) { + _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; + __current_ -= __n; return *this; } - constexpr const _Tp& operator[](difference_type __n) const noexcept { return *(*this + __n); } + _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](difference_type __n) const noexcept { return *(*this + __n); } - friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __repeat_view_iterator& __x, const __repeat_view_iterator& __y) { return __x.__current_ == __y.__current_; } - friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) { + _LIBCPP_HIDE_FROM_ABI friend constexpr auto + operator<=>(const __repeat_view_iterator& __x, const __repeat_view_iterator& __y) { return __x.__current_ <=> __y.__current_; } - friend constexpr __iterator operator+(__iterator __i, difference_type __n) { + _LIBCPP_HIDE_FROM_ABI friend constexpr __repeat_view_iterator + operator+(__repeat_view_iterator __i, difference_type __n) { __i += __n; return __i; } - friend constexpr __iterator operator+(difference_type __n, __iterator __i) { + _LIBCPP_HIDE_FROM_ABI friend constexpr __repeat_view_iterator + operator+(difference_type __n, __repeat_view_iterator __i) { __i += __n; return __i; } - friend constexpr __iterator operator-(__iterator __i, difference_type __n) { + _LIBCPP_HIDE_FROM_ABI friend constexpr __repeat_view_iterator + operator-(__repeat_view_iterator __i, difference_type __n) { __i -= __n; return __i; } - friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y) { + _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 -inline constexpr bool __can_bound_repeat_view> = true; +concept __can_unbound_repeat_view = requires { repeat_view(std::declval<_Tp>()); }; + template -inline constexpr bool __can_unbound_repeat_view> = true; +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> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __value) const { + _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> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __value, _Bound&& __bound) const { + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __value, _Bound&& __bound) const { return repeat_view(std::forward<_Tp>(__value), __bound); } }; 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 @@ -337,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,