Skip to content

Commit

Permalink
[libc++][ranges]implement std::views::take_while
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D134952
  • Loading branch information
huixie90 committed Oct 9, 2022
1 parent b72a364 commit a2c6a11
Show file tree
Hide file tree
Showing 22 changed files with 1,443 additions and 4 deletions.
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Papers.csv
Original file line number Diff line number Diff line change
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","Cologne","|In Progress|",""
"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors, Todo: elements_view and drop_while_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
6 changes: 3 additions & 3 deletions libcxx/docs/Status/Cxx2bIssues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"`3435 <https://wg21.link/LWG3435>`__","``three_way_comparable_with<reverse_iterator<int*>, reverse_iterator<const int*>>``","November 2020","|Complete|","13.0"
"`3432 <https://wg21.link/LWG3432>`__","Missing requirement for ``comparison_category``","November 2020","|Complete|","16.0","|spaceship|"
"`3447 <https://wg21.link/LWG3447>`__","Deduction guides for ``take_view`` and ``drop_view`` have different constraints","November 2020","|Complete|","14.0"
"`3450 <https://wg21.link/LWG3450>`__","The const overloads of ``take_while_view::begin/end`` are underconstrained","November 2020","","","|ranges|"
"`3450 <https://wg21.link/LWG3450>`__","The const overloads of ``take_while_view::begin/end`` are underconstrained","November 2020","|Complete|","16.0","|ranges|"
"`3464 <https://wg21.link/LWG3464>`__","``istream::gcount()`` can overflow","November 2020","",""
"`2731 <https://wg21.link/LWG2731>`__","Existence of ``lock_guard<MutexTypes...>::mutex_type`` typedef unclear","November 2020","|Complete|","5.0"
"`2743 <https://wg21.link/LWG2743>`__","P0083R3 ``node_handle`` private members missing ""exposition only"" comment","November 2020","|Nothing To Do|",""
Expand All @@ -34,7 +34,7 @@
"`3437 <https://wg21.link/LWG3437>`__","``__cpp_lib_polymorphic_allocator`` is in the wrong header","November 2020","|Complete|","14.0"
"`3446 <https://wg21.link/LWG3446>`__","``indirectly_readable_traits`` ambiguity for types with both ``value_type`` and ``element_type``","November 2020","|Complete|","14.0","|ranges|"
"`3448 <https://wg21.link/LWG3448>`__","``transform_view``'s ``sentinel<false>`` not comparable with ``iterator<true>``","November 2020","","","|ranges|"
"`3449 <https://wg21.link/LWG3449>`__","``take_view`` and ``take_while_view``'s ``sentinel<false>`` not comparable with their ``const iterator``","November 2020","","","|ranges|"
"`3449 <https://wg21.link/LWG3449>`__","``take_view`` and ``take_while_view``'s ``sentinel<false>`` not comparable with their ``const iterator``","November 2020","Complete","16.0","|ranges|"
"`3453 <https://wg21.link/LWG3453>`__","Generic code cannot call ``ranges::advance(i, s)``","November 2020","|Nothing To Do|","","|ranges|"
"`3455 <https://wg21.link/LWG3455>`__","Incorrect Postconditions on ``unique_ptr`` move assignment","November 2020","|Nothing To Do|",""
"`3460 <https://wg21.link/LWG3460>`__","Unimplementable ``noop_coroutine_handle`` guarantees","November 2020","|Complete|","14.0"
Expand Down Expand Up @@ -175,7 +175,7 @@
"`3704 <https://wg21.link/LWG3704>`__","LWG 2059 added overloads that might be ill-formed for sets","July 2022","",""
"`3705 <https://wg21.link/LWG3705>`__","Hashability shouldn't depend on basic_string's allocator","July 2022","|Complete|","16.0"
"`3707 <https://wg21.link/LWG3707>`__","chunk_view::outer-iterator::value_type::size should return unsigned type","July 2022","","","|ranges|"
"`3708 <https://wg21.link/LWG3708>`__","``take_while_view::sentinel``'s conversion constructor should move","July 2022","","","|ranges|"
"`3708 <https://wg21.link/LWG3708>`__","``take_while_view::sentinel``'s conversion constructor should move","July 2022","Complete","16.0","|ranges|"
"`3709 <https://wg21.link/LWG3709>`__","LWG-3703 was underly ambitious","July 2022","",""
"`3710 <https://wg21.link/LWG3710>`__","The ``end`` of ``chunk_view`` for input ranges can be ``const``","July 2022","","","|ranges|"
"`3711 <https://wg21.link/LWG3711>`__","Missing preconditions for slide_view constructor","July 2022","","","|ranges|"
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ set(files
__ranges/size.h
__ranges/subrange.h
__ranges/take_view.h
__ranges/take_while_view.h
__ranges/transform_view.h
__ranges/view_interface.h
__ranges/views.h
Expand Down
177 changes: 177 additions & 0 deletions libcxx/include/__ranges/take_while_view.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// -*- 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_TAKE_WHILE_VIEW_H
#define _LIBCPP___RANGES_TAKE_WHILE_VIEW_H

#include <__concepts/constructible.h>
#include <__concepts/convertible_to.h>
#include <__config>
#include <__functional/bind_back.h>
#include <__functional/invoke.h>
#include <__iterator/concepts.h>
#include <__memory/addressof.h>
#include <__ranges/access.h>
#include <__ranges/all.h>
#include <__ranges/concepts.h>
#include <__ranges/copyable_box.h>
#include <__ranges/range_adaptor.h>
#include <__ranges/view_interface.h>
#include <__type_traits/decay.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_object.h>
#include <__type_traits/maybe_const.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 {

// The spec uses the unnamed requirement inside the `begin` and `end` member functions:
// constexpr auto begin() const
// requires range<const V> && indirect_unary_predicate<const Pred, iterator_t<const V>>
// However, due to a clang-14 and clang-15 bug, the above produces a hard error when `const V` is not a range.
// The workaround is to create a named concept and use the concept instead.
// As of take_while_view is implemented, the clang-trunk has already fixed the bug.
// It is OK to remove the workaround once our CI no longer uses clang-14, clang-15 based compilers,
// because we don't actually expect a lot of vendors to ship a new libc++ with an old clang.
template <class _View, class _Pred>
concept __take_while_const_is_range =
range<const _View> && indirect_unary_predicate<const _Pred, iterator_t<const _View>>;

template <view _View, class _Pred>
requires input_range<_View> && is_object_v<_Pred> && indirect_unary_predicate<const _Pred, iterator_t<_View>>
class take_while_view : public view_interface<take_while_view<_View, _Pred>> {
template <bool>
class __sentinel;

_LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
_LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_;

public:
_LIBCPP_HIDE_FROM_ABI take_while_view()
requires default_initializable<_View> && default_initializable<_Pred>
= default;

_LIBCPP_HIDE_FROM_ABI constexpr take_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()
requires(!__simple_view<_View>)
{
return ranges::begin(__base_);
}

_LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
requires __take_while_const_is_range<_View, _Pred>
{
return ranges::begin(__base_);
}

_LIBCPP_HIDE_FROM_ABI constexpr auto end()
requires(!__simple_view<_View>)
{
return __sentinel</*_Const=*/false>(ranges::end(__base_), std::addressof(*__pred_));
}

_LIBCPP_HIDE_FROM_ABI constexpr auto end() const
requires __take_while_const_is_range<_View, _Pred>
{
return __sentinel</*_Const=*/true>(ranges::end(__base_), std::addressof(*__pred_));
}
};

template <class _Range, class _Pred>
take_while_view(_Range&&, _Pred) -> take_while_view<views::all_t<_Range>, _Pred>;

template <view _View, class _Pred>
requires input_range<_View> && is_object_v<_Pred> && indirect_unary_predicate<const _Pred, iterator_t<_View>>
template <bool _Const>
class take_while_view<_View, _Pred>::__sentinel {
using _Base = __maybe_const<_Const, _View>;

sentinel_t<_Base> __end_ = sentinel_t<_Base>();
const _Pred* __pred_ = nullptr;

friend class __sentinel<!_Const>;

public:
_LIBCPP_HIDE_FROM_ABI __sentinel() = default;

_LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
: __end_(std::move(__end)), __pred_(__pred) {}

_LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
: __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}

_LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; }

_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const iterator_t<_Base>& __x, const __sentinel& __y) {
return __x == __y.__end_ || !std::invoke(*__y.__pred_, *__x);
}

template <bool _OtherConst = !_Const>
requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
_LIBCPP_HIDE_FROM_ABI friend constexpr bool
operator==(const iterator_t<__maybe_const<_OtherConst, _View>>& __x, const __sentinel& __y) {
return __x == __y.__end_ || !std::invoke(*__y.__pred_, *__x);
}
};

namespace views {
namespace __take_while {

struct __fn {
template <class _Range, class _Pred>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pred&& __pred) const
noexcept(noexcept(/**/ take_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))))
-> decltype(/*--*/ take_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))) {
return /*-------------*/ take_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 __take_while

inline namespace __cpo {
inline constexpr auto take_while = __take_while::__fn{};
} // namespace __cpo
} // namespace views
} // namespace ranges

#endif // _LIBCPP_STD_VER >= 20

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___RANGES_TAKE_WHILE_VIEW_H
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap.in
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,7 @@ module std [system] {
module size { private header "__ranges/size.h" }
module subrange { private header "__ranges/subrange.h" }
module take_view { private header "__ranges/take_view.h" }
module take_while_view { private header "__ranges/take_while_view.h" }
module transform_view {
private header "__ranges/transform_view.h"
export functional.__functional.bind_back
Expand Down
9 changes: 9 additions & 0 deletions libcxx/include/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ namespace std::ranges {
template<class T>
inline constexpr bool enable_borrowed_range<take_view<T>> = enable_borrowed_range<T>;
// [range.take.while], take while view
template<view V, class Pred>
requires input_range<V> && is_object_v<Pred> &&
indirect_unary_predicate<const Pred, iterator_t<V>>
class take_while_view;
namespace views { inline constexpr unspecified take_while = unspecified; }
template<copy_constructible T>
requires is_object_v<T>
class single_view;
Expand Down Expand Up @@ -311,6 +319,7 @@ namespace std {
#include <__ranges/size.h>
#include <__ranges/subrange.h>
#include <__ranges/take_view.h>
#include <__ranges/take_while_view.h>
#include <__ranges/transform_view.h>
#include <__ranges/view_interface.h>
#include <__ranges/views.h>
Expand Down
1 change: 1 addition & 0 deletions libcxx/test/libcxx/private_headers.verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ END-SCRIPT
#include <__ranges/size.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/size.h'}}
#include <__ranges/subrange.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/subrange.h'}}
#include <__ranges/take_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/take_view.h'}}
#include <__ranges/take_while_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/take_while_view.h'}}
#include <__ranges/transform_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/transform_view.h'}}
#include <__ranges/view_interface.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/view_interface.h'}}
#include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//===----------------------------------------------------------------------===//
//
// 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::take_while

#include <algorithm>
#include <cassert>
#include <ranges>
#include <type_traits>
#include <utility>

#include "types.h"

struct Pred {
constexpr bool operator()(int i) const { return i < 3; }
};

struct Foo {};

struct MoveOnlyView : IntBufferViewBase {
using IntBufferViewBase::IntBufferViewBase;
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::take_while))>);
static_assert(std::is_invocable_v<decltype((std::views::take_while)), int>);
static_assert(std::is_invocable_v<decltype((std::views::take_while)), Pred>);
static_assert(!std::is_invocable_v<decltype((std::views::take_while)), int, Pred>);
static_assert(std::is_invocable_v<decltype((std::views::take_while)), int (&)[2], Pred>);
static_assert(!std::is_invocable_v<decltype((std::views::take_while)), Foo (&)[2], Pred>);
static_assert(std::is_invocable_v<decltype((std::views::take_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::take_while)>);
static_assert(CanBePiped<MoveOnlyView, decltype(std::views::take_while(Pred{}))>);
static_assert(!CanBePiped<int, decltype(std::views::take_while(Pred{}))>);
static_assert(CanBePiped<int (&)[2], decltype(std::views::take_while(Pred{}))>);
static_assert(!CanBePiped<Foo (&)[2], decltype(std::views::take_while(Pred{}))>);

constexpr bool test() {
int buff[] = {1, 2, 3, 4, 3, 2, 1};

// Test `views::take_while(p)(v)`
{
using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
std::same_as<Result> decltype(auto) result = std::views::take_while(Pred{})(MoveOnlyView{buff});
auto expected = {1, 2};
assert(std::ranges::equal(result, expected));
}
{
auto const partial = std::views::take_while(Pred{});
using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
std::same_as<Result> decltype(auto) result = partial(MoveOnlyView{buff});
auto expected = {1, 2};
assert(std::ranges::equal(result, expected));
}

// Test `v | views::take_while(p)`
{
using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
std::same_as<Result> decltype(auto) result = MoveOnlyView{buff} | std::views::take_while(Pred{});
auto expected = {1, 2};
assert(std::ranges::equal(result, expected));
}
{
auto const partial = std::views::take_while(Pred{});
using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
std::same_as<Result> decltype(auto) result = MoveOnlyView{buff} | partial;
auto expected = {1, 2};
assert(std::ranges::equal(result, expected));
}

// Test `views::take_while(v, p)`
{
using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
std::same_as<Result> decltype(auto) result = std::views::take_while(MoveOnlyView{buff}, Pred{});
auto expected = {1, 2};
assert(std::ranges::equal(result, expected));
}

// Test adaptor | adaptor
{
struct Pred2 {
constexpr bool operator()(int i) const { return i < 2; }
};
auto const partial = std::views::take_while(Pred{}) | std::views::take_while(Pred2{});
using Result = std::ranges::take_while_view<std::ranges::take_while_view<MoveOnlyView, Pred>, Pred2>;
std::same_as<Result> decltype(auto) result = MoveOnlyView{buff} | partial;
auto expected = {1};
assert(std::ranges::equal(result, expected));
}
return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Loading

0 comments on commit a2c6a11

Please sign in to comment.