diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -108,7 +108,7 @@ "`P0784R7 `__","CWG","More constexpr containers","Cologne","|Complete|","12.0" "`P0980R1 `__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0" "`P1004R2 `__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0" -"`P1035R7 `__","LWG","Input Range Adaptors, Todo: elements_view and drop_while_view","Cologne","|In Progress|","" +"`P1035R7 `__","LWG","Input Range Adaptors, Todo: elements_view","Cologne","|In Progress|","" "`P1065R2 `__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0" "`P1135R6 `__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0" "`P1207R4 `__","LWG","Movability of Single-pass Iterators","Cologne","|Complete|","15.0" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -481,6 +481,7 @@ __ranges/dangling.h __ranges/data.h __ranges/drop_view.h + __ranges/drop_while_view.h __ranges/empty.h __ranges/empty_view.h __ranges/enable_borrowed_range.h diff --git a/libcxx/include/__ranges/drop_while_view.h b/libcxx/include/__ranges/drop_while_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/drop_while_view.h @@ -0,0 +1,129 @@ +// -*- 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_DROP_WHILE_VIEW_H +#define _LIBCPP___RANGES_DROP_WHILE_VIEW_H + +#include <__algorithm/ranges_find_if_not.h> +#include <__assert> +#include <__concepts/constructible.h> +#include <__config> +#include <__functional/bind_back.h> +#include <__functional/reference_wrapper.h> +#include <__iterator/concepts.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/copyable_box.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/view_interface.h> +#include <__type_traits/conditional.h> +#include <__type_traits/decay.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/is_object.h> +#include <__utility/forward.h> +#include <__utility/in_place.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 + requires input_range<_View> && is_object_v<_Pred> && indirect_unary_predicate> +class drop_while_view : public view_interface> { +public: + _LIBCPP_HIDE_FROM_ABI drop_while_view() + requires default_initializable<_View> && default_initializable<_Pred> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr drop_while_view(_View __base, _Pred __pred) + : __base_(std::move(__base)), __pred_(std::in_place, std::move(__pred)) {} + + _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 const _Pred& pred() const { return *__pred_; } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { + _LIBCPP_ASSERT(__pred_.__has_value(), + "drop_while_view needs to have a non-empty predicate before calling begin() -- did a previous " + "assignment to this drop_while_view fail?"); + if constexpr (_UseCache) { + if (!__cached_begin_.__has_value()) { + __cached_begin_.__emplace(ranges::find_if_not(__base_, std::cref(*__pred_))); + } + return *__cached_begin_; + } else { + return ranges::find_if_not(__base_, std::cref(*__pred_)); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() { return ranges::end(__base_); } + +private: + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_; + + static constexpr bool _UseCache = forward_range<_View>; + using _Cache = _If<_UseCache, __non_propagating_cache>, __empty_cache>; + _LIBCPP_NO_UNIQUE_ADDRESS _Cache __cached_begin_ = _Cache(); +}; + +template +inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_View>; + +template +drop_while_view(_Range&&, _Pred) -> drop_while_view, _Pred>; + +namespace views { +namespace __drop_while { + +struct __fn { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pred&& __pred) const + noexcept(noexcept(/**/ drop_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)))) + -> decltype(/*--*/ drop_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))) { + return /*-------------*/ drop_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)); + } + + template + requires constructible_from, _Pred> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pred&& __pred) const + noexcept(is_nothrow_constructible_v, _Pred>) { + return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pred>(__pred))); + } +}; + +} // namespace __drop_while + +inline namespace __cpo { +inline constexpr auto drop_while = __drop_while::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_DROP_WHILE_VIEW_H \ No newline at end of file diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1021,6 +1021,7 @@ module dangling { private header "__ranges/dangling.h" } module data { private header "__ranges/data.h" } module drop_view { private header "__ranges/drop_view.h" } + module drop_while_view { private header "__ranges/drop_while_view.h" } module empty { private header "__ranges/empty.h" } module empty_view { private header "__ranges/empty_view.h" } module enable_borrowed_range { private header "__ranges/enable_borrowed_range.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -166,6 +166,18 @@ template inline constexpr bool enable_borrowed_range> = enable_borrowed_range; + // [range.drop.while], drop while view + template + requires input_range && is_object_v && + indirect_unary_predicate> + class drop_while_view; + + template + inline constexpr bool enable_borrowed_range> = + enable_borrowed_range; + + namespace views { inline constexpr unspecified drop_while = unspecified; } + // [range.transform], transform view template requires view && is_object_v && @@ -303,6 +315,7 @@ #include <__ranges/dangling.h> #include <__ranges/data.h> #include <__ranges/drop_view.h> +#include <__ranges/drop_while_view.h> #include <__ranges/empty.h> #include <__ranges/empty_view.h> #include <__ranges/enable_borrowed_range.h> diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -512,6 +512,7 @@ #include <__ranges/dangling.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/dangling.h'}} #include <__ranges/data.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/data.h'}} #include <__ranges/drop_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/drop_view.h'}} +#include <__ranges/drop_while_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/drop_while_view.h'}} #include <__ranges/empty.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty.h'}} #include <__ranges/empty_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty_view.h'}} #include <__ranges/enable_borrowed_range.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/enable_borrowed_range.h'}} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.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 +// +//===----------------------------------------------------------------------===// + +// + +// Call begin() on drop_while_view with empty predicate + +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +#include + +#include "check_assertion.h" + +struct Exception {}; +struct Pred { + Pred() = default; + Pred(const Pred&) { throw Exception{}; } + Pred& operator=(const Pred&) = delete; + + Pred(Pred&&) = default; + Pred& operator=(Pred&&) = default; + + bool operator()(int) const { return false; } +}; + +int main(int, char**) { + int input[] = {1, 2, 3}; + auto v1 = std::views::drop_while(input, Pred{}); + auto v2 = std::views::drop_while(input, Pred{}); + try { + v1 = v2; + } catch (...) { + } + TEST_LIBCPP_ASSERT_FAILURE(v1.begin(), + "drop_while_view needs to have a non-empty predicate before calling begin() -- did a " + "previous assignment to this drop_while_view fail?"); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/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::drop_while + +#include +#include +#include +#include +#include + +struct Pred { + constexpr bool operator()(int i) const { return i < 3; } +}; + +struct Foo {}; + +template +struct BufferView : std::ranges::view_base { + T* buffer_; + std::size_t size_; + + template + constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {} +}; + +using IntBufferView = BufferView; + +struct MoveOnlyView : IntBufferView { + using IntBufferView::IntBufferView; + MoveOnlyView(const MoveOnlyView&) = delete; + MoveOnlyView& operator=(const MoveOnlyView&) = delete; + MoveOnlyView(MoveOnlyView&&) = default; + MoveOnlyView& operator=(MoveOnlyView&&) = default; + constexpr const int* begin() const { return buffer_; } + constexpr const int* end() const { return buffer_ + size_; } +}; + +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(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); + +template +concept CanBePiped = + requires(View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; + }; + +static_assert(!CanBePiped); +static_assert(CanBePiped); +static_assert(!CanBePiped); +static_assert(CanBePiped); +static_assert(!CanBePiped); + +constexpr bool test() { + int buff[] = {1, 2, 3, 4, 3, 2, 1}; + + // Test `views::drop_while(p)(v)` + { + using Result = std::ranges::drop_while_view; + std::same_as auto result = std::views::drop_while(Pred{})(MoveOnlyView{buff}); + auto expected = {3, 4, 3, 2, 1}; + assert(std::ranges::equal(result, expected)); + } + { + auto const partial = std::views::drop_while(Pred{}); + using Result = std::ranges::drop_while_view; + std::same_as auto result = partial(MoveOnlyView{buff}); + auto expected = {3, 4, 3, 2, 1}; + assert(std::ranges::equal(result, expected)); + } + + // Test `v | views::drop_while(p)` + { + using Result = std::ranges::drop_while_view; + std::same_as auto result = MoveOnlyView{buff} | std::views::drop_while(Pred{}); + auto expected = {3, 4, 3, 2, 1}; + assert(std::ranges::equal(result, expected)); + } + { + auto const partial = std::views::drop_while(Pred{}); + using Result = std::ranges::drop_while_view; + std::same_as auto result = MoveOnlyView{buff} | partial; + auto expected = {3, 4, 3, 2, 1}; + assert(std::ranges::equal(result, expected)); + } + + // Test `views::drop_while(v, p)` + { + using Result = std::ranges::drop_while_view; + std::same_as auto result = std::views::drop_while(MoveOnlyView{buff}, Pred{}); + auto expected = {3, 4, 3, 2, 1}; + assert(std::ranges::equal(result, expected)); + } + + // Test adaptor | adaptor + { + struct Pred2 { + constexpr bool operator()(int i) const { return i < 4; } + }; + auto const partial = std::views::drop_while(Pred{}) | std::views::drop_while(Pred2{}); + using Result = std::ranges::drop_while_view, Pred2>; + std::same_as auto result = MoveOnlyView{buff} | partial; + auto expected = {4, 3, 2, 1}; + assert(std::ranges::equal(result, expected)); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/base.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 V base() const & requires copy_constructible { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include +#include +#include +#include + +#include "MoveOnly.h" + +struct View : std::ranges::view_base { + int i; + int* begin() const; + int* end() const; +}; + +struct MoveOnlyView : View { + MoveOnly mo; +}; + +template +concept HasBase = requires(T&& t) { std::forward(t).base(); }; + +struct Pred { + constexpr bool operator()(int i) const { return i > 5; } +}; + +static_assert(HasBase const&>); +static_assert(HasBase&&>); + +static_assert(!HasBase const&>); +static_assert(HasBase&&>); + +constexpr bool test() { + // const & + { + const std::ranges::drop_while_view dwv{View{{}, 5}, {}}; + std::same_as decltype(auto) v = dwv.base(); + assert(v.i == 5); + } + + // & + { + std::ranges::drop_while_view dwv{View{{}, 5}, {}}; + std::same_as decltype(auto) v = dwv.base(); + assert(v.i == 5); + } + + // && + { + std::ranges::drop_while_view dwv{View{{}, 5}, {}}; + std::same_as decltype(auto) v = std::move(dwv).base(); + assert(v.i == 5); + } + + // const && + { + const std::ranges::drop_while_view dwv{View{{}, 5}, {}}; + std::same_as decltype(auto) v = std::move(dwv).base(); + assert(v.i == 5); + } + + // move only + { + std::ranges::drop_while_view dwv{MoveOnlyView{{}, 5}, {}}; + std::same_as decltype(auto) v = std::move(dwv).base(); + assert(v.mo.get() == 5); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/begin.pass.cpp @@ -0,0 +1,182 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include +#include +#include +#include + +#include "test_iterators.h" + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +// Test that begin is not const +template +concept HasBegin = requires(T t) { t.begin(); }; + +struct Pred { + constexpr bool operator()(int i) const { return i < 3; } +}; + +static_assert(HasBegin>); +static_assert(!HasBegin>); + +constexpr auto always = [](auto v) { return [v](auto&&...) { return v; }; }; + +template +constexpr void testOne() { + using Sent = sentinel_wrapper; + using Range = std::ranges::subrange; + constexpr auto make_subrange = [](int(&buffer)[N]) { + return Range{Iter{buffer}, Sent{Iter{buffer + N}}}; + }; + + // empty + { + std::array a; + Range range{Iter{a.data()}, Sent{Iter{a.data() + a.size()}}}; + std::ranges::drop_while_view dwv{std::move(range), always(false)}; + std::same_as decltype(auto) it = dwv.begin(); + assert(base(it) == a.data() + a.size()); + } + + // 1 element not dropped + { + int buffer[] = {1}; + auto range = make_subrange(buffer); + std::ranges::drop_while_view dwv{std::move(range), always(false)}; + std::same_as decltype(auto) it = dwv.begin(); + assert(base(it) == buffer); + } + + // 1 element dropped + { + int buffer[] = {1}; + auto range = make_subrange(buffer); + std::ranges::drop_while_view dwv{std::move(range), always(true)}; + std::same_as decltype(auto) it = dwv.begin(); + assert(base(it) == buffer + 1); + } + + // multiple elements. no element dropped + { + int buffer[] = {1, 2, 3, 4, 5}; + auto range = make_subrange(buffer); + std::ranges::drop_while_view dwv{std::move(range), always(false)}; + std::same_as decltype(auto) it = dwv.begin(); + assert(base(it) == buffer); + } + + // multiple elements. all elements dropped + { + int buffer[] = {1, 2, 3, 4, 5}; + auto range = make_subrange(buffer); + std::ranges::drop_while_view dwv{std::move(range), always(true)}; + std::same_as decltype(auto) it = dwv.begin(); + assert(base(it) == buffer + 5); + } + + // multiple elements. some elements dropped + { + int buffer[] = {1, 2, 3, 2, 1}; + auto range = make_subrange(buffer); + std::ranges::drop_while_view dwv{std::move(range), [](int i) { return i < 3; }}; + std::same_as decltype(auto) it = dwv.begin(); + assert(base(it) == buffer + 2); + } + + // Make sure we do not make a copy of the predicate when we call begin() + { + struct TrackingPred { + constexpr explicit TrackingPred(bool* moved, bool* copied) : moved_(moved), copied_(copied) {} + constexpr TrackingPred(TrackingPred const& other) : moved_(other.moved_), copied_(other.copied_) { + *copied_ = true; + } + constexpr TrackingPred(TrackingPred&& other) : moved_(other.moved_), copied_(other.copied_) { *moved_ = true; } + TrackingPred& operator=(TrackingPred const&) = default; + TrackingPred& operator=(TrackingPred&&) = default; + + constexpr bool operator()(int i) const { return i < 3; } + bool* moved_; + bool* copied_; + }; + + int buffer[] = {1, 2, 3, 2, 1}; + bool moved = false, copied = false; + auto range = make_subrange(buffer); + std::ranges::drop_while_view dwv{std::move(range), TrackingPred(&moved, &copied)}; + moved = false; + copied = false; + [[maybe_unused]] auto it = dwv.begin(); + assert(!moved); + assert(!copied); + } + + // Test with a non-const predicate + { + int buffer[] = {1, 2, 3, 2, 1}; + auto range = make_subrange(buffer); + std::ranges::drop_while_view dwv{std::move(range), [](int i) mutable { return i < 3; }}; + std::same_as decltype(auto) it = dwv.begin(); + assert(base(it) == buffer + 2); + } + + // Test with a predicate that takes by non-const reference + { + int buffer[] = {1, 2, 3, 2, 1}; + auto range = make_subrange(buffer); + std::ranges::drop_while_view dwv{std::move(range), [](int& i) { return i < 3; }}; + std::same_as decltype(auto) it = dwv.begin(); + assert(base(it) == buffer + 2); + } + + if constexpr (std::forward_iterator) { + // Make sure that we cache the result of begin() on subsequent calls + { + int buffer[] = {1, 2, 3, 2, 1}; + auto range = make_subrange(buffer); + + int called = 0; + auto pred = [&](int i) { + ++called; + return i < 3; + }; + std::ranges::drop_while_view dwv{range, pred}; + for (auto i = 0; i < 10; ++i) { + std::same_as decltype(auto) it = dwv.begin(); + assert(base(it) == buffer + 2); + assert(called == 3); + } + } + } +} + +constexpr bool test() { + testOne>(); + testOne>(); + testOne>(); + testOne>(); + testOne>(); + testOne>(); + testOne(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.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 +// +// template +// inline constexpr bool enable_borrowed_range> = +// enable_borrowed_range; + +#include + +struct NonBorrowed : std::ranges::view_base { + int* begin(); + int* end(); +}; + +struct Borrowed : std::ranges::view_base { + int* begin(); + int* end(); +}; + +struct Pred { + bool operator()(int) const; +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +static_assert(!std::ranges::borrowed_range>); +static_assert(std::ranges::borrowed_range>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctad.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 +// drop_while_view(R&&, Pred) -> drop_while_view, Pred>; + +#include +#include +#include + +struct Container { + int* begin() const; + int* end() const; +}; + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct Pred { + bool operator()(int i) const; +}; + +bool pred(int); + +static_assert(std::is_same_v, Pred>>); + +static_assert(std::is_same_v>); + +static_assert(std::is_same_v>); + +void testRef() { + Container c{}; + Pred p{}; + static_assert(std::is_same_v, Pred>>); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.default.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// drop_while_view() requires default_initializable && default_initializable = default; + +#include +#include +#include + +template +struct View : std::ranges::view_base { + int i = 42; + constexpr explicit View() + requires DefaultInitializable + = default; + int* begin() const; + int* end() const; +}; + +template +struct Pred { + int i = 42; + constexpr explicit Pred() + requires DefaultInitializable + = default; + bool operator()(int) const; +}; + +// clang-format off +static_assert( std::is_default_constructible_v, Pred>>); +static_assert(!std::is_default_constructible_v, Pred>>); +static_assert(!std::is_default_constructible_v, Pred>>); +static_assert(!std::is_default_constructible_v, Pred>>); +// clang-format on + +constexpr bool test() { + { + std::ranges::drop_while_view, Pred> dwv = {}; + assert(dwv.base().i == 42); + assert(dwv.pred().i == 42); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.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 drop_while_view(V base, Pred pred); + +#include +#include +#include +#include + +#include "MoveOnly.h" + +struct View : std::ranges::view_base { + MoveOnly mo; + int* begin() const; + int* end() const; +}; + +struct Pred { + bool copied = false; + bool moved = false; + constexpr Pred() = default; + constexpr Pred(Pred&&) : moved(true) {} + constexpr Pred(const Pred&) : copied(true) {} + bool operator()(int) const; +}; + +constexpr bool test() { + { + std::ranges::drop_while_view dwv = {View{{}, MoveOnly{5}}, Pred{}}; + assert(dwv.pred().moved); + assert(!dwv.pred().copied); + assert(std::move(dwv).base().mo.get() == 5); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/end.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include +#include +#include + +#include "test_iterators.h" + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +// Test that end is not const +template +concept HasEnd = requires(T t) { t.end(); }; + +struct Pred { + constexpr bool operator()(int i) const { return i < 3; } +}; + +static_assert(HasEnd>); +static_assert(!HasEnd>); + +constexpr bool test() { + // return iterator + { + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + std::ranges::drop_while_view dwv(buffer, Pred{}); + std::same_as decltype(auto) st = dwv.end(); + assert(st == buffer + 11); + } + + // return sentinel + { + using Iter = int*; + using Sent = sentinel_wrapper; + using Range = std::ranges::subrange; + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + Range range = {buffer, Sent{buffer + 11}}; + std::ranges::drop_while_view dwv(range, Pred{}); + std::same_as decltype(auto) st = dwv.end(); + assert(base(st) == buffer + 11); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/general.pass.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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 drop_while_view might be used in the wild. This is a general +// collection of sample algorithms and functions that try to mock general usage of +// this view. + +#include +#include +#include +#include + +int main(int, char**) { + using namespace std::string_view_literals; + std::string_view source = " \t \t \t hello there"sv; + auto is_invisible = [](const auto x) { return x == ' ' || x == '\t'; }; + auto skip_ws = std::views::drop_while(source, is_invisible); + assert(std::ranges::equal(skip_ws, "hello there"sv)); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/pred.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/pred.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// 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 const Pred& pred() const; + +#include +#include +#include +#include + +struct View : std::ranges::view_interface { + int* begin() const; + int* end() const; +}; + +struct Pred { + int i; + bool operator()(int) const; +}; + +constexpr bool test() { + // & + { + std::ranges::drop_while_view dwv{{}, Pred{5}}; + decltype(auto) x = dwv.pred(); + static_assert(std::same_as); + assert(x.i == 5); + } + + // const & + { + const std::ranges::drop_while_view dwv{{}, Pred{5}}; + decltype(auto) x = dwv.pred(); + static_assert(std::same_as); + assert(x.i == 5); + } + + // && + { + std::ranges::drop_while_view dwv{{}, Pred{5}}; + decltype(auto) x = std::move(dwv).pred(); + static_assert(std::same_as); + assert(x.i == 5); + } + + // const && + { + const std::ranges::drop_while_view dwv{{}, Pred{5}}; + decltype(auto) x = std::move(dwv).pred(); + static_assert(std::same_as); + assert(x.i == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/range.concept.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/range.concept.compile.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 + +// concept checking +// template +// requires input_range && is_object_v && +// indirect_unary_predicate> +// class drop_while_view; + +#include +#include + +#include "test_iterators.h" + +template +using Range = std::ranges::subrange>; + +template +struct Pred { + bool operator()(const Val&) const; +}; + +template +concept HasDropWhileView = requires { typename std::ranges::drop_while_view; }; + +static_assert(HasDropWhileView, bool (*)(int)>); +static_assert(HasDropWhileView, Pred>); + +// !view +static_assert(!HasDropWhileView, Pred>); + +// !input_range +static_assert(!HasDropWhileView>, bool (*)(int)>); + +// !is_object_v +static_assert(!HasDropWhileView, Pred&>); + +// !indirect_unary_predicate> +static_assert(!HasDropWhileView, int>); +static_assert(!HasDropWhileView, Pred>);