[libcxx-commits] [libcxx] 594fa14 - [libc++][ranges] implement `std::ranges::drop_while_view`
Hui Xie via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Oct 18 11:36:32 PDT 2022
Author: Hui Xie
Date: 2022-10-18T19:34:47+01:00
New Revision: 594fa1474f0c96da864257c0cda31b9b8381cd15
URL: https://github.com/llvm/llvm-project/commit/594fa1474f0c96da864257c0cda31b9b8381cd15
DIFF: https://github.com/llvm/llvm-project/commit/594fa1474f0c96da864257c0cda31b9b8381cd15.diff
LOG: [libc++][ranges] implement `std::ranges::drop_while_view`
Differential Revision: https://reviews.llvm.org/D135460
Added:
libcxx/include/__ranges/drop_while_view.h
libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/adaptor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/ctad.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/end.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/general.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/pred.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop.while/range.concept.compile.pass.cpp
Modified:
libcxx/docs/Status/Cxx20Papers.csv
libcxx/include/CMakeLists.txt
libcxx/include/module.modulemap.in
libcxx/include/ranges
libcxx/test/libcxx/private_headers.verify.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index e606e41202b7d..b6ce160e36822 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -108,7 +108,7 @@
"`P0784R7 <https://wg21.link/P0784R7>`__","CWG","More constexpr containers","Cologne","|Complete|","12.0"
"`P0980R1 <https://wg21.link/P0980R1>`__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0"
"`P1004R2 <https://wg21.link/P1004R2>`__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0"
-"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors, Todo: elements_view and drop_while_view","Cologne","|In Progress|",""
+"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors, Todo: elements_view","Cologne","|In Progress|",""
"`P1065R2 <https://wg21.link/P1065R2>`__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0"
"`P1135R6 <https://wg21.link/P1135R6>`__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0"
"`P1207R4 <https://wg21.link/P1207R4>`__","LWG","Movability of Single-pass Iterators","Cologne","|Complete|","15.0"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4f95209e77816..c9f3d2afa147a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -481,6 +481,7 @@ set(files
__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
index 0000000000000..8cc52dbbfd7ad
--- /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 <view _View, class _Pred>
+ requires input_range<_View> && is_object_v<_Pred> && indirect_unary_predicate<const _Pred, iterator_t<_View>>
+class drop_while_view : public view_interface<drop_while_view<_View, _Pred>> {
+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<iterator_t<_View>>, __empty_cache>;
+ _LIBCPP_NO_UNIQUE_ADDRESS _Cache __cached_begin_ = _Cache();
+};
+
+template <class _View, class _Pred>
+inline constexpr bool enable_borrowed_range<drop_while_view<_View, _Pred>> = enable_borrowed_range<_View>;
+
+template <class _Range, class _Pred>
+drop_while_view(_Range&&, _Pred) -> drop_while_view<views::all_t<_Range>, _Pred>;
+
+namespace views {
+namespace __drop_while {
+
+struct __fn {
+ template <class _Range, class _Pred>
+ [[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 <class _Pred>
+ requires constructible_from<decay_t<_Pred>, _Pred>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pred&& __pred) const
+ noexcept(is_nothrow_constructible_v<decay_t<_Pred>, _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
index 2794edea565bf..ddae89593236f 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1120,6 +1120,7 @@ module std [system] {
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
index 9207298fb72df..643853e1d3abe 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -166,6 +166,18 @@ namespace std::ranges {
template<class T>
inline constexpr bool enable_borrowed_range<drop_view<T>> = enable_borrowed_range<T>;
+ // [range.drop.while], drop while view
+ template<view V, class Pred>
+ requires input_range<V> && is_object_v<Pred> &&
+ indirect_unary_predicate<const Pred, iterator_t<V>>
+ class drop_while_view;
+
+ template<class T, class Pred>
+ inline constexpr bool enable_borrowed_range<drop_while_view<T, Pred>> =
+ enable_borrowed_range<T>;
+
+ namespace views { inline constexpr unspecified drop_while = unspecified; }
+
// [range.transform], transform view
template<input_range V, copy_constructible F>
requires view<V> && is_object_v<F> &&
@@ -303,6 +315,7 @@ namespace std {
#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
index f30a848ae7fca..7e324a666b910 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -512,6 +512,7 @@ END-SCRIPT
#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
index 0000000000000..e9cd5747d3fbb
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.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
+//
+//===----------------------------------------------------------------------===//
+
+// <ranges>
+
+// Call begin() on drop_while_view with empty predicate
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-exceptions
+// 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 <ranges>
+
+#include "check_assertion.h"
+
+struct Exception {};
+struct ThrowOnCopyPred {
+ ThrowOnCopyPred() = default;
+ ThrowOnCopyPred(const ThrowOnCopyPred&) { throw Exception{}; }
+ ThrowOnCopyPred& operator=(const ThrowOnCopyPred&) = delete;
+
+ ThrowOnCopyPred(ThrowOnCopyPred&&) = default;
+ ThrowOnCopyPred& operator=(ThrowOnCopyPred&&) = default;
+
+ bool operator()(int) const { return false; }
+};
+
+int main(int, char**) {
+ int input[] = {1, 2, 3};
+ auto v1 = std::views::drop_while(input, ThrowOnCopyPred{});
+ auto v2 = std::views::drop_while(input, ThrowOnCopyPred{});
+ 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
index 0000000000000..409b400f7f87f
--- /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 <algorithm>
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i < 3; }
+};
+
+struct Foo {};
+
+template <class T>
+struct BufferView : std::ranges::view_base {
+ T* buffer_;
+ std::size_t size_;
+
+ template <std::size_t N>
+ constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {}
+};
+
+using IntBufferView = BufferView<int>;
+
+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<decltype((std::views::drop_while))>);
+static_assert(std::is_invocable_v<decltype((std::views::drop_while)), int>);
+static_assert(std::is_invocable_v<decltype((std::views::drop_while)), Pred>);
+static_assert(!std::is_invocable_v<decltype((std::views::drop_while)), int, Pred>);
+static_assert(std::is_invocable_v<decltype((std::views::drop_while)), int (&)[2], Pred>);
+static_assert(!std::is_invocable_v<decltype((std::views::drop_while)), Foo (&)[2], Pred>);
+static_assert(std::is_invocable_v<decltype((std::views::drop_while)), MoveOnlyView, Pred>);
+
+template <class View, class T>
+concept CanBePiped =
+ requires(View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(t) };
+ };
+
+static_assert(!CanBePiped<MoveOnlyView, decltype(std::views::drop_while)>);
+static_assert(CanBePiped<MoveOnlyView, decltype(std::views::drop_while(Pred{}))>);
+static_assert(!CanBePiped<int, decltype(std::views::drop_while(Pred{}))>);
+static_assert(CanBePiped<int (&)[2], decltype(std::views::drop_while(Pred{}))>);
+static_assert(!CanBePiped<Foo (&)[2], decltype(std::views::drop_while(Pred{}))>);
+
+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<MoveOnlyView, Pred>;
+ std::same_as<Result> 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<MoveOnlyView, Pred>;
+ std::same_as<Result> 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<MoveOnlyView, Pred>;
+ std::same_as<Result> 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<MoveOnlyView, Pred>;
+ std::same_as<Result> 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<MoveOnlyView, Pred>;
+ std::same_as<Result> 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<std::ranges::drop_while_view<MoveOnlyView, Pred>, Pred2>;
+ std::same_as<Result> 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
index 0000000000000..7ac5e0aec9848
--- /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<V> { return base_; }
+// constexpr V base() && { return std::move(base_); }
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct View : std::ranges::view_base {
+ int i;
+ int* begin() const;
+ int* end() const;
+};
+
+struct MoveOnlyView : View {
+ MoveOnly mo;
+};
+
+template <class T>
+concept HasBase = requires(T&& t) { std::forward<T>(t).base(); };
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i > 5; }
+};
+
+static_assert(HasBase<std::ranges::drop_while_view<View, Pred> const&>);
+static_assert(HasBase<std::ranges::drop_while_view<View, Pred>&&>);
+
+static_assert(!HasBase<std::ranges::drop_while_view<MoveOnlyView, Pred> const&>);
+static_assert(HasBase<std::ranges::drop_while_view<MoveOnlyView, Pred>&&>);
+
+constexpr bool test() {
+ // const &
+ {
+ const std::ranges::drop_while_view<View, Pred> dwv{View{{}, 5}, {}};
+ std::same_as<View> decltype(auto) v = dwv.base();
+ assert(v.i == 5);
+ }
+
+ // &
+ {
+ std::ranges::drop_while_view<View, Pred> dwv{View{{}, 5}, {}};
+ std::same_as<View> decltype(auto) v = dwv.base();
+ assert(v.i == 5);
+ }
+
+ // &&
+ {
+ std::ranges::drop_while_view<View, Pred> dwv{View{{}, 5}, {}};
+ std::same_as<View> decltype(auto) v = std::move(dwv).base();
+ assert(v.i == 5);
+ }
+
+ // const &&
+ {
+ const std::ranges::drop_while_view<View, Pred> dwv{View{{}, 5}, {}};
+ std::same_as<View> decltype(auto) v = std::move(dwv).base();
+ assert(v.i == 5);
+ }
+
+ // move only
+ {
+ std::ranges::drop_while_view<MoveOnlyView, Pred> dwv{MoveOnlyView{{}, 5}, {}};
+ std::same_as<MoveOnlyView> 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
index 0000000000000..5c8ee5273f64c
--- /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 <array>
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+
+// Test that begin is not const
+template <class T>
+concept HasBegin = requires(T t) { t.begin(); };
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i < 3; }
+};
+
+static_assert(HasBegin<std::ranges::drop_while_view<View, Pred>>);
+static_assert(!HasBegin<const std::ranges::drop_while_view<View, Pred>>);
+
+constexpr auto always = [](auto v) { return [v](auto&&...) { return v; }; };
+
+template <class Iter>
+constexpr void testOne() {
+ using Sent = sentinel_wrapper<Iter>;
+ using Range = std::ranges::subrange<Iter, Sent>;
+ constexpr auto make_subrange = []<std::size_t N>(int(&buffer)[N]) {
+ return Range{Iter{buffer}, Sent{Iter{buffer + N}}};
+ };
+
+ // empty
+ {
+ std::array<int, 0> 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<Iter> 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<Iter> 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<Iter> 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<Iter> 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<Iter> 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<Iter> 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<Iter> 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<Iter> decltype(auto) it = dwv.begin();
+ assert(base(it) == buffer + 2);
+ }
+
+ if constexpr (std::forward_iterator<Iter>) {
+ // 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<Iter> decltype(auto) it = dwv.begin();
+ assert(base(it) == buffer + 2);
+ assert(called == 3);
+ }
+ }
+ }
+}
+
+constexpr bool test() {
+ testOne<cpp17_input_iterator<int*>>();
+ testOne<cpp20_input_iterator<int*>>();
+ testOne<forward_iterator<int*>>();
+ testOne<bidirectional_iterator<int*>>();
+ testOne<random_access_iterator<int*>>();
+ testOne<contiguous_iterator<int*>>();
+ testOne<int*>();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.pass.cpp
new file mode 100644
index 0000000000000..9956fd09648d6
--- /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<class T, class Pred>
+// inline constexpr bool enable_borrowed_range<drop_while_view<T, Pred>> =
+// enable_borrowed_range<T>;
+
+#include <ranges>
+
+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<Borrowed> = true;
+
+static_assert(!std::ranges::borrowed_range<std::ranges::drop_while_view<NonBorrowed, Pred>>);
+static_assert(std::ranges::borrowed_range<std::ranges::drop_while_view<Borrowed, Pred>>);
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
index 0000000000000..d148fd6ec271a
--- /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<class R, class Pred>
+// drop_while_view(R&&, Pred) -> drop_while_view<views::all_t<R>, Pred>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+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<decltype(std::ranges::drop_while_view(Container{}, Pred{})),
+ std::ranges::drop_while_view<std::ranges::owning_view<Container>, Pred>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::drop_while_view(View{}, pred)), //
+ std::ranges::drop_while_view<View, bool (*)(int)>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::drop_while_view(View{}, Pred{})), //
+ std::ranges::drop_while_view<View, Pred>>);
+
+void testRef() {
+ Container c{};
+ Pred p{};
+ static_assert(std::is_same_v<decltype(std::ranges::drop_while_view(c, p)),
+ std::ranges::drop_while_view<std::ranges::ref_view<Container>, 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
index 0000000000000..320bdfe5945db
--- /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<V> && default_initializable<Pred> = default;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+template <bool DefaultInitializable>
+struct View : std::ranges::view_base {
+ int i = 42;
+ constexpr explicit View()
+ requires DefaultInitializable
+ = default;
+ int* begin() const;
+ int* end() const;
+};
+
+template <bool DefaultInitializable>
+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<std::ranges::drop_while_view<View<true >, Pred<true >>>);
+static_assert(!std::is_default_constructible_v<std::ranges::drop_while_view<View<false>, Pred<true >>>);
+static_assert(!std::is_default_constructible_v<std::ranges::drop_while_view<View<true >, Pred<false>>>);
+static_assert(!std::is_default_constructible_v<std::ranges::drop_while_view<View<false>, Pred<false>>>);
+// clang-format on
+
+constexpr bool test() {
+ {
+ std::ranges::drop_while_view<View<true>, Pred<true>> 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
index 0000000000000..cf9f9dbca9a56
--- /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 <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#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<View, Pred> 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
index 0000000000000..cd246480279d1
--- /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 <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+
+// Test that end is not const
+template <class T>
+concept HasEnd = requires(T t) { t.end(); };
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i < 3; }
+};
+
+static_assert(HasEnd<std::ranges::drop_while_view<View, Pred>>);
+static_assert(!HasEnd<const std::ranges::drop_while_view<View, Pred>>);
+
+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<int*> decltype(auto) st = dwv.end();
+ assert(st == buffer + 11);
+ }
+
+ // return sentinel
+ {
+ using Iter = int*;
+ using Sent = sentinel_wrapper<Iter>;
+ using Range = std::ranges::subrange<Iter, Sent>;
+ 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<Sent> 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
index 0000000000000..3c13362476c6e
--- /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 <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+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
index 0000000000000..41ba8ee9fb0df
--- /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 <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+struct View : std::ranges::view_interface<View> {
+ int* begin() const;
+ int* end() const;
+};
+
+struct Pred {
+ int i;
+ bool operator()(int) const;
+};
+
+constexpr bool test() {
+ // &
+ {
+ std::ranges::drop_while_view<View, Pred> dwv{{}, Pred{5}};
+ decltype(auto) x = dwv.pred();
+ static_assert(std::same_as<decltype(x), Pred const&>);
+ assert(x.i == 5);
+ }
+
+ // const &
+ {
+ const std::ranges::drop_while_view<View, Pred> dwv{{}, Pred{5}};
+ decltype(auto) x = dwv.pred();
+ static_assert(std::same_as<decltype(x), Pred const&>);
+ assert(x.i == 5);
+ }
+
+ // &&
+ {
+ std::ranges::drop_while_view<View, Pred> dwv{{}, Pred{5}};
+ decltype(auto) x = std::move(dwv).pred();
+ static_assert(std::same_as<decltype(x), Pred const&>);
+ assert(x.i == 5);
+ }
+
+ // const &&
+ {
+ const std::ranges::drop_while_view<View, Pred> dwv{{}, Pred{5}};
+ decltype(auto) x = std::move(dwv).pred();
+ static_assert(std::same_as<decltype(x), Pred const&>);
+ 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
index 0000000000000..f522458ee6bf7
--- /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<view V, class Pred>
+// requires input_range<V> && is_object_v<Pred> &&
+// indirect_unary_predicate<const Pred, iterator_t<V>>
+// class drop_while_view;
+
+#include <array>
+#include <ranges>
+
+#include "test_iterators.h"
+
+template <class It>
+using Range = std::ranges::subrange<It, sentinel_wrapper<It>>;
+
+template <class Val = int>
+struct Pred {
+ bool operator()(const Val&) const;
+};
+
+template <class V, class Pred>
+concept HasDropWhileView = requires { typename std::ranges::drop_while_view<V, Pred>; };
+
+static_assert(HasDropWhileView<Range<int*>, bool (*)(int)>);
+static_assert(HasDropWhileView<Range<int*>, Pred<int>>);
+
+// !view<V>
+static_assert(!HasDropWhileView<std::array<int, 5>, Pred<int>>);
+
+// !input_range
+static_assert(!HasDropWhileView<Range<cpp20_output_iterator<int*>>, bool (*)(int)>);
+
+// !is_object_v<Pred>
+static_assert(!HasDropWhileView<Range<int*>, Pred<int>&>);
+
+// !indirect_unary_predicate<const Pred, iterator_t<V>>
+static_assert(!HasDropWhileView<Range<int*>, int>);
+static_assert(!HasDropWhileView<Range<int**>, Pred<int>>);
More information about the libcxx-commits
mailing list