diff options
author | Hui <hui.xie0621@gmail.com> | 2023-01-18 23:26:34 +0000 |
---|---|---|
committer | Hui <hui.xie0621@gmail.com> | 2023-01-24 09:03:33 +0000 |
commit | a2b3ab8f7786b9bb6e1b8bbb01b88d4bbe28af69 (patch) | |
tree | 3b298efa0689e85f82a55c8bb5d7d5e4b756b37f /libcxx | |
parent | 2a832d0f09f73faf46aae54ff73cdcd99a7bacf3 (diff) | |
download | llvm-a2b3ab8f7786b9bb6e1b8bbb01b88d4bbe28af69.zip llvm-a2b3ab8f7786b9bb6e1b8bbb01b88d4bbe28af69.tar.gz llvm-a2b3ab8f7786b9bb6e1b8bbb01b88d4bbe28af69.tar.bz2 |
[libc++][ranges] implement `std::ranges::split_view`
- implement `std::ranges::split_view` (last c++20 view)
- Work in process on testing iterator/sentinel, but since we are
getting closer to the deadline, I'd like to send the review early
Differential Revision: https://reviews.llvm.org/D142063
Diffstat (limited to 'libcxx')
30 files changed, 2246 insertions, 3 deletions
diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst index 345dd53..c274f45 100644 --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -69,7 +69,8 @@ Implemented Papers - P1035R7 - Input Range Adaptors - P2325R3 - Views should not be required to be default constructible - P2446R2 - ``views::as_rvalue`` -- P1020 - Smart pointer creation with default initialization +- P1020R1 - Smart pointer creation with default initialization +- P2210R2 - Superior String Splitting Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv index 3999b01..6b01966 100644 --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -194,7 +194,7 @@ "","","","","","","" "`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Partial| [#note-P2231]_","13.0" "`P2325R3 <https://wg21.link/P2325R3>`__","LWG","Views should not be required to be default constructible","June 2021","|Complete|","16.0","|ranges|" -"`P2210R2 <https://wg21.link/P2210R2>`__","LWG","Superior String Splitting","June 2021","|In progress|","","|ranges|" +"`P2210R2 <https://wg21.link/P2210R2>`__","LWG","Superior String Splitting","June 2021","|Complete|","16.0","|ranges|" "`P2216R3 <https://wg21.link/P2216R3>`__","LWG","std::format improvements","June 2021","|Complete|","15.0" "`P2281R1 <https://wg21.link/P2281R1>`__","LWG","Clarifying range adaptor objects","June 2021","|Complete|","14.0","|ranges|" "`P2328R1 <https://wg21.link/P2328R1>`__","LWG","join_view should join all views of ranges","June 2021","|Complete|","15.0","|ranges|" diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv index 97a8afb..b2cc710 100644 --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -131,7 +131,7 @@ `3581 <https://wg21.link/LWG3581>`__,"The range constructor makes ``basic_string_view`` not trivially move constructible","October 2021","|Complete|","14.0","|ranges|" `3585 <https://wg21.link/LWG3585>`__,"``variant`` converting assignment with immovable alternative","October 2021","","" `3589 <https://wg21.link/LWG3589>`__,"The ``const`` lvalue reference overload of ``get`` for ``subrange`` does not constrain ``I`` to be ``copyable`` when ``N == 0``","October 2021","|Complete|","14.0","|ranges|" -`3590 <https://wg21.link/LWG3590>`__,"``split_view::base() const &`` is overconstrained","October 2021","","","|ranges|" +`3590 <https://wg21.link/LWG3590>`__,"``split_view::base() const &`` is overconstrained","October 2021","|Complete|","16.0","|ranges|" `3591 <https://wg21.link/LWG3591>`__,"``lazy_split_view<input_view>::inner-iterator::base() &&`` invalidates outer iterators","October 2021","","","|ranges|" `3592 <https://wg21.link/LWG3592>`__,"``lazy_split_view`` needs to check the simpleness of Pattern","October 2021","","","|ranges|" `3593 <https://wg21.link/LWG3593>`__,"Several iterators' ``base() const &`` and ``lazy_split_view::outer-iterator::value_type::end()`` missing ``noexcept``","October 2021","","","|ranges|" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 51bfc8c..a12aa1d 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -528,6 +528,7 @@ set(files __ranges/reverse_view.h __ranges/single_view.h __ranges/size.h + __ranges/split_view.h __ranges/subrange.h __ranges/take_view.h __ranges/take_while_view.h diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h new file mode 100644 index 0000000..9758ee9 --- /dev/null +++ b/libcxx/include/__ranges/split_view.h @@ -0,0 +1,232 @@ +// -*- 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_SPLIT_VIEW_H +#define _LIBCPP___RANGES_SPLIT_VIEW_H + +#include <__algorithm/ranges_search.h> +#include <__concepts/constructible.h> +#include <__config> +#include <__functional/bind_back.h> +#include <__functional/ranges_operations.h> +#include <__iterator/indirectly_comparable.h> +#include <__iterator/iterator_traits.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/empty.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/single_view.h> +#include <__ranges/subrange.h> +#include <__ranges/view_interface.h> +#include <__type_traits/decay.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__utility/forward.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +namespace ranges { + +template <class _View, class _Pattern> +struct __split_view_iterator; + +template <class _View, class _Pattern> +struct __split_view_sentinel; + +template <forward_range _View, forward_range _Pattern> + requires view<_View> && view<_Pattern> && + indirectly_comparable<iterator_t<_View>, iterator_t<_Pattern>, ranges::equal_to> +class split_view : public view_interface<split_view<_View, _Pattern>> { +private: + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + _LIBCPP_NO_UNIQUE_ADDRESS _Pattern __pattern_ = _Pattern(); + using _Cache = __non_propagating_cache<subrange<iterator_t<_View>>>; + _Cache __cached_begin_ = _Cache(); + + template <class, class> + friend struct __split_view_iterator; + + template <class, class> + friend struct __split_view_sentinel; + + using __iterator = __split_view_iterator<_View, _Pattern>; + using __sentinel = __split_view_sentinel<_View, _Pattern>; + + _LIBCPP_HIDE_FROM_ABI constexpr subrange<iterator_t<_View>> __find_next(iterator_t<_View> __it) { + auto [__begin, __end] = ranges::search(subrange(__it, ranges::end(__base_)), __pattern_); + if (__begin != ranges::end(__base_) && ranges::empty(__pattern_)) { + ++__begin; + ++__end; + } + return {__begin, __end}; + } + +public: + _LIBCPP_HIDE_FROM_ABI split_view() + requires default_initializable<_View> && default_initializable<_Pattern> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr split_view(_View __base, _Pattern __pattern) + : __base_(std::move(__base)), __pattern_(std::move((__pattern))) {} + + template <forward_range _Range> + requires constructible_from<_View, views::all_t<_Range>> && + constructible_from<_Pattern, single_view<range_value_t<_Range>>> + _LIBCPP_HIDE_FROM_ABI constexpr split_view(_Range&& __range, range_value_t<_Range> __elem) + : __base_(views::all(std::forward<_Range>(__range))), __pattern_(views::single(std::move(__elem))) {} + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() { + if (!__cached_begin_.__has_value()) { + __cached_begin_.__emplace(__find_next(ranges::begin(__base_))); + } + return {*this, ranges::begin(__base_), *__cached_begin_}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() { + if constexpr (common_range<_View>) { + return __iterator{*this, ranges::end(__base_), {}}; + } else { + return __sentinel{*this}; + } + } +}; + +template <class _Range, class _Pattern> +split_view(_Range&&, _Pattern&&) -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>; + +template <forward_range _Range> +split_view(_Range&&, range_value_t<_Range>) -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>; + +template <class _View, class _Pattern> +struct __split_view_iterator { +private: + split_view<_View, _Pattern>* __parent_ = nullptr; + _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __cur_ = iterator_t<_View>(); + _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>(); + bool __trailing_empty_ = false; + + template <class, class> + friend struct __split_view_sentinel; + +public: + using iterator_concept = forward_iterator_tag; + using iterator_category = input_iterator_tag; + using value_type = subrange<iterator_t<_View>>; + using difference_type = range_difference_t<_View>; + + _LIBCPP_HIDE_FROM_ABI __split_view_iterator() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __split_view_iterator( + split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next) + : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; } + + _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; } + + _LIBCPP_HIDE_FROM_ABI constexpr __split_view_iterator& operator++() { + __cur_ = __next_.begin(); + if (__cur_ != ranges::end(__parent_->__base_)) { + __cur_ = __next_.end(); + if (__cur_ == ranges::end(__parent_->__base_)) { + __trailing_empty_ = true; + __next_ = {__cur_, __cur_}; + } else { + __next_ = __parent_->__find_next(__cur_); + } + } else { + __trailing_empty_ = false; + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __split_view_iterator operator++(int) { + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __split_view_iterator& __x, const __split_view_iterator& __y) { + return __x.__cur_ == __y.__cur_ && __x.__trailing_empty_ == __y.__trailing_empty_; + } +}; + +template <class _View, class _Pattern> +struct __split_view_sentinel { +private: + _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>(); + + _LIBCPP_HIDE_FROM_ABI static constexpr bool + __equals(const __split_view_iterator<_View, _Pattern>& __x, const __split_view_sentinel& __y) { + return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_; + } + +public: + _LIBCPP_HIDE_FROM_ABI __split_view_sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __split_view_sentinel(split_view<_View, _Pattern>& __parent) + : __end_(ranges::end(__parent.__base_)) {} + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __split_view_iterator<_View, _Pattern>& __x, const __split_view_sentinel& __y) { + return __equals(__x, __y); + } +}; + +namespace views { +namespace __split_view { +struct __fn : __range_adaptor_closure<__fn> { + // clang-format off + template <class _Range, class _Pattern> + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range, _Pattern&& __pattern) const + noexcept(noexcept(split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)))) + -> decltype( split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))) + { return split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)); } + // clang-format on + + template <class _Pattern> + requires constructible_from<decay_t<_Pattern>, _Pattern> + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pattern&& __pattern) const + noexcept(is_nothrow_constructible_v<decay_t<_Pattern>, _Pattern>) { + return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pattern>(__pattern))); + } +}; +} // namespace __split_view + +inline namespace __cpo { +inline constexpr auto split = __split_view::__fn{}; +} // namespace __cpo +} // namespace views + +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_SPLIT_VIEW_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 5715775..1f1d67d 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1256,6 +1256,7 @@ module std [system] { 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" } + module split_view { private header "__ranges/split_view.h" } module subrange { private header "__ranges/subrange.h" diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 99c1687..f999fa0 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -265,8 +265,15 @@ namespace std::ranges { (forward_range<V> || tiny-range<Pattern>) class lazy_split_view; + // [range.split], split view + template<forward_range V, forward_range Pattern> + requires view<V> && view<Pattern> && + indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> + class split_view; + namespace views { inline constexpr unspecified lazy_split = unspecified; + inline constexpr unspecified split = unspecified; } // [range.istream], istream view @@ -360,6 +367,7 @@ namespace std { #include <__ranges/reverse_view.h> #include <__ranges/single_view.h> #include <__ranges/size.h> +#include <__ranges/split_view.h> #include <__ranges/subrange.h> #include <__ranges/take_view.h> #include <__ranges/take_while_view.h> diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp index 1498faa..ebbd50b 100644 --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -559,6 +559,7 @@ END-SCRIPT #include <__ranges/reverse_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/reverse_view.h'}} #include <__ranges/single_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/single_view.h'}} #include <__ranges/size.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/size.h'}} +#include <__ranges/split_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/split_view.h'}} #include <__ranges/subrange.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/subrange.h'}} #include <__ranges/take_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/take_view.h'}} #include <__ranges/take_while_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/take_while_view.h'}} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.split/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.split/no_unique_address.compile.pass.cpp new file mode 100644 index 0000000..c04dbd7 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.split/no_unique_address.compile.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// clang-cl and cl currently don't support [[no_unique_address]] +// XFAIL: msvc + +// class split_view { +// _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); +// _LIBCPP_NO_UNIQUE_ADDRESS _Pattern __pattern_ = _Pattern(); +// }; + +#include <ranges> + +#include "test_iterators.h" + +struct EmptyView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +using SplitView = std::ranges::split_view<EmptyView, EmptyView>; +static_assert(sizeof(SplitView) == sizeof(std::ranges::__non_propagating_cache<std::ranges::subrange<int*>>)); diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/adaptor.pass.cpp new file mode 100644 index 0000000..cd12011 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/adaptor.pass.cpp @@ -0,0 +1,126 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::split + +#include <ranges> + +#include <array> +#include <cassert> +#include <concepts> +#include <string_view> +#include <utility> + +#include "test_iterators.h" + +template <class View, class T> +concept CanBePiped = requires (View&& view, T&& t) { + { std::forward<View>(view) | std::forward<T>(t) }; +}; + +struct SomeView : std::ranges::view_base { + const std::string_view* v_; + constexpr SomeView(const std::string_view& v) : v_(&v) {} + constexpr auto begin() const { return v_->begin(); } + constexpr auto end() const { return v_->end(); } +}; + +struct NotAView { }; + +static_assert(!std::is_invocable_v<decltype(std::views::split)>); +static_assert(!std::is_invocable_v<decltype(std::views::split), SomeView, NotAView>); +static_assert(!std::is_invocable_v<decltype(std::views::split), NotAView, SomeView>); +static_assert( std::is_invocable_v<decltype(std::views::split), SomeView, SomeView>); + +static_assert( CanBePiped<SomeView&, decltype(std::views::split)>); +static_assert( CanBePiped<char(&)[10], decltype(std::views::split)>); +static_assert(!CanBePiped<char(&&)[10], decltype(std::views::split)>); +static_assert(!CanBePiped<NotAView, decltype(std::views::split)>); + +static_assert(std::same_as<decltype(std::views::split), decltype(std::ranges::views::split)>); + +constexpr bool test() { + std::string_view input = "abc"; + std::string_view sep = "a"; + + // Test that `std::views::split` is a range adaptor. + + // Test `views::split(input, sep)`. + { + SomeView view(input); + + using Result = std::ranges::split_view<SomeView, std::string_view>; + std::same_as<Result> decltype(auto) result = std::views::split(view, sep); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `views::split(sep)(input)`. + { + SomeView view(input); + + using Result = std::ranges::split_view<SomeView, std::string_view>; + std::same_as<Result> decltype(auto) result = std::views::split(sep)(view); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `view | views::split`. + { + SomeView view(input); + + using Result = std::ranges::split_view<SomeView, std::string_view>; + std::same_as<Result> decltype(auto) result = view | std::views::split(sep); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `adaptor | views::split`. + { + SomeView view(input); + auto f = [](char c) { return c; }; + auto partial = std::views::transform(f) | std::views::split(sep); + + using Result = std::ranges::split_view<std::ranges::transform_view<SomeView, decltype(f)>, std::string_view>; + std::same_as<Result> decltype(auto) result = partial(view); + assert(result.base().base().begin() == input.begin()); + assert(result.base().base().end() == input.end()); + } + + // Test `views::split | adaptor`. + { + SomeView view(input); + auto f = [](auto v) { return v; }; + auto partial = std::views::split(sep) | std::views::transform(f); + + using Result = std::ranges::transform_view<std::ranges::split_view<SomeView, std::string_view>, decltype(f)>; + std::same_as<Result> decltype(auto) result = partial(view); + assert(result.base().base().begin() == input.begin()); + assert(result.base().base().end() == input.end()); + } + + // Test that one can call `std::views::split` with arbitrary stuff, as long as we + // don't try to actually complete the call by passing it a range. + // + // That makes no sense and we can't do anything with the result, but it's valid. + { + struct X { }; + [[maybe_unused]] auto partial = std::views::split(X{}); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/base.pass.cpp new file mode 100644 index 0000000..56ac55c --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/base.pass.cpp @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr V base() const & requires copy_constructible<V> { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include <cassert> +#include <ranges> +#include <type_traits> +#include <utility> + +#include "MoveOnly.h" + +struct View : std::ranges::view_base { + int i; + int* begin() const; + int* end() const; +}; + +struct MoveOnlyView : View { + MoveOnly mo; +}; + +template <class T> +concept HasBase = requires(T&& t) { std::forward<T>(t).base(); }; + +static_assert(HasBase<std::ranges::split_view<View, View> const&>); +static_assert(HasBase<std::ranges::split_view<View, View>&&>); + +static_assert(!HasBase<std::ranges::split_view<MoveOnlyView, View> const&>); +static_assert(HasBase<std::ranges::split_view<MoveOnlyView, View>&&>); + +constexpr bool test() { + // const & + { + const std::ranges::split_view<View, View> sv{View{{}, 5}, View{{}, 0}}; + std::same_as<View> decltype(auto) v = sv.base(); + assert(v.i == 5); + } + + // & + { + std::ranges::split_view<View, View> sv{View{{}, 5}, View{{}, 0}}; + std::same_as<View> decltype(auto) v = sv.base(); + assert(v.i == 5); + } + + // && + { + std::ranges::split_view<View, View> sv{View{{}, 5}, View{{}, 0}}; + std::same_as<View> decltype(auto) v = std::move(sv).base(); + assert(v.i == 5); + } + + // const && + { + std::ranges::split_view<View, View> sv{View{{}, 5}, View{{}, 0}}; + std::same_as<View> decltype(auto) v = std::move(sv).base(); + assert(v.i == 5); + } + + // move only + { + std::ranges::split_view<MoveOnlyView, View> sv{MoveOnlyView{{}, 5}, View{{}, 0}}; + std::same_as<MoveOnlyView> decltype(auto) v = std::move(sv).base(); + assert(v.mo.get() == 5); + } + + // LWG 3590 split_view::base() const & is overconstrained + { + struct CopyCtorButNotAssignable : std::ranges::view_base { + int i; + constexpr CopyCtorButNotAssignable(int ii) : i(ii) {} + constexpr CopyCtorButNotAssignable(const CopyCtorButNotAssignable&) = default; + constexpr CopyCtorButNotAssignable(CopyCtorButNotAssignable&&) = default; + constexpr CopyCtorButNotAssignable& operator=(CopyCtorButNotAssignable&&) = default; + constexpr CopyCtorButNotAssignable& operator=(const CopyCtorButNotAssignable&) = delete; + constexpr int* begin() const { return nullptr; } + constexpr int* end() const { return nullptr; } + }; + static_assert(std::copy_constructible<CopyCtorButNotAssignable>); + static_assert(!std::copyable<CopyCtorButNotAssignable>); + const std::ranges::split_view<CopyCtorButNotAssignable, CopyCtorButNotAssignable> sv{ + CopyCtorButNotAssignable{5}, CopyCtorButNotAssignable{5}}; + std::same_as<CopyCtorButNotAssignable> decltype(auto) v = sv.base(); + assert(v.i == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/begin.pass.cpp new file mode 100644 index 0000000..15df4d0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/begin.pass.cpp @@ -0,0 +1,173 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto begin(); + +#include <array> +#include <cassert> +#include <ranges> +#include <type_traits> +#include <utility> + +#include "test_iterators.h" + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +// Test that begin is not const +template <class T> +concept HasBegin = requires(T t) { t.begin(); }; + +static_assert(HasBegin<std::ranges::split_view<View, View>>); +static_assert(!HasBegin<const std::ranges::split_view<View, View>>); + +template <template <class> class MakeIter> +constexpr void testOne() { + constexpr auto make_subrange = []<class T, std::size_t N>(T(&buffer)[N]) { + using Iter = MakeIter<T*>; + using Sent = sentinel_wrapper<Iter>; + return std::ranges::subrange<Iter, Sent>{Iter{buffer}, Sent{Iter{buffer + N}}}; + }; + + using Iter = MakeIter<int*>; + using Sent = sentinel_wrapper<Iter>; + using Range = std::ranges::subrange<Iter, Sent>; + + // empty view + { + std::array<int, 0> a; + Range range{Iter{a.data()}, Sent{Iter{a.data() + a.size()}}}; + std::ranges::split_view sv{std::move(range), 1}; + auto it = sv.begin(); + auto firstRange = *it; + assert(firstRange.begin() == range.begin()); + assert(firstRange.end() == range.end()); + } + + // empty pattern + { + int buffer[] = {1, 2, 3}; + auto range = make_subrange(buffer); + std::ranges::split_view sv{std::move(range), std::views::empty<int>}; + auto it = sv.begin(); + auto firstRange = *it; + assert(firstRange.begin() == range.begin()); + assert(firstRange.end() == std::next(range.begin())); + } + + // empty view and empty pattern + { + std::array<int, 0> a; + Range range{Iter{a.data()}, Sent{Iter{a.data() + a.size()}}}; + std::ranges::split_view sv{std::move(range), std::views::empty<int>}; + auto it = sv.begin(); + auto firstRange = *it; + assert(firstRange.begin() == range.begin()); + assert(firstRange.end() == range.end()); + } + + // pattern found at the beginning + { + int buffer[] = {1, 2, 3}; + auto range = make_subrange(buffer); + int pattern[] = {1, 2}; + std::ranges::split_view sv{range, pattern}; + + auto it = sv.begin(); + auto firstRange = *it; + assert(firstRange.begin() == range.begin()); + assert(firstRange.end() == range.begin()); + } + + // pattern found in the middle + { + int buffer[] = {1, 2, 3, 4}; + auto range = make_subrange(buffer); + int pattern[] = {2, 3}; + std::ranges::split_view sv{range, pattern}; + + auto it = sv.begin(); + auto firstRange = *it; + assert(firstRange.begin() == range.begin()); + assert(firstRange.end() == std::next(range.begin())); + } + + // pattern found at the end + { + int buffer[] = {1, 2, 3}; + auto range = make_subrange(buffer); + int pattern[] = {2, 3}; + std::ranges::split_view sv{range, pattern}; + + auto it = sv.begin(); + auto firstRange = *it; + assert(firstRange.begin() == range.begin()); + assert(firstRange.end() == std::next(range.begin())); + } + + // pattern not found + { + int buffer[] = {1, 2, 3}; + auto range = make_subrange(buffer); + int pattern[] = {1, 3}; + std::ranges::split_view sv{range, pattern}; + + auto it = sv.begin(); + auto firstRange = *it; + assert(firstRange.begin() == range.begin()); + assert(firstRange.end() == range.end()); + } + + // Make sure that we cache the result of begin() on subsequent calls + { + struct Foo { + int& equalsCalledTimes; + + constexpr bool operator==(const Foo&) const { + ++equalsCalledTimes; + return true; + } + }; + + int equalsCalledTimes = 0; + Foo buffer[] = {Foo{equalsCalledTimes}, Foo{equalsCalledTimes}}; + auto range = make_subrange(buffer); + + std::ranges::split_view sv{range, Foo{equalsCalledTimes}}; + + assert(equalsCalledTimes == 0); + + [[maybe_unused]] auto it1 = sv.begin(); + auto calledTimesAfterFirstBegin = equalsCalledTimes; + assert(calledTimesAfterFirstBegin != 0); + + for (int i = 0; i < 10; ++i) { + [[maybe_unused]] auto it2 = sv.begin(); + assert(equalsCalledTimes == calledTimesAfterFirstBegin); + } + } +} + +constexpr bool test() { + testOne<forward_iterator>(); + testOne<bidirectional_iterator>(); + testOne<random_access_iterator>(); + testOne<contiguous_iterator>(); + testOne<std::type_identity_t>(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/constraints.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/constraints.compile.pass.cpp new file mode 100644 index 0000000..66a491f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/constraints.compile.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// template<forward_range V, forward_range Pattern> +// requires view<V> && view<Pattern> && +// indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> +// class split_view; + +#include <ranges> + +#include "test_macros.h" +#include "test_iterators.h" + +template <class It> +using Range = std::ranges::subrange<It, sentinel_wrapper<It>>; + +template <class View, class Pattern> +concept HasSplitView = requires { typename std::ranges::split_view<View, Pattern>; }; + +static_assert(HasSplitView<Range<int*>, Range<int*>>); + +// !forward_range<V> +static_assert(!HasSplitView<Range<cpp20_input_iterator<int*>>, Range<int*>>); + +// !forward_range<Pattern> +static_assert(!HasSplitView<Range<int*>, Range<cpp20_input_iterator<int*>>>); + +struct NotAView { + int* begin() const; + int* end() const; +}; + +// !view<V> +static_assert(!HasSplitView<NotAView, Range<int*>>); + +// !view<Pattern> +static_assert(!HasSplitView<Range<int*>, NotAView>); + +// indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to +struct Foo {}; +static_assert(!HasSplitView<Range<int*>, Range<Foo*>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/ctad.compile.pass.cpp new file mode 100644 index 0000000..90d49e4 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/ctad.compile.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template <class R, class P> +// split_view(R&&, P&&) -> split_view<views::all_t<R>, views::all_t<P>>; +// +// template <input_range R> +// split_view(R&&, range_value_t<R>) -> split_view<views::all_t<R>, single_view<range_value_t<R>>>; + +#include <concepts> +#include <ranges> +#include <type_traits> +#include <utility> + +struct Container { + int* begin() const; + int* end() const; +}; + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +template <class I1, class I2, class ExpectedView, class ExpectedPattern> +constexpr void test() { + static_assert(std::is_same_v<decltype(std::ranges::split_view(std::declval<I1>(), std::declval<I2>())), + std::ranges::split_view<ExpectedView, ExpectedPattern>>); +} + +constexpr void testCtad() { + // (Range, Pattern) + test<View, View, View, View>(); + test<Container&, Container&, std::ranges::ref_view<Container>, std::ranges::ref_view<Container>>(); + test<Container&&, Container&&, std::ranges::owning_view<Container>, std::ranges::owning_view<Container>>(); + + // (Range, RangeElement) + test<Container&, int, std::ranges::ref_view<Container>, std::ranges::single_view<int>>(); + test<View, int, View, std::ranges::single_view<int>>(); + + // (Range, RangeElement) with implicit conversion. + test<Container&, bool, std::ranges::ref_view<Container>, std::ranges::single_view<int>>(); + test<View, bool, View, std::ranges::single_view<int>>(); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.default.pass.cpp new file mode 100644 index 0000000..aff0740 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.default.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 + +// constexpr split_view() requires +// default_initializable<V> && default_initializable<Pattern> = default; + +#include <cassert> +#include <ranges> +#include <type_traits> + +template <bool DefaultInitializable> +struct View : std::ranges::view_base { + int i = 42; + constexpr explicit View() + requires DefaultInitializable + = default; + int* begin() const; + int* end() const; +}; + +// clang-format off +static_assert( std::is_default_constructible_v<std::ranges::split_view<View<true >, View<true >>>); +static_assert(!std::is_default_constructible_v<std::ranges::split_view<View<false>, View<true >>>); +static_assert(!std::is_default_constructible_v<std::ranges::split_view<View<true >, View<false>>>); +static_assert(!std::is_default_constructible_v<std::ranges::split_view<View<false>, View<false>>>); +// clang-format on + +constexpr bool test() { + { + std::ranges::split_view<View<true>, View<true>> sv = {}; + assert(sv.base().i == 42); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/ctor.range.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.range.pass.cpp new file mode 100644 index 0000000..605e3d54 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.range.pass.cpp @@ -0,0 +1,139 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template <input_range Range> +// requires constructible_from<View, views::all_t<Range>> && +// constructible_from<Pattern, single_view<range_value_t<Range>>> +// constexpr split_view(Range&& r, range_value_t<Range> e); + +#include <algorithm> +#include <cassert> +#include <ranges> +#include <string> +#include <string_view> +#include <type_traits> +#include <utility> + +struct Counting { + int* times_copied = nullptr; + int* times_moved = nullptr; + + constexpr Counting(int& copies_ctr, int& moves_ctr) : times_copied(&copies_ctr), times_moved(&moves_ctr) {} + + constexpr Counting(const Counting& rhs) : times_copied(rhs.times_copied), times_moved(rhs.times_moved) { + ++(*times_copied); + } + constexpr Counting(Counting&& rhs) : times_copied(rhs.times_copied), times_moved(rhs.times_moved) { + ++(*times_moved); + } + + constexpr Counting& operator=(const Counting&) = default; + constexpr Counting& operator=(Counting&&) = default; +}; + +struct ElementWithCounting : Counting { + using Counting::Counting; + + constexpr bool operator==(const ElementWithCounting&) const { return true; } +}; + +struct RangeWithCounting : Counting { + using Counting::Counting; + + constexpr const ElementWithCounting* begin() const { return nullptr; } + constexpr const ElementWithCounting* end() const { return nullptr; } + + constexpr bool operator==(const RangeWithCounting&) const { return true; } +}; +static_assert(std::ranges::forward_range<RangeWithCounting>); +static_assert(!std::ranges::view<RangeWithCounting>); + +struct StrView : std::ranges::view_base { + std::string buffer_; + + template <std::ranges::range R> + constexpr StrView(R&& r) : buffer_(std::ranges::begin(r), std::ranges::end(r)) {} + constexpr const char* begin() const { return buffer_.data(); } + constexpr const char* end() const { return buffer_.data() + buffer_.size(); } + constexpr bool operator==(const StrView& rhs) const { return buffer_ == rhs.buffer_; } +}; +static_assert(std::ranges::random_access_range<StrView>); +static_assert(std::ranges::view<StrView>); +static_assert(std::is_copy_constructible_v<StrView>); + +constexpr bool test() { + { + using V = std::ranges::split_view<StrView, StrView>; + + // Calling the constructor with `(std::string, range_value_t)`. + { + std::string input("abc def"); + V v(input, ' '); + assert(v.base() == input); + assert(std::ranges::equal(*v.begin(), std::string_view("abc"))); + } + + // Calling the constructor with `(StrView, range_value_t)`. + { + StrView input("abc def"); + V v(input, ' '); + assert(v.base() == input); + assert(std::ranges::equal(*v.begin(), std::string_view("abc"))); + } + + struct Empty {}; + static_assert(!std::is_constructible_v<V, Empty, std::string_view>); + static_assert(!std::is_constructible_v<V, std::string_view, Empty>); + } + + // Make sure the arguments are moved, not copied. + { + using Range = RangeWithCounting; + using Element = ElementWithCounting; + using Pattern = std::ranges::single_view<Element>; + + // Arguments are lvalues. + { + using View = std::ranges::ref_view<Range>; + + int range_copied = 0, range_moved = 0, element_copied = 0, element_moved = 0; + Range range(range_copied, range_moved); + Element element(element_copied, element_moved); + + std::ranges::split_view<View, Pattern> v(range, element); + assert(range_copied == 0); // `ref_view` does neither copy... + assert(range_moved == 0); // ...nor move the element. + assert(element_copied == 1); // The element is copied into the argument... + assert(element_moved == 1); // ...and moved into the member variable. + } + + // Arguments are rvalues. + { + using View = std::ranges::owning_view<Range>; + + int range_copied = 0, range_moved = 0, element_copied = 0, element_moved = 0; + std::ranges::split_view<View, Pattern> v( + Range(range_copied, range_moved), Element(element_copied, element_moved)); + assert(range_copied == 0); + assert(range_moved == 1); // `owning_view` moves the given argument. + assert(element_copied == 0); + assert(element_moved == 1); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.view.pass.cpp new file mode 100644 index 0000000..ad206ee --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.view.pass.cpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr split_view(View base, Pattern pattern); + +#include <algorithm> +#include <cassert> +#include <ranges> +#include <string_view> +#include <utility> + +struct ViewWithCounting : std::ranges::view_base { + int* times_copied = nullptr; + int* times_moved = nullptr; + + constexpr ViewWithCounting(int& copies_ctr, int& moves_ctr) : times_copied(&copies_ctr), times_moved(&moves_ctr) {} + + constexpr ViewWithCounting(const ViewWithCounting& rhs) + : times_copied(rhs.times_copied), times_moved(rhs.times_moved) { + ++(*times_copied); + } + constexpr ViewWithCounting(ViewWithCounting&& rhs) : times_copied(rhs.times_copied), times_moved(rhs.times_moved) { + ++(*times_moved); + } + + constexpr const char* begin() const { return nullptr; } + constexpr const char* end() const { return nullptr; } + + constexpr ViewWithCounting& operator=(const ViewWithCounting&) = default; + constexpr ViewWithCounting& operator=(ViewWithCounting&&) = default; + constexpr bool operator==(const ViewWithCounting&) const { return true; } +}; + +constexpr bool test() { + { + std::string_view input = "abc def"; + std::ranges::lazy_split_view<std::string_view, std::string_view> v(input, " "); + assert(v.base() == input); + assert(std::ranges::equal(*v.begin(), std::string_view{"abc"})); + } + + // Make sure the arguments are moved, not copied. + { + using View = ViewWithCounting; + using Pattern = ViewWithCounting; + + // Arguments are lvalues. + { + int view_copied = 0, view_moved = 0, pattern_copied = 0, pattern_moved = 0; + View view(view_copied, view_moved); + Pattern pattern(pattern_copied, pattern_moved); + + std::ranges::split_view<View, Pattern> v(view, pattern); + assert(view_copied == 1); // The local variable is copied into the argument. + assert(view_moved == 1); + assert(pattern_copied == 1); + assert(pattern_moved == 1); + } + + // Arguments are rvalues. + { + int view_copied = 0, view_moved = 0, pattern_copied = 0, pattern_moved = 0; + std::ranges::split_view<View, Pattern> v(View(view_copied, view_moved), Pattern(pattern_copied, pattern_moved)); + assert(view_copied == 0); + assert(view_moved == 1); + assert(pattern_copied == 0); + assert(pattern_moved == 1); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/end.pass.cpp new file mode 100644 index 0000000..6c8aece --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/end.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 + +// constexpr auto end(); + +#include <cassert> +#include <ranges> +#include <type_traits> +#include <utility> + +#include "test_iterators.h" + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +// Test that end is not const +template <class T> +concept HasEnd = requires(T t) { t.end(); }; + +static_assert(HasEnd<std::ranges::split_view<View, View>>); +static_assert(!HasEnd<const std::ranges::split_view<View, View>>); + +constexpr bool test() { + // return iterator + { + int buffer[] = {1, 2, -1, 4, 5, 6, 5, 4, -1, 2, 1}; + auto inputView = std::views::all(buffer); + static_assert(std::ranges::common_range<decltype(inputView)>); + + std::ranges::split_view sv(buffer, -1); + using SplitIter = std::ranges::iterator_t<decltype(sv)>; + std::same_as<SplitIter> decltype(auto) sentinel = sv.end(); + assert(sentinel.base() == buffer + 11); + } + + // return sentinel + { + using Iter = int*; + using Sent = sentinel_wrapper<Iter>; + using Range = std::ranges::subrange<Iter, Sent>; + int buffer[] = {1, 2, -1, 4, 5, 6, 5, 4, -1, 2, 1}; + Range range = {buffer, Sent{buffer + 11}}; + static_assert(!std::ranges::common_range<Range>); + + std::ranges::split_view sv(range, -1); + auto sentinel = sv.end(); + + using SplitIter = std::ranges::iterator_t<decltype(sv)>; + static_assert(!std::same_as<decltype(sentinel), SplitIter>); + + assert(std::next(sv.begin(), 3) == sentinel); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/general.pass.cpp new file mode 100644 index 0000000..8684e3b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/general.pass.cpp @@ -0,0 +1,395 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Some basic examples of how split_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. + +// These test check the output `split_view` produces for a variety of inputs, including many corner cases, with no +// restrictions on which member functions can be called. + +#include <algorithm> +#include <array> +#include <cassert> +#include <concepts> +#include <map> +#include <ranges> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +#include "test_macros.h" + +template <std::ranges::view View, std::ranges::range Expected> +constexpr bool is_equal(View& view, const Expected& expected) { + return std::ranges::equal(view, expected, std::ranges::equal); +} + +template <class T, class Separator, class U, size_t M> +constexpr bool test_function_call(T&& input, Separator&& separator, std::array<U, M> expected) { + std::ranges::split_view v(input, separator); + return is_equal(v, expected); +} + +template <class T, class Separator, class U, size_t M> +constexpr bool test_with_piping(T&& input, Separator&& separator, std::array<U, M> expected) { + auto expected_it = expected.begin(); + for (auto e : input | std::ranges::views::split(separator)) { + if (expected_it == expected.end()) + return false; + if (!std::ranges::equal(e, *expected_it)) + return false; + + ++expected_it; + } + + return expected_it == expected.end(); +} + +constexpr bool test_l_r_values() { + using namespace std::string_view_literals; + + // Both lvalues and rvalues can be used as input. + { + // Lvalues. + { + auto input = "abc"sv; + auto sep = " "sv; + [[maybe_unused]] std::ranges::split_view v(input, sep); + } + + // Const lvalues. + { + const auto input = "abc"sv; + const auto sep = " "sv; + [[maybe_unused]] std::ranges::split_view v(input, sep); + } + + // Rvalues. + { + auto input = "abc"sv; + auto sep = " "sv; + [[maybe_unused]] std::ranges::split_view v(std::move(input), std::move(sep)); + } + + // Const rvalues. + { + const auto input = "abc"sv; + const auto sep = " "sv; + [[maybe_unused]] std::ranges::split_view v(std::move(input), std::move(sep)); + } + } + + return true; +} + +constexpr bool test_string_literal_separator() { + using namespace std::string_view_literals; + + // Splitting works as expected when the separator is a single character literal. + { + std::ranges::split_view v("abc def"sv, ' '); + assert(is_equal(v, std::array{"abc"sv, "def"sv})); + } + + // Counterintuitively, a seemingly equivalent separator expressed as a string literal doesn't match anything. This is + // because of the implicit terminating null in the literal. + { + std::ranges::split_view v("abc def"sv, " "); + assert(is_equal(v, std::array{"abc def"sv})); + } + + // To illustrate the previous point further, the separator is actually a two-character string literal: `{' ', '\0'}`. + // Should the input string contain that two-character sequence, the separator would match. + { + std::ranges::split_view v("abc \0def"sv, " "); + assert(is_equal(v, std::array{"abc"sv, "def"sv})); + } + + return true; +} + +// Make sure that a string literal and a `string_view` produce the same results (which isn't always the case, see +// below). +template <class T> +constexpr std::string_view sv(T&& str) { + return std::string_view(str); +}; + +template <class T, class Separator, class U, size_t M> +constexpr void test_one(T&& input, Separator&& separator, std::array<U, M> expected) { + assert(test_function_call(input, separator, expected)); + assert(test_with_piping(input, separator, expected)); +} + +constexpr bool test_string_literals() { + // These tests show characteristic examples of how using string literals with `split_view` produces unexpected + // results due to the implicit terminating null that is treated as part of the range. + + using namespace std::string_view_literals; + + char short_sep = ' '; + auto long_sep = "12"sv; + + // When splitting a string literal, only the last segment will be null-terminated (getting the terminating null from + // the original range). + { + std::array expected = {"abc"sv, std::string_view("def", sizeof("def"))}; + + assert(test_function_call("abc def", short_sep, expected)); + assert(test_with_piping("abc def", short_sep, expected)); + assert(test_function_call("abc12def", long_sep, expected)); + assert(test_with_piping("abc12def", long_sep, expected)); + } + + // Empty string. + { + // Because an empty string literal contains an implicit terminating null, the output will contain one segment. + std::array expected = {std::string_view("", 1)}; + + assert(test_function_call("", short_sep, expected)); + assert(test_with_piping("", short_sep, expected)); + assert(test_function_call("", long_sep, expected)); + assert(test_with_piping("", long_sep, expected)); + } + + // Terminating null in the separator -- the character literal `' '` and the seemingly equivalent string literal `" "` + // are treated differently due to the presence of an implicit `\0` in the latter. + { + const char input[] = "abc def"; + std::array expected_unsplit = {std::string_view(input, sizeof(input))}; + std::array expected_split = {"abc"sv, std::string_view("def", sizeof("def"))}; + + assert(test_function_call(input, " ", expected_unsplit)); + assert(test_function_call("abc \0def", " ", expected_split)); + // Note: string literals don't work with piping because arrays decay to pointers, and pointers don't model `range`. + } + + // Empty separator. + { + auto empty_sep = ""sv; + std::array expected = {"a"sv, "b"sv, "c"sv, "\0"sv}; + + assert(test_function_call("abc", empty_sep, expected)); + assert(test_with_piping("abc", empty_sep, expected)); + } + + return true; +} + +bool test_nontrivial_characters() { + // Try a deliberately heavyweight "character" type to see if it triggers any corner cases. + + using Map = std::map<std::string, int>; + using Vec = std::vector<Map>; + + Map sep = {{"yyy", 999}}; + Map m1 = { + {"a", 1}, + {"bc", 2}, + }; + Map m2 = { + {"def", 3}, + }; + Map m3 = { + {"g", 4}, + {"hijk", 5}, + }; + + Vec expected1 = {m1, m2}; + Vec expected2 = {m3}; + + std::ranges::split_view v(Vec{m1, m2, sep, m3}, sep); + + // Segment 1: {m1, m2} + auto outer = v.begin(); + assert(outer != v.end()); + auto inner = (*outer).begin(); + assert(*inner++ == m1); + assert(*inner++ == m2); + assert(inner == (*outer).end()); + + // Segment 2: {m3} + ++outer; + assert(outer != v.end()); + inner = (*outer).begin(); + assert(*inner++ == m3); + assert(inner == (*outer).end()); + + ++outer; + assert(outer == v.end()); + + return true; +} + +constexpr bool main_test() { + using namespace std::string_view_literals; + + char short_sep = ' '; + auto long_sep = "12"sv; + + // One separator. + { + std::array expected = {"abc"sv, "def"sv}; + test_one("abc def"sv, short_sep, expected); + test_one("abc12def"sv, long_sep, expected); + } + + // Several separators in a row. + { + std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv}; + test_one("abc def"sv, short_sep, expected); + test_one("abc12121212def"sv, long_sep, expected); + } + + // Trailing separator. + { + std::array expected = {"abc"sv, "def"sv, ""sv}; + test_one("abc def "sv, short_sep, expected); + test_one("abc12def12"sv, long_sep, expected); + } + + // Leading separator. + { + std::array expected = {""sv, "abc"sv, "def"sv}; + test_one(" abc def"sv, short_sep, expected); + test_one("12abc12def"sv, long_sep, expected); + } + + // No separator. + { + std::array expected = {"abc"sv}; + test_one("abc"sv, short_sep, expected); + test_one("abc"sv, long_sep, expected); + } + + // Input consisting of a single separator. + { + std::array expected = {""sv, ""sv}; + test_one(" "sv, short_sep, expected); + test_one("12"sv, long_sep, expected); + } + + // Input consisting of only separators. + { + std::array expected = {""sv, ""sv, ""sv, ""sv}; + test_one(" "sv, short_sep, expected); + test_one("121212"sv, long_sep, expected); + } + + // The separator and the string use the same character only. + { + auto overlapping_sep = "aaa"sv; + std::array expected = {""sv, "aa"sv}; + test_one("aaaaa"sv, overlapping_sep, expected); + } + + // Many redundant separators. + { + std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv}; + test_one(" abc def "sv, short_sep, expected); + test_one("1212abc121212def1212"sv, long_sep, expected); + } + + // Separators after every character. + { + std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv}; + test_one(" a b c "sv, short_sep, expected); + test_one("12a12b12c12"sv, long_sep, expected); + } + + // Overlap between the separator and the string (see https://wg21.link/lwg3505). + { + auto overlapping_sep = "ab"sv; + std::array expected = {"a"sv, "aa"sv, ""sv, "b"sv}; + test_one("aabaaababb"sv, overlapping_sep, expected); + } + + // Empty input. + { + std::array<std::string_view, 0> expected = {}; + test_one(""sv, short_sep, expected); + test_one(""sv, long_sep, expected); + } + + // Empty separator. + { + auto empty_sep = ""sv; + std::array expected = {"a"sv, "b"sv, "c"sv}; + test_one("abc"sv, empty_sep, expected); + test_one("abc"sv, empty_sep, expected); + } + + // Terminating null as a separator. + { + std::array expected = {"abc"sv, "def"sv}; + test_one("abc\0def"sv, '\0', expected); + test_one("abc\0\0def"sv, "\0\0"sv, expected); + } + + // Different character types. + { + // `char`. + test_function_call("abc def", ' ', std::array{"abc"sv, "def"sv}); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // `wchar_t`. + test_function_call(L"abc def", L' ', std::array{L"abc"sv, L"def"sv}); +#endif + // `char8_t`. + test_function_call(u8"abc def", u8' ', std::array{u8"abc"sv, u8"def"sv}); + // `char16_t`. + test_function_call(u"abc def", u' ', std::array{u"abc"sv, u"def"sv}); + // `char32_t`. + test_function_call(U"abc def", U' ', std::array{U"abc"sv, U"def"sv}); + } + + // Non-character input. + { + std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; + test_one(std::array{1, 2, 3, 0, 4, 5, 6}, 0, expected); + test_one(std::array{1, 2, 3, 0, 0, 0, 4, 5, 6}, std::array{0, 0, 0}, expected); + } + + return true; +} + +constexpr bool example_test() { + // example code in the spec + std::string str{"the quick brown fox"}; + std::vector<std::string_view> result; + for (auto r : std::views::split(str, ' ')) { + result.emplace_back(r.begin(), r.end()); + } + using namespace std::string_view_literals; + auto expected = {"the"sv, "quick"sv, "brown"sv, "fox"sv}; + assert(std::ranges::equal(result, expected)); + + return true; +} + +int main(int, char**) { + example_test(); + static_assert(example_test()); + + test_string_literals(); + static_assert(test_string_literals()); + + test_l_r_values(); + static_assert(test_l_r_values()); + + test_string_literal_separator(); + static_assert(test_string_literal_separator()); + + // Note: map is not `constexpr`, so this test is runtime-only. + test_nontrivial_characters(); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp new file mode 100644 index 0000000..325189a --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr iterator_t<V> base() const; + +#include <cassert> +#include <ranges> + +#include "../types.h" + +struct Iter : ForwardIterBase<Iter> { + int i; + constexpr Iter() = default; + constexpr Iter(int ii) : i(ii) {} +}; + +constexpr bool test() { + // base only has one const overload + using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>; + using SplitIter = std::ranges::iterator_t<SplitView>; + + // const & + { + SplitView sv; + const SplitIter it{sv, Iter{5}, {}}; + std::same_as<Iter> decltype(auto) base = it.base(); + assert(base.i == 5); + } + + // & + { + SplitView sv; + SplitIter it{sv, Iter{5}, {}}; + std::same_as<Iter> decltype(auto) base = it.base(); + assert(base.i == 5); + } + + // && + { + SplitView sv; + SplitIter it{sv, Iter{5}, {}}; + std::same_as<Iter> decltype(auto) base = std::move(it).base(); + assert(base.i == 5); + } + + // const && + { + SplitView sv; + const SplitIter it{sv, Iter{5}, {}}; + std::same_as<Iter> decltype(auto) base = std::move(it).base(); + assert(base.i == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp new file mode 100644 index 0000000..20b3c19 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.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 + +// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next); + +#include <cassert> +#include <ranges> + +#include "../types.h" + +struct TracedMoveIter : ForwardIterBase<TracedMoveIter> { + bool moved = false; + + constexpr TracedMoveIter() = default; + constexpr TracedMoveIter(const TracedMoveIter&) = default; + constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {} + constexpr TracedMoveIter& operator=(TracedMoveIter&&) = default; + constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default; +}; + +struct TracedMoveView : std::ranges::view_base { + constexpr TracedMoveIter begin() const { return {}; } + constexpr TracedMoveIter end() const { return {}; } +}; + +constexpr bool test() { + using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>; + using SplitIter = std::ranges::iterator_t<SplitView>; + + SplitView sv{TracedMoveView{}, TracedMoveView{}}; + SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}}; + assert(iter.base().moved); + + auto subRange = *iter; + assert(subRange.begin().moved); + assert(subRange.end().moved); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.default.pass.cpp new file mode 100644 index 0000000..72a6fe5 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.default.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// iterator() = default; + +#include <cassert> +#include <ranges> + +#include "../types.h" + +struct PODIter : ForwardIterBase<PODIter> { + int i; +}; + +constexpr bool test() { + using SplitView = std::ranges::split_view<std::ranges::subrange<PODIter>, std::ranges::subrange<PODIter>>; + using SplitIter = std::ranges::iterator_t<SplitView>; + { + SplitIter iter; + assert(iter.base().i == 0); // PODIter has to be initialised to have value 0 + } + + { + SplitIter iter = {}; // explicit(false) + assert(iter.base().i == 0); // PODIter has to be initialised to have value 0 + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp new file mode 100644 index 0000000..721a1cc --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.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 + +// constexpr value_type operator*() const; +// Effects: Equivalent to return {cur_, next_.begin()}; + +#include <cassert> +#include <ranges> + +#include "../types.h" + +struct Iter : ForwardIterBase<Iter> { + int i; + constexpr Iter() = default; + constexpr Iter(int ii) : i(ii) {} +}; + +constexpr bool test() { + using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>; + using SplitIter = std::ranges::iterator_t<SplitView>; + + { + SplitView sv; + Iter current{5}; + std::ranges::subrange next{Iter{6}, Iter{7}}; + const SplitIter it{sv, current, next}; + std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it; + assert(value.begin().i == 5); + assert(value.end().i == 6); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/equal.pass.cpp new file mode 100644 index 0000000..30888d1 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/equal.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 + +// friend constexpr bool operator==(const iterator& x, const iterator& y); + +#include <algorithm> +#include <cassert> +#include <concepts> +#include <ranges> + +#include "test_iterators.h" + +template <class Iter> +constexpr void testOne() { + using Range = std::ranges::subrange<Iter>; + using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>; + static_assert(std::ranges::common_range<SplitView>); + + { + // simple test + { + int buffer[] = {0, 1, 2, -1, 4, 5, 6}; + Range input(Iter{buffer}, Iter{buffer + 7}); + SplitView sv(input, -1); + auto b = sv.begin(), e = sv.end(); + + assert(b == b); + assert(!(b != b)); + + assert(e == e); + assert(!(e != e)); + + assert(!(b == e)); + assert(b != e); + + std::advance(b, 2); + assert(b == b); + assert(!(b != b)); + + assert(e == e); + assert(!(e != e)); + + assert(b == e); + assert(!(b != e)); + } + + // iterator at trailing empty position should not equal to end + { + int buffer[] = {0, 1, 2, -1}; + Range input(Iter{buffer}, Iter{buffer + 4}); + SplitView sv(input, -1); + auto b = sv.begin(), e = sv.end(); + + ++b; // cur points to end but trailing_empty is true + + assert(b != e); + assert(!(b == e)); + + ++b; + assert(b == e); + assert(!(b != e)); + } + + // Default-constructed iterators compare equal. + { + int buffer[] = {0, 1, 2, -1, 4, 5, 6}; + Range input(Iter{buffer}, Iter{buffer + 7}); + std::ranges::split_view sv(buffer, -1); + using SplitIter = decltype(sv.begin()); + SplitIter i1, i2; + assert(i1 == i2); + assert(!(i1 != i2)); + } + } +} + +constexpr bool test() { + testOne<forward_iterator<int*>>(); + testOne<bidirectional_iterator<int*>>(); + testOne<random_access_iterator<int*>>(); + testOne<contiguous_iterator<int*>>(); + testOne<int*>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/increment.pass.cpp new file mode 100644 index 0000000..56036dc --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/increment.pass.cpp @@ -0,0 +1,155 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr iterator& operator++(); +// constexpr iterator operator++(int); + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" + +template <class Iter> +constexpr void testOne() { + constexpr auto make_subrange = []<std::size_t N>(int(&buffer)[N]) { + return std::ranges::subrange<Iter>{Iter{buffer}, Iter{buffer + N}}; + }; + + // next subrange does not reach the end + { + int buffer[] = {0, 1, 2, -1, 4, 5, -1, 7}; + auto input = make_subrange(buffer); + std::ranges::split_view sv(input, -1); + using SplitIter = std::ranges::iterator_t<decltype(sv)>; + + // ++it + { + auto it = sv.begin(); + + decltype(auto) it1 = ++it; + static_assert(std::is_same_v<decltype(it1), SplitIter&>); + assert(&it1 == &it); + + assert(base((*it).begin()) == buffer + 4); + assert(base((*it).end()) == buffer + 6); + + ++it; + assert(base((*it).begin()) == buffer + 7); + assert(base((*it).end()) == buffer + 8); + } + + // it++ + { + auto it = sv.begin(); + auto original = it; + + decltype(auto) it1 = it++; + static_assert(std::is_same_v<decltype(it1), SplitIter>); + assert(it1 == original); + + assert(base((*it).begin()) == buffer + 4); + assert(base((*it).end()) == buffer + 6); + + it++; + assert(base((*it).begin()) == buffer + 7); + assert(base((*it).end()) == buffer + 8); + } + } + + // next's begin is the end + { + int buffer[] = {0, 1, 2}; + auto input = make_subrange(buffer); + std::ranges::split_view sv(input, -1); + using SplitIter = std::ranges::iterator_t<decltype(sv)>; + + // ++it + { + auto it = sv.begin(); + + decltype(auto) it1 = ++it; // trailing_empty is false + static_assert(std::is_same_v<decltype(it1), SplitIter&>); + assert(&it1 == &it); + + assert(it == sv.end()); + } + + // it++ + { + auto it = sv.begin(); + auto original = it; + + decltype(auto) it1 = it++; // trailing_empty is false + static_assert(std::is_same_v<decltype(it1), SplitIter>); + assert(it1 == original); + + assert(it == sv.end()); + } + } + + // next's end is the end + { + int buffer[] = {0, 1, 2, -1}; + auto input = make_subrange(buffer); + std::ranges::split_view sv(input, -1); + using SplitIter = std::ranges::iterator_t<decltype(sv)>; + + // ++it + { + auto it = sv.begin(); + + decltype(auto) it1 = ++it; // trailing_empty is true + static_assert(std::is_same_v<decltype(it1), SplitIter&>); + assert(&it1 == &it); + + assert(it != sv.end()); + assert(base((*it).begin()) == buffer + 4); + assert(base((*it).begin()) == buffer + 4); + + ++it; + assert(it == sv.end()); + } + + // it++ + { + auto it = sv.begin(); + auto original = it; + + decltype(auto) it1 = it++; // trailing_empty is true + static_assert(std::is_same_v<decltype(it1), SplitIter>); + assert(it1 == original); + + assert(it != sv.end()); + + assert(base((*it).begin()) == buffer + 4); + assert(base((*it).begin()) == buffer + 4); + + it++; + assert(it == sv.end()); + } + } +} + +constexpr bool test() { + testOne<forward_iterator<int*>>(); + testOne<bidirectional_iterator<int*>>(); + testOne<random_access_iterator<int*>>(); + testOne<contiguous_iterator<int*>>(); + testOne<int*>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/member_types.compile.pass.cpp new file mode 100644 index 0000000..aa1a346 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/member_types.compile.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 + +// Member typedefs in split_view<V, P>::iterator. + +#include <concepts> +#include <ranges> + +#include "test_macros.h" +#include "test_iterators.h" + +template <class Iter, class PatternIter> +constexpr void testIteratorTypedef() { + using Range = std::ranges::subrange<Iter, sentinel_wrapper<Iter>>; + using Pattern = std::ranges::subrange<PatternIter, sentinel_wrapper<PatternIter>>; + using SplitIter = std::ranges::iterator_t<std::ranges::split_view<Range, Pattern>>; + + static_assert(std::same_as<typename SplitIter::iterator_concept, // + std::forward_iterator_tag>); + + static_assert(std::same_as<typename SplitIter::iterator_category, // + std::input_iterator_tag>); + + static_assert(std::same_as<typename SplitIter::value_type, // + std::ranges::subrange<Iter>>); + + static_assert(std::same_as<typename SplitIter::difference_type, // + std::iter_difference_t<Iter>>); +} + +template <class Iter> +void testIteratorTypedefPattern() { + testIteratorTypedef<Iter, forward_iterator<int*>>(); + testIteratorTypedef<Iter, bidirectional_iterator<int*>>(); + testIteratorTypedef<Iter, random_access_iterator<int*>>(); + testIteratorTypedef<Iter, contiguous_iterator<int*>>(); + testIteratorTypedef<Iter, int*>(); +} + +void test() { + testIteratorTypedefPattern<forward_iterator<int*>>(); + testIteratorTypedefPattern<bidirectional_iterator<int*>>(); + testIteratorTypedefPattern<random_access_iterator<int*>>(); + testIteratorTypedefPattern<contiguous_iterator<int*>>(); + testIteratorTypedefPattern<int*>(); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.default.pass.cpp new file mode 100644 index 0000000..7914af9 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.default.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// sentinel() = default; + +#include <cassert> +#include <ranges> + +struct PODSentinel { + int i; + + friend constexpr bool operator==(int*, const PODSentinel& p) { return p.i == 0; } +}; + +struct Range : std::ranges::view_base { + int* begin() const; + PODSentinel end(); +}; + +constexpr bool test() { + using SplitView = std::ranges::split_view<Range, Range>; + using SplitIter = std::ranges::iterator_t<SplitView>; + using SplitSent = std::ranges::sentinel_t<SplitView>; + static_assert(!std::is_same_v<SplitSent, SplitIter>); + + { + SplitIter it; + SplitSent s; + assert(s == it); // to make sure that s.__end_.i is initialised to 0; + } + + { + SplitIter it; + SplitSent s = {}; + assert(s == it); // to make sure that s.__end_.i is initialised to 0; + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp new file mode 100644 index 0000000..c89b1ee --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr explicit sentinel(split_view& parent); + +#include <cassert> +#include <ranges> +#include <type_traits> + +#include "test_iterators.h" + +// test explicit +using Range = std::ranges::subrange<int*, sentinel_wrapper<int*>>; +using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>; +using SplitSent = std::ranges::sentinel_t<SplitView>; + +static_assert(std::is_constructible_v<SplitSent, SplitView&>); +static_assert(!std::is_convertible_v<SplitView&, SplitSent>); + +constexpr bool test() { + { + int buffer[] = {0, 1, 2}; + Range input{buffer, sentinel_wrapper<int*>(buffer + 3)}; + SplitView sv(input, -1); + auto it = sv.begin(); + + SplitSent sent(sv); + assert(sent != it); + + ++it; + assert(sent == it); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/equal.pass.cpp new file mode 100644 index 0000000..7b33a6b57 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/equal.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// friend constexpr bool operator==(const iterator& x, const sentinel& y); + +#include <algorithm> +#include <cassert> +#include <concepts> +#include <ranges> + +#include "test_iterators.h" + +template <class Iter> +constexpr void testOne() { + using Sent = sentinel_wrapper<Iter>; + using Range = std::ranges::subrange<Iter, Sent>; + using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>; + static_assert(!std::ranges::common_range<SplitView>); + + { + // simple test + { + int buffer[] = {0, 1, 2, -1, 4, 5, 6}; + Range input(Iter{buffer}, Sent{Iter{buffer + 7}}); + SplitView sv(input, -1); + auto b = sv.begin(); + auto e = sv.end(); + + assert(!(b == e)); + assert(b != e); + + std::advance(b, 2); + assert(b == e); + assert(!(b != e)); + } + + // iterator at trailing empty position should not equal to end + { + int buffer[] = {0, 1, 2, -1}; + Range input(Iter{buffer}, Sent{Iter{buffer + 4}}); + SplitView sv(input, -1); + auto b = sv.begin(); + auto e = sv.end(); + + ++b; // cur points to end but trailing_empty is true + + assert(b != e); + assert(!(b == e)); + + ++b; + assert(b == e); + assert(!(b != e)); + } + } +} + +constexpr bool test() { + testOne<forward_iterator<int*>>(); + testOne<bidirectional_iterator<int*>>(); + testOne<random_access_iterator<int*>>(); + testOne<contiguous_iterator<int*>>(); + testOne<int*>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/types.h b/libcxx/test/std/ranges/range.adaptors/range.split/types.h new file mode 100644 index 0000000..ff2ce38 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.split/types.h @@ -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 +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_SPLIT_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_SPLIT_TYPES_H + +#include <functional> +#include <ranges> + +#include "test_macros.h" +#include "test_iterators.h" +#include "test_range.h" + +template <class Derived> +struct ForwardIterBase { + using iterator_concept = std::forward_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + constexpr int operator*() const { return 5; } + + constexpr Derived& operator++() { return static_cast<Derived&>(*this); } + constexpr Derived operator++(int) { return {}; } + + friend constexpr bool operator==(const ForwardIterBase&, const ForwardIterBase&) { return true; }; +}; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_SPLIT_TYPES_H |