Skip to content

Commit

Permalink
Implement ranges::join_view
Browse files Browse the repository at this point in the history
Addresses microsoft#39
  • Loading branch information
miscco committed Feb 18, 2021
1 parent 7f08bb3 commit 92bf126
Show file tree
Hide file tree
Showing 5 changed files with 774 additions and 0 deletions.
346 changes: 346 additions & 0 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -2371,6 +2371,352 @@ namespace ranges {
inline constexpr _Common_fn common;
} // namespace views

// CLASS TEMPLATE ranges::join_view
// clang-format off
template <input_range _Vw>
requires view<_Vw> && input_range<range_reference_t<_Vw>>
&& (is_reference_v<range_reference_t<_Vw>> || view<range_value_t<_Vw>>)
class join_view : public view_interface<join_view<_Vw>> {
// clang-format on
private:
using _InnerRng = range_reference_t<_Vw>;
/* [[no_unique_address]] */ _Vw _Range{};
/* [[no_unique_address]] */ views::all_t<_InnerRng> _Inner{};

template <bool _Const>
class _Sentinel;

template <class _OuterTraits, class _InnerTraits, bool _Deref_is_ref> // TRANSITION, LWG-3289
struct _Category_base {};

// clang-format off
template <class _OuterTraits, class _InnerTraits, bool _Deref_is_ref>
requires _Has_member_iterator_category<_InnerTraits> && _Has_member_iterator_category<_OuterTraits>
struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_ref> {
using iterator_category =
conditional_t<_Deref_is_ref
&& derived_from<typename _OuterTraits::iterator_category, bidirectional_iterator_tag>
&& derived_from<typename _InnerTraits::iterator_category, bidirectional_iterator_tag>,
bidirectional_iterator_tag,
conditional_t<_Deref_is_ref
&& derived_from<typename _OuterTraits::iterator_category, forward_iterator_tag>
&& derived_from<typename _InnerTraits::iterator_category, forward_iterator_tag>,
forward_iterator_tag,
conditional_t<derived_from<typename _OuterTraits::iterator_category, input_iterator_tag>
&& derived_from<typename _InnerTraits::iterator_category, input_iterator_tag>,
input_iterator_tag,
output_iterator_tag>>>;
};
// clang-format on

template <bool _Const>
class _Iterator : public _Category_base<iterator_traits<iterator_t<_Maybe_const<_Const, _Vw>>>,
iterator_traits<iterator_t<range_reference_t<_Maybe_const<_Const, _Vw>>>>,
is_reference_v<range_reference_t<_Maybe_const<_Const, _Vw>>>> {
private:
template <bool>
friend class _Iterator;
template <bool>
friend class _Sentinel;

using _Parent_t = _Maybe_const<_Const, join_view>;
using _Base = _Maybe_const<_Const, _Vw>;
using _SubRng = range_reference_t<_Base>;
using _OuterIter = iterator_t<_Base>;
using _InnerIter = iterator_t<_SubRng>;

static constexpr bool _Deref_is_ref = is_reference_v<_SubRng>;

_OuterIter _Outer{};
_InnerIter _Inner{};
_Parent_t* _Parent{};

constexpr void _Satisfy() {
auto _Update_inner = [this](_SubRng _Range) -> auto& {
if constexpr (_Deref_is_ref) {
return _Range;
} else {
return (_Parent->_Inner = views::all(_STD move(_Range)));
}
};

for (; _Outer != _RANGES end(_Parent->_Range); ++_Outer) {
auto& _Tmp = _Update_inner(*_Outer);
_Inner = _RANGES begin(_Tmp);
if (_Inner != _RANGES end(_Tmp)) {
return;
}
}
if constexpr (_Deref_is_ref) {
_Inner = _InnerIter{};
}
}

#if _ITERATOR_DEBUG_LEVEL != 0
constexpr void _Check_dereference() const noexcept {
_STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator");
_STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator");
_STL_VERIFY(_Inner != _RANGES end(_Parent->_Inner), "cannot dereference end join_view iterator");
}
#endif // _ITERATOR_DEBUG_LEVEL != 0

#if _ITERATOR_DEBUG_LEVEL != 0
constexpr void _Same_range(const _Iterator& _Right) const noexcept {
_STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible join_view iterators");
}
#endif // _ITERATOR_DEBUG_LEVEL != 0

public:
using iterator_concept =
conditional_t<_Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng>,
bidirectional_iterator_tag,
conditional_t<_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>, forward_iterator_tag,
input_iterator_tag>>;
using value_type = range_value_t<_SubRng>;
using difference_type = common_type<range_difference_t<_Base>, range_difference_t<_SubRng>>;

_Iterator() = default;

constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_)
: _Outer{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} {
#if _ITERATOR_DEBUG_LEVEL != 0
_Adl_verify_range(_Outer, _RANGES end(_Parent_._Range));
if constexpr (forward_range<_Base>) {
_Adl_verify_range(_RANGES begin(_Parent_._Range), _Outer);
}
#endif // _ITERATOR_DEBUG_LEVEL != 0
_Satisfy();
}

// clang-format off
constexpr _Iterator(_Iterator<!_Const> _It)
requires _Const && convertible_to<iterator_t<_Vw>, _OuterIter>
&& convertible_to<iterator_t<_InnerRng>, _InnerIter>
: _Outer{_STD move(_It._Outer)}, _Inner{_STD move(_It._Inner)}, _Parent{_It._Parent} {}
// clang-format on

_NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(*_Inner)) /* strengthened */ {
#if _ITERATOR_DEBUG_LEVEL != 0
_Check_dereference();
#endif // _ITERATOR_DEBUG_LEVEL != 0
return *_Inner;
}

_NODISCARD constexpr _InnerIter operator->() const // Per resolution of LWG-XXXX
noexcept(is_nothrow_copy_constructible_v<_InnerIter>) /* strengthened */
requires _Has_arrow<_InnerIter>&& copyable<_InnerIter> {
#if _ITERATOR_DEBUG_LEVEL != 0
_Check_dereference();
#endif // _ITERATOR_DEBUG_LEVEL != 0
return _Inner;
}

constexpr _Iterator& operator++() {
if constexpr (_Deref_is_ref) {
if (++_Inner == _RANGES end(*_Outer)) {
++_Outer;
_Satisfy();
}
return *this;
} else {
if (++_Inner == _RANGES end(_Parent->_Inner)) {
++_Outer;
_Satisfy();
}
return *this;
}
}

constexpr void operator++(int) {
++*this;
}

// clang-format off
constexpr decltype(auto) operator++(int)
requires _Deref_is_ref && forward_range<_Base>&& forward_range<_SubRng> {
// clang-format on
auto _Tmp = *this;
++*this;
return _Tmp;
}

// clang-format off
constexpr _Iterator& operator--()
requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng>
&& common_range<_SubRng> {
// clang-format on
if (_Outer == _RANGES end(_Parent->_Range)) {
_Inner = _RANGES end(*--_Outer);
}
while (_Inner == _RANGES begin(*_Outer)) {
_Inner = _RANGES end(*--_Outer);
}
--_Inner;
return *this;
}

// clang-format off
constexpr _Iterator& operator--(int)
requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng>
&& common_range<_SubRng> {
// clang-format on
auto _Tmp = *this;
--*this;
return _Tmp;
}

// clang-format off
_NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept(
noexcept(_Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner)) /* strengthened */
requires _Deref_is_ref && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> {
// clang-format on
#if _ITERATOR_DEBUG_LEVEL != 0
_Left._Same_range(_Right);
#endif // _ITERATOR_DEBUG_LEVEL != 0
return _Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner;
}

_NODISCARD friend constexpr decltype(auto) iter_move(const _Iterator& _It) noexcept(
noexcept(_RANGES iter_move(_It._Inner))) {
#if _ITERATOR_DEBUG_LEVEL != 0
_It._Check_dereference();
#endif // _ITERATOR_DEBUG_LEVEL != 0
return _RANGES iter_move(_It._Inner);
}

friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept(
noexcept(ranges::iter_swap(_Left._Inner, _Right._Inner))) requires indirectly_swappable<_InnerIter> {
#if _ITERATOR_DEBUG_LEVEL != 0
_Left._Check_dereference();
_Right._Check_dereference();
#endif // _ITERATOR_DEBUG_LEVEL != 0
_RANGES iter_swap(_Left._Inner, _Right._Inner);
}
};

template <bool _Const>
class _Sentinel {
private:
template <bool>
friend class _Iterator;
template <bool>
friend class _Sentinel;

using _Parent_t = _Maybe_const<_Const, join_view>;
using _Base = _Maybe_const<_Const, _Vw>;

template <bool _OtherConst>
using _Maybe_const_iter = iterator_t<_Maybe_const<_OtherConst, _Vw>>;

sentinel_t<_Base> _Last{};

template <bool _OtherConst>
_NODISCARD static constexpr const _Maybe_const_iter<_OtherConst>& _Get_current(
const _Iterator<_OtherConst>& _It) noexcept {
return _It._Current;
}

public:
_Sentinel() = default;
// clang-format off
constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept(noexcept(_RANGES end(_STD declval<_Base>()))
&& is_nothrow_move_constructible_v<sentinel_t<_Base>>) // strengthened
: _Last(_RANGES end(_Parent._Range)) {}

constexpr _Sentinel(_Sentinel<!_Const> _Se)
noexcept(is_nothrow_constructible_v<sentinel_t<_Base>, sentinel_t<_Vw>>) // strengthened
requires _Const && convertible_to<sentinel_t<_Vw>, sentinel_t<_Base>>
: _Last(_STD move(_Se._Last)) {}

template <bool _OtherConst>
requires sentinel_for<sentinel_t<_Base>, _Maybe_const_iter<_OtherConst>>
_NODISCARD friend constexpr bool operator==(const _Iterator<_OtherConst>& _Left,
const _Sentinel& _Right) noexcept(noexcept(_Get_current(_Right) == _Last)) /* strengthened */ {
// clang-format on
return _Get_current(_Left) == _Right._Last;
}
};

public:
join_view() = default;
constexpr explicit join_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened
: _Range(_STD move(_Range_)) {}

_NODISCARD constexpr _Vw base() const& noexcept(
is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> {
return _Range;
}
_NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ {
return _STD move(_Range);
}

_NODISCARD constexpr _Iterator<false> begin() noexcept(
noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v<iterator_t<_Vw>>) /* strengthened */ {
return _Iterator<false>{*this, _RANGES begin(_Range)};
}

_NODISCARD constexpr _Iterator<true> begin() noexcept(
noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v<iterator_t<_Vw>>) /* strengthened */
requires _Simple_view<_Vw>&& is_reference_v<range_reference_t<_Vw>> {
return _Iterator<true>{*this, _RANGES begin(_Range)};
}

// clang-format off
_NODISCARD constexpr _Iterator<true> begin() const noexcept(noexcept(
_RANGES begin(_Range)) && is_nothrow_move_constructible_v<iterator_t<const _Vw>>) /* strengthened */
requires input_range<const _Vw> && is_reference_v<range_reference_t<const _Vw>> {
// clang-format on
return _Iterator<true>{*this, _RANGES begin(_Range)};
}

// clang-format off
_NODISCARD constexpr auto end() noexcept(noexcept(
_RANGES end(_Range)) && is_nothrow_move_constructible_v<sentinel_t<_Vw>>) /* strengthened */ {
if constexpr (forward_range<_Vw> && is_reference_v<range_reference_t<_Vw>>
&& forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) {
// clang-format on
return _Iterator<_Simple_view<_Vw>>{*this, _RANGES end(_Range)};
} else {
return _Sentinel<_Simple_view<_Vw>>{*this};
}
}

// clang-format off
_NODISCARD constexpr auto end() const noexcept(noexcept(
_RANGES end(_Range)) && is_nothrow_move_constructible_v<sentinel_t<_Vw>>) /* strengthened */
requires input_range<const _Vw> && is_reference_v<range_reference_t<const _Vw>> {
if constexpr (forward_range<const _Vw> && is_reference_v<range_reference_t<const _Vw>>
&& forward_range<range_reference_t<const _Vw>> && common_range<const _Vw>
&& common_range<range_reference_t<const _Vw>>) {
// clang-format on
return _Iterator<true>{*this, _RANGES end(_Range)};
} else {
return _Sentinel<true>{*this};
}
}
};

template <class _Rng>
join_view(_Rng &&) -> join_view<views::all_t<_Rng>>;

namespace views {
// VARIABLE views::join
class _Join_fn : public _Pipe::_Base<_Join_fn> {
public:
// clang-format off
template <viewable_range _Rng>
_NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept(
join_view<views::all_t<_Rng>>{_STD forward<_Rng>(_Range)})) requires requires {
join_view<views::all_t<_Rng>>{static_cast<_Rng&&>(_Range)};
} {
// clang-format on
return join_view<views::all_t<_Rng>>{_STD forward<_Rng>(_Range)};
}
};

inline constexpr _Join_fn join;
} // namespace views

// CLASS TEMPLATE ranges::reverse_view
// clang-format off
template <view _Vw>
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ tests\P0896R4_views_elements
tests\P0896R4_views_empty
tests\P0896R4_views_filter
tests\P0896R4_views_filter_death
tests\P0896R4_views_join
tests\P0896R4_views_reverse
tests\P0896R4_views_single
tests\P0896R4_views_take
Expand Down
1 change: 1 addition & 0 deletions tests/std/tests/P0896R4_ranges_range_machinery/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ STATIC_ASSERT(test_cpo(ranges::views::drop));
STATIC_ASSERT(test_cpo(ranges::views::drop_while));
STATIC_ASSERT(test_cpo(ranges::views::elements<42>));
STATIC_ASSERT(test_cpo(ranges::views::filter));
STATIC_ASSERT(test_cpo(ranges::views::join));
STATIC_ASSERT(test_cpo(ranges::views::keys));
STATIC_ASSERT(test_cpo(ranges::views::reverse));
STATIC_ASSERT(test_cpo(ranges::views::single));
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_views_join/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_matrix.lst
Loading

0 comments on commit 92bf126

Please sign in to comment.