Skip to content

Commit

Permalink
[libc++][ranges] implement std::ranges::drop_while_view
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D135460
  • Loading branch information
huixie90 committed Oct 18, 2022
1 parent 62267e8 commit 594fa14
Show file tree
Hide file tree
Showing 18 changed files with 984 additions and 1 deletion.
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Papers.csv
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Expand Up @@ -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
Expand Down
129 changes: 129 additions & 0 deletions 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
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap.in
Expand Up @@ -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" }
Expand Down
13 changes: 13 additions & 0 deletions libcxx/include/ranges
Expand Up @@ -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> &&
Expand Down Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions libcxx/test/libcxx/private_headers.verify.cpp
Expand Up @@ -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'}}
Expand Down
@@ -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;
}
@@ -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;
}

0 comments on commit 594fa14

Please sign in to comment.