Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[libc++][ranges] implement
std::ranges::drop_while_view
Differential Revision: https://reviews.llvm.org/D135460
- Loading branch information
Showing
18 changed files
with
984 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
126 changes: 126 additions & 0 deletions
126
libcxx/test/std/ranges/range.adaptors/range.drop.while/adaptor.pass.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Oops, something went wrong.