diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -155,7 +155,7 @@ `[range.transform] `_,`transform_view `_,[range.all],Zoe Carver,✅ `[range.take] `_,`take_view `_,[range.all],Zoe Carver,✅ `[range.join] `_,`join_view `_,[range.all],Zoe Carver,✅ -`[range.split] `_,`split_view (renamed to lazy_split_view by P2210) `_,[range.all],Zoe Carver,In progress +`[range.split] `_,`split_view (renamed to lazy_split_view by P2210) `_,[range.all],Zoe Carver and Konstantin Varlamov,✅ `[range.counted] `_,`view::counted `_,[range.subrange],Zoe Carver,✅ `[range.common] `_,`common_view `_,[range.all],Zoe Carver,✅ `[range.reverse] `_,`reverse_view `_,[range.all],Zoe Carver,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -351,6 +351,7 @@ __ranges/enable_view.h __ranges/iota_view.h __ranges/join_view.h + __ranges/lazy_split_view.h __ranges/non_propagating_cache.h __ranges/owning_view.h __ranges/range_adaptor.h diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/lazy_split_view.h @@ -0,0 +1,414 @@ +// -*- 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_LAZY_SPLIT_VIEW_H +#define _LIBCPP___RANGES_LAZY_SPLIT_VIEW_H + +#include <__algorithm/find.h> +#include <__algorithm/ranges_mismatch.h> +#include <__concepts/constructible.h> +#include <__config> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/default_sentinel.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/indirectly_comparable.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/iter_swap.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/single_view.h> +#include <__ranges/view_interface.h> +#include <__utility/move.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_RANGES) + +namespace ranges { +template struct __require_constant; + +template +concept __tiny_range = + sized_range<_Range> && + requires { typename __require_constant::size()>; } && + (remove_reference_t<_Range>::size() <= 1); + +template + requires view<_View> && view<_Pattern> && + indirectly_comparable, iterator_t<_Pattern>, ranges::equal_to> && + (forward_range<_View> || __tiny_range<_Pattern>) +class lazy_split_view : public view_interface> { + + _View __base_ = _View(); + _Pattern __pattern_ = _Pattern(); + + using _MaybeCurrent = _If, __non_propagating_cache>, __empty_cache>; + [[no_unique_address]] _MaybeCurrent __current_; + + template struct __outer_iterator; + template struct __inner_iterator; + +public: + _LIBCPP_HIDE_FROM_ABI + lazy_split_view() + requires default_initializable<_View> && default_initializable<_Pattern> = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr lazy_split_view(_View __base, _Pattern __pattern) + : __base_(std::move(__base)), __pattern_(std::move(__pattern)) {} + + template + requires constructible_from<_View, views::all_t<_Range>> && + constructible_from<_Pattern, single_view>> + _LIBCPP_HIDE_FROM_ABI + constexpr lazy_split_view(_Range&& __r, range_value_t<_Range> __e) + : __base_(views::all(std::forward<_Range>(__r))) + , __pattern_(ranges::single_view(std::move(__e))) {} + + _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 auto begin() { + if constexpr (forward_range<_View>) { + return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::begin(__base_)}; + } else { + if (!__current_.__has_value()) { + __current_.__emplace(ranges::begin(__base_)); + } + return __outer_iterator{*this}; + } + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() const requires forward_range<_View> && forward_range { + return __outer_iterator{*this, ranges::begin(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() requires forward_range<_View> && common_range<_View> { + return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() const { + if constexpr (forward_range<_View> && forward_range && common_range) { + return __outer_iterator{*this, ranges::end(__base_)}; + } else { + return default_sentinel; + } + } + +private: + + template + struct __outer_iterator_category {}; + + template + struct __outer_iterator_category<_Tp> { + using iterator_category = input_iterator_tag; + }; + + template struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> { + private: + template + friend class __inner_iterator; + + using _Parent = __maybe_const<_Const, lazy_split_view>; + using _Base = __maybe_const<_Const, _View>; + + _Parent* __parent_ = nullptr; + using _MaybeCurrent = _If, iterator_t<_Base>, __empty_cache>; + [[no_unique_address]] _MaybeCurrent __current_; + bool __trailing_empty_ = false; + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto& __current() { + if constexpr (forward_range<_Base>) { + return __current_; + } else { + return *__parent_->__current_; + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto& __current() const { + if constexpr (forward_range<_Base>) { + return __current_; + } else { + return *__parent_->__current_; + } + } + + public: + // using iterator_category = inherited; + using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; + using difference_type = range_difference_t<_Base>; + + struct value_type : view_interface { + private: + __outer_iterator __i_ = __outer_iterator(); + + public: + _LIBCPP_HIDE_FROM_ABI + value_type() = default; + _LIBCPP_HIDE_FROM_ABI + constexpr explicit value_type(__outer_iterator __i) + : __i_(std::move(__i)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; } + _LIBCPP_HIDE_FROM_ABI + constexpr default_sentinel_t end() const { return default_sentinel; } + }; + + _LIBCPP_HIDE_FROM_ABI + __outer_iterator() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __outer_iterator(_Parent& __parent) + requires (!forward_range<_Base>) + : __parent_(std::addressof(__parent)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current) + requires forward_range<_Base> + : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __outer_iterator(__outer_iterator __i) + requires _Const && convertible_to, iterator_t<_Base>> + : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr value_type operator*() const { return value_type{*this}; } + + _LIBCPP_HIDE_FROM_ABI + constexpr __outer_iterator& operator++() { + const auto __end = ranges::end(__parent_->__base_); + if (__current() == __end) { + __trailing_empty_ = false; + return *this; + } + + const auto [__pbegin, __pend] = ranges::subrange{__parent_->__pattern_}; + if (__pbegin == __pend) { + ++__current(); + + } else if constexpr (__tiny_range<_Pattern>) { + // TODO(varconst): should be `ranges::find`. + __current() = std::find(std::move(__current()), __end, *__pbegin); + if (__current() != __end) { + ++__current(); + if (__current() == __end) + __trailing_empty_ = true; + } + + } else { + do { + const auto [__b, __p] = std::ranges::mismatch(__current(), __end, __pbegin, __pend); + if (__p == __pend) { + __current() = __b; + if (__current() == __end) { + __trailing_empty_ = true; + } + break; // The pattern matched; skip it. + } + } while (++__current() != __end); + } + + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_Base>) { + auto __tmp = *this; + ++*this; + return __tmp; + } else { + ++*this; + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __outer_iterator& __x, const __outer_iterator& __y) + requires forward_range<_Base> + { + return __x.__current_ == __y.__current_ && __x.__trailing_empty_ == __y.__trailing_empty_; + } + + friend constexpr bool operator==(const __outer_iterator& __x, default_sentinel_t) { + return __x.__current() == ranges::end(__x.__parent_->__base_) && !__x.__trailing_empty_; + } + }; + + template + struct __inner_iterator_category {}; + + template + struct __inner_iterator_category<_Tp> { + using iterator_category = _If< + derived_from>::iterator_category, forward_iterator_tag>, + forward_iterator_tag, + typename iterator_traits>::iterator_category + >; + }; + + template struct __inner_iterator : private __inner_iterator_category<__maybe_const<_Const, _View>> { + private: + using _Base = __maybe_const<_Const, _View>; + __outer_iterator<_Const> __i_ = __outer_iterator<_Const>(); + bool __incremented_ = false; + + public: + using iterator_concept = typename __outer_iterator<_Const>::iterator_concept; + using value_type = range_value_t<_Base>; + using difference_type = range_difference_t<_Base>; + + _LIBCPP_HIDE_FROM_ABI + __inner_iterator() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) + : __i_(std::move(__i)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); } + _LIBCPP_HIDE_FROM_ABI + constexpr iterator_t<_Base> base() && + requires forward_range<_View> { return std::move(__i_.__current()); } + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator*() const { return *__i_.__current(); } + + _LIBCPP_HIDE_FROM_ABI + constexpr __inner_iterator& operator++() { + __incremented_ = true; + + if constexpr (!forward_range<_Base>) { + if constexpr (_Pattern::size() == 0) { + return *this; + } + } + + ++__i_.__current(); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_Base>) { + auto __tmp = *this; + ++*this; + return __tmp; + + } else { + ++*this; + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __inner_iterator& __x, const __inner_iterator& __y) + requires forward_range<_Base> { + return __x.__i_.__current() == __y.__i_.__current(); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __inner_iterator& __x, default_sentinel_t) { + auto [__pcur, __pend] = ranges::subrange{__x.__i_.__parent_->__pattern_}; + const auto __end = ranges::end(__x.__i_.__parent_->__base_); + + if constexpr (__tiny_range<_Pattern>) { + const auto& __cur = __x.__i_.__current(); + if (__cur == __end) return true; + if (__pcur == __pend) return __x.__incremented_; + + return *__cur == *__pcur; + + } else { + auto __cur = __x.__i_.__current(); + if (__cur == __end) return true; + if (__pcur == __pend) return __x.__incremented_; + + do { + if (*__cur != *__pcur) return false; + if (++__pcur == __pend) return true; + } while (++__cur != __end); + + return false; + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr decltype(auto) iter_move(const __inner_iterator& __i) + noexcept(noexcept(ranges::iter_move(__i.__i_.__current()))) { + return ranges::iter_move(__i.__i_.__current()); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr void iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) + noexcept(noexcept(ranges::iter_swap(__x.__i_.__current(), __y.__i_.__current()))) + requires indirectly_swappable> { + ranges::iter_swap(__x.__i_.__current(), __y.__i_.__current()); + } + }; + +}; + +template +lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view, views::all_t<_Pattern>>; + +template +lazy_split_view(_Range&&, range_value_t<_Range>) + -> lazy_split_view, single_view>>; + +namespace views { +namespace __lazy_split_view { +struct __fn : __range_adaptor_closure<__fn> { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range, _Pattern&& __pattern) const + noexcept( noexcept(lazy_split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)))) + -> decltype( lazy_split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))) + { return lazy_split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)); } + + template + requires constructible_from, _Pattern> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Pattern&& __pattern) const + noexcept(is_nothrow_constructible_v, _Pattern>) { + return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pattern>(__pattern))); + } +}; +} // namespace __lazy_split_view + +inline namespace __cpo { + inline constexpr auto lazy_split = __lazy_split_view::__fn{}; +} // namespace __cpo +} // namespace views + +} // namespace ranges + +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_LAZY_SPLIT_VIEW_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -825,6 +825,7 @@ module enable_view { private header "__ranges/enable_view.h" } module iota_view { private header "__ranges/iota_view.h" } module join_view { private header "__ranges/join_view.h" } + module lazy_split_view { private header "__ranges/lazy_split_view.h" } module non_propagating_cache { private header "__ranges/non_propagating_cache.h" } module owning_view { private header "__ranges/owning_view.h" } module range_adaptor { private header "__ranges/range_adaptor.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -196,6 +196,20 @@ template requires view && input_range> class join_view; + + // [range.lazy.split], lazy split view + template + concept tiny-range = see below; // exposition only + + template + requires view && view && + indirectly_comparable, iterator_t, ranges::equal_to> && + (forward_range || tiny-range) + class lazy_split_view; + + namespace views { + inline constexpr unspecified lazy_split = unspecified; + } } namespace std { @@ -245,6 +259,7 @@ #include <__ranges/enable_view.h> #include <__ranges/iota_view.h> #include <__ranges/join_view.h> +#include <__ranges/lazy_split_view.h> #include <__ranges/rbegin.h> #include <__ranges/ref_view.h> #include <__ranges/rend.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/lazy_split_view.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/lazy_split_view.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/lazy_split_view.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__ranges/lazy_split_view.h'}} +#include <__ranges/lazy_split_view.h> diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp @@ -0,0 +1,135 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::views::lazy_split + +#include + +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "types.h" + +template +concept CanBePiped = requires (View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; +}; + +static_assert(std::same_as); + +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(); } +}; + +constexpr bool test() { + using namespace std::string_view_literals; + + constexpr std::string_view input = "abc"; + constexpr std::string_view sep = "a"; + + // Test that std::views::lazy_split is a range adaptor. + + // Test `views::lazy_split(input, sep)`. + { + SomeView view(input); + + using Result = std::ranges::lazy_split_view; + std::same_as auto result = std::views::lazy_split(view, sep); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `views::lazy_split(sep)(input)`. + { + SomeView view(input); + + using Result = std::ranges::lazy_split_view; + std::same_as auto result = std::views::lazy_split(sep)(view); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `view | views::lazy_split`. + { + SomeView view(input); + + using Result = std::ranges::lazy_split_view; + std::same_as auto result = view | std::views::lazy_split(sep); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `adaptor | views::lazy_split`. + { + SomeView view(input); + auto f = [](char c) { return c; }; + auto partial = std::views::transform(f) | std::views::lazy_split(sep); + + using Result = std::ranges::lazy_split_view, std::string_view>; + std::same_as auto result = partial(view); + assert(result.base().base().begin() == input.begin()); + assert(result.base().base().end() == input.end()); + } + + // Test `views::lazy_split | adaptor`. + { + SomeView view(input); + auto f = [](auto v) { return v; }; + auto partial = std::views::lazy_split(sep) | std::views::transform(f); + + using Result = std::ranges::transform_view, decltype(f)>; + std::same_as 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::filter 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 { }; + auto partial = std::views::lazy_split_view(X{}); + (void)partial; + } + + // Check SFINAE friendliness. + { + struct NotAView { }; + + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert( std::is_invocable_v); + + static_assert( CanBePiped); + static_assert( CanBePiped); + static_assert(!CanBePiped); + static_assert(!CanBePiped); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/base.pass.cpp @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr View base() const& requires copy_constructible<_View>; +// constexpr View base() &&; + +#include + +#include +#include +#include +#include "types.h" + +template +concept CanCallBase = requires(View v) { std::forward(v).base(); }; + +static_assert( CanCallBase&>); +static_assert( CanCallBase const &>); +static_assert( CanCallBase&&>); +static_assert( CanCallBase const &&>); +static_assert(!CanCallBase&>); +static_assert(!CanCallBase const &>); +static_assert( CanCallBase&&>); +static_assert(!CanCallBase const &&>); + +struct View : std::ranges::view_base { + enum class InitializedBy { + Copy, + Move, + Invalid + }; + + std::string_view v_; + InitializedBy initialized_by = InitializedBy::Invalid; + constexpr View(std::string_view v) : v_(v) {} + + constexpr auto begin() const { return v_.begin(); } + constexpr auto end() const { return v_.end(); } + + constexpr View(const View& rhs) : v_(rhs.v_) { initialized_by = InitializedBy::Copy; } + constexpr View(View&& rhs) : v_(rhs.v_) { initialized_by = InitializedBy::Move; } + constexpr View& operator=(const View& rhs) = default; + constexpr View& operator=(View&& rhs) = default; + constexpr bool operator==(const View& rhs) const { return v_ == rhs.v_; } +}; + +constexpr bool test() { + // Copyable input -- both lvalue and rvalue overloads of `base` are available. + { + { + constexpr View str("abc def"); + std::ranges::lazy_split_view v(str, " "); + + std::same_as auto result = v.base(); + assert(result == str); + assert(result.initialized_by == View::InitializedBy::Copy); + } + + { + constexpr View str("abc def"); + std::ranges::lazy_split_view v(str, " "); + + std::same_as auto result = std::move(v).base(); + assert(result == str); + assert(result.initialized_by == View::InitializedBy::Move); + } + } + + // Move-only input -- only the rvalue overload of `base` is available. + { + std::ranges::lazy_split_view v; + assert(std::move(v).base() == MoveOnlyView()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/begin.pass.cpp @@ -0,0 +1,166 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto begin(); +// constexpr auto begin() const requires forward_range && forward_range; + +#include + +#include +#include +#include "test_iterators.h" +#include "types.h" + +template +concept OuterIterConceptSameAs = + std::same_as().begin())::iterator_concept, Expected>; + +template +concept InnerIterValueTypeSameAs = requires (View v) { + { *(*v.begin()).begin() } -> std::same_as; +}; + +template +concept ConstBeginDisabled = !requires (const View v) { + { (*v.begin()) }; +}; + +struct CountingInputView : std::ranges::view_base { + int* begin_calls_count = nullptr; + constexpr CountingInputView(int* count) : begin_calls_count(count) {} + constexpr cpp20_input_iterator begin() const { + ++(*begin_calls_count); + return cpp20_input_iterator(nullptr); + } + constexpr const int* end() const { return nullptr; } +}; +constexpr bool operator==(const cpp20_input_iterator&, const int*) { return true; } +constexpr bool operator==(const int*, const cpp20_input_iterator&) { return true; } +static_assert(std::ranges::input_range); + +struct CountingForwardView : std::ranges::view_base { + int* begin_calls_count = nullptr; + constexpr CountingForwardView(int* count) : begin_calls_count(count) {} + constexpr forward_iterator begin() const { + ++(*begin_calls_count); + return {}; + } + constexpr forward_iterator end() const { return {}; } +}; +static_assert(std::ranges::forward_range); + +constexpr bool test() { + // non-const: forward_range && simple-view -> outer-iterator + // const: forward_range && forward_range -> outer-iterator + { + using V = ForwardView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !simple-view -> outer-iterator + // const: forward_range && forward_range -> outer-iterator + { + using V = ForwardDiffView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !simple-view -> outer-iterator + // const: forward_range && !forward_range -> disabled + { + using V = ForwardOnlyIfNonConstView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + static_assert(ConstBeginDisabled); + } + + // non-const: !forward_range && tiny-range -> outer-iterator + // const: !forward_range -> disabled + { + using V = InputView; + using P = ForwardTinyView; + + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::forward_range

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + static_assert(ConstBeginDisabled); + } + + // For non-forward ranges, the `begin` iterator is cached. + { + int begin_calls_count = 0; + CountingInputView input(&begin_calls_count); + std::ranges::lazy_split_view v(input, ForwardTinyView()); + for (int i = 0; i != 3; ++i) { + (void)v.begin(); + assert(begin_calls_count == 1); + } + } + + // For forward ranges, the `begin` iterator is not cached. + { + int begin_calls_count = 0; + CountingForwardView input(&begin_calls_count); + std::ranges::lazy_split_view v(input, ForwardView()); + for (int i = 0; i != 3; ++i) { + (void)v.begin(); + assert(begin_calls_count == i + 1); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctad.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// lazy_split_view(R&&, P&&) -> lazy_split_view, views::all_t

>; +// +// template +// lazy_split_view(R&&, range_value_t) -> lazy_split_view, single_view>>; + +#include + +#include +#include +#include +#include "types.h" + +template +constexpr void test() { + I1 i1{}; + I2 i2{}; + + std::ranges::lazy_split_view v(std::move(i1), std::move(i2)); + using O = decltype(std::move(v).base()); + static_assert(std::same_as); +} + +constexpr void testCtad() { + // (Range, Pattern) + test(); + test>(); + + // (Range, RangeElement) + test>(); + test>(); + + // (Range, RangeElement) with implicit conversion. + test>(); + test>(); + + // Note: CTAD from (InputRange, TinyForwardRange) doesn't work -- the deduction guide wraps the pattern in + // `views::all_t`, resulting in `views::owning_view`. That type would never satisfy `tiny-range` + // because `views::owning_view` contains a member function `size()` that shadows the static `size()` in + // `TinyForwardRange`. +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.default.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// lazy_split_view() requires default_initializable && default_initializable

= default; + +#include + +#include +#include "test_macros.h" +#include "types.h" + +static_assert( std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); + +LIBCPP_STATIC_ASSERT( std::is_nothrow_default_constructible_v>); +static_assert(!std::is_nothrow_default_constructible_v); +static_assert(!std::is_nothrow_default_constructible_v< + std::ranges::lazy_split_view>); + +constexpr bool test() { + std::ranges::lazy_split_view v; + assert(v.base() == CopyableView()); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// requires constructible_from> && +// constructible_from>> +// constexpr lazy_split_view(Range&& r, range_value_t e); + +#include + +#include +#include +#include +#include "types.h" + +template +concept CanConstruct = requires(R&& r, D&& d) { + std::ranges::lazy_split_view(std::forward(r), std::move(d)); +}; + +static_assert( CanConstruct); +static_assert( CanConstruct); + +struct Empty {}; +static_assert(!CanConstruct); +static_assert(!CanConstruct); + +constexpr bool test() { + decltype(auto) str = "abc def"; + std::ranges::lazy_split_view v(str, ' '); + assert(v.base() == std::string_view(str, sizeof(str))); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr lazy_split_view(View base, Pattern pattern); + +#include + +#include +#include + +constexpr bool test() { + constexpr std::string_view str = "abc def"; + std::ranges::lazy_split_view v(str, " "); + assert(v.base() == str); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/end.pass.cpp @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto end() requires forward_range && common_range; +// constexpr auto end() const; + +#include + +#include +#include +#include "test_iterators.h" +#include "types.h" + +template +concept OuterIterConceptSameAs = + std::same_as().end())::iterator_concept, Expected>; + +template +concept InnerIterValueTypeSameAs = requires (View v) { + { *(*v.end()).begin() } -> std::same_as; +}; + +struct ForwardViewCommonIfConst : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardViewCommonIfConst() = default; + constexpr ForwardViewCommonIfConst(const char* ptr) : view_(ptr) {} + constexpr ForwardViewCommonIfConst(std::string_view v) : view_(v) {} + constexpr ForwardViewCommonIfConst(ForwardViewCommonIfConst&&) = default; + constexpr ForwardViewCommonIfConst& operator=(ForwardViewCommonIfConst&&) = default; + constexpr ForwardViewCommonIfConst(const ForwardViewCommonIfConst&) = default; + constexpr ForwardViewCommonIfConst& operator=(const ForwardViewCommonIfConst&) = default; + constexpr forward_iterator begin() { return forward_iterator(nullptr); } + constexpr std::default_sentinel_t end() { return std::default_sentinel; } + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(view_.end()); } +}; +bool operator==(forward_iterator, std::default_sentinel_t) { return false; } + +struct ForwardViewNonCommonRange : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardViewNonCommonRange() = default; + constexpr ForwardViewNonCommonRange(const char* ptr) : view_(ptr) {} + constexpr ForwardViewNonCommonRange(std::string_view v) : view_(v) {} + constexpr ForwardViewNonCommonRange(ForwardViewNonCommonRange&&) = default; + constexpr ForwardViewNonCommonRange& operator=(ForwardViewNonCommonRange&&) = default; + constexpr ForwardViewNonCommonRange(const ForwardViewNonCommonRange&) = default; + constexpr ForwardViewNonCommonRange& operator=(const ForwardViewNonCommonRange&) = default; + constexpr forward_iterator begin() { return forward_iterator(nullptr); } + constexpr std::default_sentinel_t end() { return std::default_sentinel; } + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr std::default_sentinel_t end() const { return std::default_sentinel; } +}; +bool operator==(forward_iterator, std::default_sentinel_t) { return false; } + +constexpr bool test() { + // non-const: forward_range && simple_view && simple_view

-> outer-iterator + // const: forward_range && common_range -> outer-iterator + { + using V = ForwardView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && common_range && simple_view && !simple_view

-> outer-iterator + // const: forward_range && forward_range && common_range -> outer-iterator + { + using V = ForwardView; + using P = ForwardNonSimpleView; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !common_range -> disabled + // const: forward_range && forward_range && common_range -> outer-iterator + { + using V = ForwardViewCommonIfConst; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !common_range -> disabled + // const: forward_range && forward_range && !common_range -> outer-iterator + { + using V = ForwardViewNonCommonRange; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::common_range); + + std::ranges::lazy_split_view v; + static_assert(std::same_as); + + const std::ranges::lazy_split_view cv; + static_assert(std::same_as); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp @@ -0,0 +1,399 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view; + +#include + +#include +#include +#include +#include +#include + +template +constexpr bool eq(const L& left, const R& right) { + auto l = left.begin(); + auto r = right.begin(); + + for (; l != left.end(); ++l, ++r) { + if (r == right.end()) { + auto next = l; + ++next; + return *l == '\0' && next == left.end(); + } + if (*l != *r) return false; + } + + return r == right.end(); +} + +template +constexpr bool test(const T (&input)[N], Separator separator, std::array expected) { + std::ranges::lazy_split_view v(input, separator); + + return std::equal(v.begin(), v.end(), expected.begin(), expected.end(), + [](const auto& split_value, const auto& expected_value) { + // TODO(varconst): use `std::ranges::equal`. + return eq(split_value, expected_value); + }); +} + +template +constexpr bool test_with_piping(const T (&input)[N], Separator separator, std::array expected) { + auto expected_it = expected.begin(); + for (auto e : input | std::ranges::views::lazy_split(separator)) { + // TODO(varconst): use `std::ranges::equal`. + if (expected_it == expected.end()) return false; + + if (!eq(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. + { + std::string_view input = "abc"; + std::string_view sep = " "; + std::ranges::lazy_split_view v(input, sep); + (void)v; + } + + // Const lvalues. + { + const std::string_view input = "abc"; + const std::string_view sep = " "; + std::ranges::lazy_split_view v(input, sep); + (void)v; + } + + // Rvalues. + { + std::ranges::lazy_split_view v("abc"sv, " "sv); + (void)v; + } + + // Const rvalues. + { + const std::string_view input = "abc"; + const std::string_view sep = " "; + std::ranges::lazy_split_view v(std::move(input), std::move(sep)); + (void)v; + } + } + + return true; +} + +int main(int, char**) { + using namespace std::string_view_literals; + + constexpr char short_sep = ' '; + constexpr std::string_view long_sep = "12"; + + // One separator. + { + { + decltype(auto) input = "abc def"; + constexpr std::array expected = {"abc"sv, "def"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc12def"; + constexpr std::array expected = {"abc"sv, "def"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Several separators in a row. + { + { + decltype(auto) input = "abc def"; + constexpr std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc12121212def"; + constexpr std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Trailing separator. + { + { + decltype(auto) input = "abc def "; + constexpr std::array expected = {"abc"sv, "def"sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc12def12"; + constexpr std::array expected = {"abc"sv, "def"sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Leading separator. + { + { + decltype(auto) input = " abc def"; + constexpr std::array expected = {""sv, "abc"sv, "def"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "12abc12def"; + constexpr std::array expected = {""sv, "abc"sv, "def"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // No separator. + { + { + decltype(auto) input = "abc"; + constexpr std::array expected = {"abc"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc"; + constexpr std::array expected = {"abc"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Empty string. + { + { + decltype(auto) input = ""; + constexpr std::array expected = {""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = ""; + constexpr std::array expected = {""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Input consisting of a single separator. + { + { + decltype(auto) input = " "; + constexpr std::array expected = {""sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "12"; + constexpr std::array expected = {""sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Input consisting of only separators. + { + { + decltype(auto) input = " "; + constexpr std::array expected = {""sv, ""sv, ""sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "121212"; + constexpr std::array expected = {""sv, ""sv, ""sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Many redundant separators. + { + { + decltype(auto) input = " abc def "; + constexpr std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "1212abc121212def1212"; + constexpr std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Separators after every character. + { + { + decltype(auto) input = " a b c "; + constexpr std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "12a12b12c12"; + constexpr std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Empty separator. + { + { + constexpr std::string_view empty_sep = ""; + + decltype(auto) input = "abc"; + constexpr std::array expected = {"a"sv, "b"sv, "c"sv, ""sv}; + + assert(test(input, empty_sep, expected)); + static_assert(test(input, empty_sep, expected)); + assert(test_with_piping(input, empty_sep, expected)); + static_assert(test_with_piping(input, empty_sep, expected)); + } + } + + // Overlap between the separator and the string. + { + { + constexpr std::string_view overlapping_sep = "ab"; + + decltype(auto) input = "aabaaababb"; + constexpr std::array expected = {"a"sv, "aa"sv, ""sv, "b"sv}; + + assert(test(input, overlapping_sep, expected)); + static_assert(test(input, overlapping_sep, expected)); + assert(test_with_piping(input, overlapping_sep, expected)); + static_assert(test_with_piping(input, overlapping_sep, expected)); + } + } + + // Non-character input. + { + { + constexpr int sep = 0; + constexpr int input[] = {1, 2, 3, 0, 4, 5, 6}; + constexpr std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; + + assert(test(input, sep, expected)); + static_assert(test(input, sep, expected)); + assert(test_with_piping(input, sep, expected)); + static_assert(test_with_piping(input, sep, expected)); + } + + { + constexpr std::array sep = {0, 0, 0}; + constexpr int input[] = {1, 2, 3, 0, 0, 0, 4, 5, 6}; + constexpr std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; + + assert(test(input, sep, expected)); + static_assert(test(input, sep, expected)); + assert(test_with_piping(input, sep, expected)); + static_assert(test_with_piping(input, sep, expected)); + } + } + + assert(test_l_r_values()); + static_assert(test_l_r_values()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/begin.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::begin() + +#include + +#include +#include +#include +#include + +constexpr bool test() { + using View = std::ranges::lazy_split_view; + std::string_view input = "a"; + + // Non-const. + { + View v(input, "b"); + auto val = *v.begin(); + assert(val.begin().base() == input); + } + + // Const. + { + View v(input, "b"); + const auto val = *v.begin(); + assert(val.begin().base() == input); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::value_type() + +#include + +#include +#include +#include +#include + +using View = std::ranges::lazy_split_view; +using ValueType = decltype(*std::declval().begin()); +static_assert(std::is_default_constructible_v); + +int main(int, char**) { + ValueType val; + (void)val; + + constexpr ValueType val2; + (void)val2; + // Note: attempting a check like `val.begin() == val.end()` leads to null pointer dereference. + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::value_type(outer-iterator i) + +#include + +#include +#include +#include +#include + +using View = std::ranges::lazy_split_view; +using ValueType = decltype(*std::declval().begin()); +static_assert(std::is_constructible_v>); + +constexpr bool test() { + std::string_view input = "a"; + View v(input, "b"); + ValueType val(v.begin()); + return val.begin().base() == input; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/end.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::end() + +#include + +#include +#include +#include + +constexpr bool test() { + using View = std::ranges::lazy_split_view; + std::string_view input = "a"; + + // Non-const. + { + View v(input, "b"); + auto val = *v.begin(); + static_assert(std::same_as); + } + + // Const. + { + View v(input, "b"); + const auto val = *v.begin(); + static_assert(std::same_as); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.copy.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +#include + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.current.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.current.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.current.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +#include + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::outer-iterator() + +#include + +#include +#include +#include +#include + +using View = std::ranges::lazy_split_view; +using OuterIter = decltype(std::declval().begin()); +static_assert(std::is_default_constructible_v); + +int main(int, char**) { + OuterIter i; + (void)i; + constexpr OuterIter i2; + (void)i2; + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent) +// requires (!forward_range) + +#include + +#include +#include +#include +#include +#include "../types.h" + +using SplitView = std::ranges::lazy_split_view; +using OuterIter = decltype(std::declval().begin()); +static_assert(std::ranges::forward_range); +static_assert(!std::is_constructible_v); + +using SplitViewInput = std::ranges::lazy_split_view; +using OuterIterInput = decltype(std::declval().begin()); +// TODO(varconst): uncomment once `lazy_split_view` can use `ranges::find`. +//static_assert(!std::ranges::forward_range); +//static_assert(std::is_constructible_v); + +constexpr bool test() { + InputView input; + SplitViewInput v(input, ForwardTinyView()); + + OuterIterInput i(v); + (void)v; + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/equal.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/equal.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +#include + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/increment.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +#include + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/star.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/star.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/star.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +#include + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/types.compile.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view::outer-iterator; + +#include + +#include +#include +#include "../types.h" + +template +using OuterIter = decltype(std::declval>().begin()); + +// iterator_concept + +static_assert(std::same_as::iterator_concept, std::forward_iterator_tag>); +static_assert(std::same_as::iterator_concept, std::input_iterator_tag>); + +// iterator_category + +static_assert(std::same_as::iterator_category, std::input_iterator_tag>); + +template +concept NoIteratorCategory = !requires { typename OuterIter::iterator_category; }; +static_assert(NoIteratorCategory); + +// difference_type + +static_assert(std::same_as::difference_type, + std::ranges::range_difference_t>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/types.h b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/types.h @@ -0,0 +1,174 @@ +//===----------------------------------------------------------------------===// +// +// 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_LAZY_SPLIT_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_TYPES_H + +#include +#include +#include "test_iterators.h" + +int globalBuff[8]; + +struct MoveOnlyView : std::ranges::view_base { + std::string_view view_; + constexpr MoveOnlyView() = default; + constexpr MoveOnlyView(const char* ptr) : view_(ptr) {} + constexpr MoveOnlyView(std::string_view v) : view_(v) {} + constexpr MoveOnlyView(MoveOnlyView&&) = default; + constexpr MoveOnlyView& operator=(MoveOnlyView&&) = default; + constexpr const char* begin() const { return view_.begin(); } + constexpr const char* end() const { return view_.end(); } + constexpr bool operator==(MoveOnlyView rhs) const { return view_ == rhs.view_; } +}; +static_assert( std::ranges::view); +static_assert( std::ranges::contiguous_range); +static_assert(!std::copyable); + +struct CopyableView : std::ranges::view_base { + int start_; + constexpr explicit CopyableView(int start = 0) : start_(start) {} + constexpr CopyableView(CopyableView const&) = default; + constexpr CopyableView& operator=(CopyableView const&) = default; + constexpr int *begin() const { return globalBuff + start_; } + constexpr int *end() const { return globalBuff + 8; } + constexpr bool operator==(CopyableView rhs) const { return start_ == rhs.start_; } +}; + +using ForwardIter = forward_iterator; + +struct ForwardView : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardView() = default; + constexpr ForwardView(const char* ptr) : view_(ptr) {} + constexpr ForwardView(std::string_view v) : view_(v) {} + constexpr ForwardView(ForwardView&&) = default; + constexpr ForwardView& operator=(ForwardView&&) = default; + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(view_.end()); } +}; + +// Iterator types differ based on constness of this class. +struct ForwardDiffView : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardDiffView() = default; + constexpr ForwardDiffView(const char* ptr) : view_(ptr) {} + constexpr ForwardDiffView(std::string_view v) : view_(v) {} + constexpr ForwardDiffView(ForwardDiffView&&) = default; + constexpr ForwardDiffView& operator=(ForwardDiffView&&) = default; + constexpr ForwardDiffView(const ForwardDiffView&) = default; + constexpr ForwardDiffView& operator=(const ForwardDiffView&) = default; + constexpr forward_iterator begin() { return forward_iterator(nullptr); } + constexpr forward_iterator end() { return forward_iterator(nullptr); } + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(view_.end()); } +}; + +struct ForwardNonSimpleView : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardNonSimpleView() = default; + constexpr ForwardNonSimpleView(const char* ptr) : view_(ptr) {} + constexpr ForwardNonSimpleView(std::string_view v) : view_(v) {} + constexpr ForwardNonSimpleView(ForwardNonSimpleView&&) = default; + constexpr ForwardNonSimpleView& operator=(ForwardNonSimpleView&&) = default; + constexpr forward_iterator begin() { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() { return forward_iterator(view_.end()); } + constexpr cpp20_input_iterator begin() const { return cpp20_input_iterator(view_.begin()); } + constexpr cpp20_input_iterator end() const { return cpp20_input_iterator(view_.end()); } +}; + +struct ForwardTinyView : ForwardView { + ForwardTinyView() = default; + constexpr static size_t size() { return 1; } +}; + +template +class almost_forward_iterator { + It it_; + + template friend class almost_forward_iterator; + +public: + typedef std::forward_iterator_tag iterator_category; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::difference_type difference_type; + typedef It pointer; + typedef typename std::iterator_traits::reference reference; + + TEST_CONSTEXPR almost_forward_iterator() : it_() {} + TEST_CONSTEXPR explicit almost_forward_iterator(It it) : it_(it) {} + template + TEST_CONSTEXPR almost_forward_iterator(const almost_forward_iterator& u) : it_(u.it_) {} + + TEST_CONSTEXPR reference operator*() const {return *it_;} + TEST_CONSTEXPR pointer operator->() const {return it_;} + + TEST_CONSTEXPR_CXX14 almost_forward_iterator& operator++() { ++it_; return *this; } + // Notice the slightly different return type. + TEST_CONSTEXPR_CXX14 const almost_forward_iterator operator++(int) {return almost_forward_iterator(it_);} + + friend TEST_CONSTEXPR bool operator==(const almost_forward_iterator& x, const almost_forward_iterator& y) {return x.it_ == y.it_;} + friend TEST_CONSTEXPR bool operator!=(const almost_forward_iterator& x, const almost_forward_iterator& y) {return x.it_ != y.it_;} + + template + void operator,(T const &) = delete; +}; + +struct ForwardOnlyIfNonConstView : std::ranges::view_base { + std::string_view view_; + + constexpr explicit ForwardOnlyIfNonConstView() = default; + constexpr ForwardOnlyIfNonConstView(const char* ptr) : view_(ptr) {} + constexpr ForwardOnlyIfNonConstView(std::string_view v) : view_(v) {} + constexpr ForwardOnlyIfNonConstView(ForwardOnlyIfNonConstView&&) = default; + constexpr ForwardOnlyIfNonConstView& operator=(ForwardOnlyIfNonConstView&&) = default; + + constexpr forward_iterator begin() { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() { return forward_iterator(view_.end()); } + constexpr almost_forward_iterator begin() const { + return almost_forward_iterator(view_.begin()); + } + constexpr almost_forward_iterator end() const { + return almost_forward_iterator(view_.end()); + } +}; + +struct ThrowingDefaultCtorForwardView : std::ranges::view_base { + ThrowingDefaultCtorForwardView() noexcept(false); + ForwardIter begin() const; + ForwardIter end() const; +}; + +struct NoDefaultCtorForwardView : std::ranges::view_base { + NoDefaultCtorForwardView() = delete; + ForwardIter begin() const; + ForwardIter end() const; +}; + +struct InputView : std::ranges::view_base { + constexpr cpp20_input_iterator begin() { return cpp20_input_iterator(globalBuff); } + constexpr int* end() { return globalBuff + 8; } + constexpr cpp20_input_iterator begin() const { return cpp20_input_iterator(globalBuff); } + constexpr const int* end() const { return globalBuff + 8; } +}; +constexpr bool operator==(const cpp20_input_iterator &lhs, int* rhs) { return base(lhs) == rhs; } +constexpr bool operator==(int* lhs, const cpp20_input_iterator &rhs) { return base(rhs) == lhs; } + +struct ForwardRange { + ForwardIter begin() const; + ForwardIter end() const; +}; + +struct InputRange { + cpp20_input_iterator begin() const; + std::default_sentinel_t end() const; +}; +bool operator==(const cpp20_input_iterator&, std::default_sentinel_t); +static_assert(std::ranges::input_range); + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_TYPES_H diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/view_interface.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/view_interface.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/view_interface.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view; + +#include + +#include +#include + +using V = decltype(std::ranges::lazy_split_view("abc", "d")); +static_assert(std::ranges::forward_range); + +// empty() +template +concept CanCallEmpty = requires (View& v) { v.empty(); }; +static_assert(CanCallEmpty); + +// operator bool() +template +concept CanCallOperatorBool = requires (View& v) { static_cast(v); }; +static_assert(CanCallOperatorBool); + +// front() +template +concept CanCallFront = requires (View& v) { v.front(); }; +static_assert(CanCallFront); + +// size() is unavailable. +static_assert(!std::sized_sentinel_for, std::ranges::iterator_t>); +template +concept CanCallSize = requires (View& v) { v.size(); }; +static_assert(!CanCallSize); + +// back() is unavailable. +static_assert(!std::ranges::bidirectional_range); +template +concept CanCallBack = requires (View& v) { v.back(); }; +static_assert(!CanCallBack); + +// data() is unavailable. +static_assert(!std::contiguous_iterator>); +template +concept CanCallData = requires (View& v) { v.data(); }; +static_assert(!CanCallData); + +// operator[] is unavailable. +static_assert(!std::ranges::random_access_range); +template +concept CanCallOperatorSubscript = requires (View& v) { v[0]; }; +static_assert(!CanCallOperatorSubscript); + +int main(int, char**) { + // empty() + { + { + constexpr std::ranges::lazy_split_view v("abc", ""); + assert(!v.empty()); + static_assert(!v.empty()); + } + // Note: a `lazy_split_view` is never empty. + } + + // operator bool() + { + { + constexpr std::ranges::lazy_split_view v("abc", ""); + assert(v); + static_assert(v); + } + // Note: a `lazy_split_view` is never empty. + } + + // front() + { + constexpr std::ranges::lazy_split_view v("abc", ""); + assert(*(v.front()).begin() == 'a'); + static_assert(*(v.front()).begin() == 'a'); + } + + return 0; +} diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -510,6 +510,9 @@ template void operator,(T const &) = delete; }; +#ifndef _LIBCPP_HAS_NO_CONCEPTS + static_assert(std::input_iterator>); +#endif template struct iter_value_or_void { using type = void; };