/// \file // Range v3 library // // Copyright Eric Niebler 2013-present // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Project home: https://github.com/ericniebler/range-v3 // #ifndef RANGES_V3_VIEW_SPLIT_HPP #define RANGES_V3_VIEW_SPLIT_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { /// \addtogroup group-views /// @{ /// \cond namespace detail { // clang-format off CPP_def ( template(typename R) concept tiny_range, sized_range && ranges::type::size()), std::remove_reference_t::size()>> && (std::remove_reference_t::size() <= 1) ); // clang-format on } // namespace detail template #if CPP_CXX_CONCEPTS requires input_range && forward_range && view_ && view_< Pattern> && indirectly_comparable, iterator_t, ranges::equal_to> && (forward_range || detail::tiny_range) #endif struct split_view; namespace detail { struct there { template static decltype(auto) current_(T & t) noexcept { return (t.curr_); } }; template struct here { It curr_ = It(); It & current_(ignore_t) noexcept { return curr_; } It const & current_(ignore_t) const noexcept { return curr_; } }; template struct here_or_there_ { template using invoke = there; }; template<> struct here_or_there_ { template using invoke = here; }; template using split_view_base = meta::invoke>, It>; template struct split_outer_iterator; template struct split_inner_iterator; template struct split_inner_iterator, Const> { private: using Outer = split_outer_iterator, Const>; using Base = meta::const_if_c; using BaseIterCategory = typename std::iterator_traits>::iterator_category; Outer i_ = Outer(); bool incremented_ = false; constexpr decltype(auto) current_() noexcept { return i_.current_(); } constexpr decltype(auto) current_() const noexcept { return i_.current_(); } constexpr bool done_() const { auto cur = current_(); auto last = ranges::end(i_.parent_->base_); if(cur == last) return true; auto pcur = ranges::begin(i_.parent_->pattern_); auto pend = ranges::end(i_.parent_->pattern_); if(pcur == pend) return incremented_; do { if(*cur != *pcur) return false; if(++pcur == pend) return true; } while(++cur != last); return false; } #if RANGES_CXX_IF_CONSTEXPR < RANGES_CXX_IF_CONSTEXPR_17 constexpr void pre_inc(std::true_type) // Forward { ++current_(); } constexpr void pre_inc(std::false_type) // Input { if(Pattern::size() != 0) ++current_(); } constexpr split_inner_iterator post_inc(std::true_type) // Forward { auto tmp = *this; pre_inc(std::true_type{}); return tmp; } constexpr void post_inc(std::false_type) // Input { pre_inc(std::false_type{}); } #endif public: using iterator_concept = typename Outer::iterator_concept; using iterator_category = if_then_t, std::forward_iterator_tag, std::input_iterator_tag>; using value_type = range_value_t; using difference_type = range_difference_t; using reference = range_reference_t; // Not to spec using pointer = iter_pointer_t>; // Not to spec split_inner_iterator() = default; constexpr explicit split_inner_iterator(Outer i) : i_(std::move(i)) {} constexpr decltype(auto) operator*() const { return *current_(); } constexpr split_inner_iterator & operator++() { incremented_ = true; #if RANGES_CXX_IF_CONSTEXPR >= RANGES_CXX_IF_CONSTEXPR_17 if constexpr(!forward_range) if constexpr(Pattern::size() == 0) return *this; ++current_(); #else pre_inc(meta::bool_>{}); #endif return *this; } constexpr decltype(auto) operator++(int) { #if RANGES_CXX_IF_CONSTEXPR >= RANGES_CXX_IF_CONSTEXPR_17 if constexpr(forward_range) { auto tmp = *this; ++*this; return tmp; } else ++*this; #else return post_inc(meta::bool_>{}); #endif } CPP_broken_friend_member friend constexpr auto operator==(split_inner_iterator const & x, split_inner_iterator const & y) -> CPP_broken_friend_ret(bool)( // requires forward_range) { return x.i_.curr_ == y.i_.curr_; } CPP_broken_friend_member friend constexpr auto operator!=(split_inner_iterator const & x, split_inner_iterator const & y) -> CPP_broken_friend_ret(bool)( // requires forward_range) { return x.i_.curr_ != y.i_.curr_; } #ifdef RANGES_WORKAROUND_MSVC_756601 template #endif // RANGES_WORKAROUND_MSVC_756601 friend constexpr bool operator==(split_inner_iterator const & x, default_sentinel_t) { return x.done_(); } #ifdef RANGES_WORKAROUND_MSVC_756601 template #endif // RANGES_WORKAROUND_MSVC_756601 friend constexpr bool operator==(default_sentinel_t, split_inner_iterator const & x) { return x.done_(); } #ifdef RANGES_WORKAROUND_MSVC_756601 template #endif // RANGES_WORKAROUND_MSVC_756601 friend constexpr bool operator!=(split_inner_iterator const & x, default_sentinel_t) { return !x.done_(); } #ifdef RANGES_WORKAROUND_MSVC_756601 template #endif // RANGES_WORKAROUND_MSVC_756601 friend constexpr bool operator!=(default_sentinel_t, split_inner_iterator const & x) { return !x.done_(); } #ifdef RANGES_WORKAROUND_MSVC_756601 template #endif // RANGES_WORKAROUND_MSVC_756601 friend constexpr decltype(auto) iter_move( split_inner_iterator const & i) noexcept(noexcept(ranges::iter_move(i.current_()))) { return ranges::iter_move(i.current_()); } CPP_broken_friend_member friend constexpr auto iter_swap( split_inner_iterator const & x, split_inner_iterator const & y) noexcept(noexcept(ranges::iter_swap(x.current_(), y.current_()))) -> CPP_broken_friend_ret(void)( // requires indirectly_swappable>) { ranges::iter_swap(x.current_(), y.current_()); } }; template using split_outer_iterator_base = meta::invoke>, It>; template struct split_outer_iterator; template struct split_outer_iterator, Const> : split_outer_iterator_base>> { private: friend struct split_inner_iterator, Const>; using Parent = meta::const_if_c>; using Base = meta::const_if_c; using Current = split_outer_iterator_base>; Parent * parent_ = nullptr; constexpr decltype(auto) current_() noexcept { return parent_->current_(*this); } constexpr decltype(auto) current_() const noexcept { return parent_->current_(*this); } constexpr decltype(auto) base_() const noexcept { return (parent_->base_); } #if RANGES_CXX_IF_CONSTEXPR < RANGES_CXX_IF_CONSTEXPR_17 constexpr split_outer_iterator post_inc(std::true_type) // Forward { auto tmp = *this; ++*this; return tmp; } constexpr void post_inc(std::false_type) // Input { ++*this; } #endif public: using iterator_concept = if_then_t, std::forward_iterator_tag, std::input_iterator_tag>; using iterator_category = std::input_iterator_tag; struct value_type : view_interface { private: split_outer_iterator i_ = split_outer_iterator(); public: value_type() = default; constexpr explicit value_type(split_outer_iterator i) : i_(std::move(i)) {} constexpr split_inner_iterator, Const> begin() const { return split_inner_iterator, Const>(i_); } constexpr default_sentinel_t end() const { return default_sentinel; } }; using difference_type = range_difference_t; using reference = value_type; // Not to spec using pointer = value_type *; // Not to spec split_outer_iterator() = default; CPP_member constexpr explicit CPP_ctor(split_outer_iterator)(Parent & parent)( // requires(!forward_range)) : parent_(&parent) {} CPP_member constexpr CPP_ctor(split_outer_iterator)(Parent & parent, iterator_t current)( // requires forward_range) : Current{std::move(current)} , parent_(&parent) {} CPP_template(bool Other)( // requires Const && (!Other) && convertible_to, iterator_t>) // constexpr split_outer_iterator( split_outer_iterator, Other> i) : Current{std::move(i.curr_)} , parent_(i.parent_) {} constexpr value_type operator*() const { return value_type{*this}; } constexpr split_outer_iterator & operator++() { auto & current = current_(); const auto last = ranges::end(base_()); if(current == last) return *this; auto const pbegin = ranges::begin(parent_->pattern_); auto const pend = ranges::end(parent_->pattern_); if(pbegin == pend) ++current; else do { const auto ret = ranges::mismatch(current, last, pbegin, pend); if(ret.in2 == pend) { current = ret.in1; // The pattern matched; skip it break; } } while(++current != last); return *this; } constexpr decltype(auto) operator++(int) { #if RANGES_CXX_IF_CONSTEXPR >= RANGES_CXX_IF_CONSTEXPR_17 if constexpr(forward_range) { auto tmp = *this; ++*this; return tmp; } else ++*this; #else return post_inc(meta::bool_>{}); #endif } CPP_broken_friend_member friend constexpr auto operator==(split_outer_iterator const & x, split_outer_iterator const & y) -> CPP_broken_friend_ret(bool)( // requires forward_range) { return x.curr_ == y.curr_; } CPP_broken_friend_member friend constexpr auto operator!=(split_outer_iterator const & x, split_outer_iterator const & y) -> CPP_broken_friend_ret(bool)( // requires forward_range) { return x.curr_ != y.curr_; } #ifdef RANGES_WORKAROUND_MSVC_756601 template #endif // RANGES_WORKAROUND_MSVC_756601 friend constexpr bool operator==(split_outer_iterator const & x, default_sentinel_t) { return x.current_() == ranges::end(x.base_()); } #ifdef RANGES_WORKAROUND_MSVC_756601 template #endif // RANGES_WORKAROUND_MSVC_756601 friend constexpr bool operator==(default_sentinel_t, split_outer_iterator const & x) { return x.current_() == ranges::end(x.base_()); } #ifdef RANGES_WORKAROUND_MSVC_756601 template #endif // RANGES_WORKAROUND_MSVC_756601 friend constexpr bool operator!=(split_outer_iterator const & x, default_sentinel_t) { return x.current_() != ranges::end(x.base_()); } #ifdef RANGES_WORKAROUND_MSVC_756601 template #endif // RANGES_WORKAROUND_MSVC_756601 friend constexpr bool operator!=(default_sentinel_t, split_outer_iterator const & x) { return x.current_() != ranges::end(x.base_()); } }; } // namespace detail /// \endcond template #if CPP_CXX_CONCEPTS requires input_range && forward_range && view_ && view_< Pattern> && indirectly_comparable, iterator_t, ranges::equal_to> && (forward_range || detail::tiny_range) #endif struct RANGES_EMPTY_BASES split_view : view_interface, is_finite::value ? finite : unknown> , private detail::split_view_base> { private: template friend struct detail::split_outer_iterator; template friend struct detail::split_inner_iterator; V base_ = V(); Pattern pattern_ = Pattern(); template using outer_iterator = detail::split_outer_iterator; #if RANGES_CXX_IF_CONSTEXPR < RANGES_CXX_IF_CONSTEXPR_17 outer_iterator()> begin_(std::true_type) { return outer_iterator()>{*this, ranges::begin(base_)}; } outer_iterator begin_(std::false_type) { this->curr_ = ranges::begin(base_); return outer_iterator{*this}; } outer_iterator()> end_(std::true_type) const { return outer_iterator{*this, ranges::end(base_)}; } default_sentinel_t end_(std::false_type) const { return default_sentinel; } #endif public: split_view() = default; constexpr split_view(V base, Pattern pattern) : base_((V &&) base) , pattern_((Pattern &&) pattern) {} CPP_member constexpr CPP_ctor(split_view)(V base, range_value_t e)( requires constructible_from>) : base_(std::move(base)) , pattern_(e) {} constexpr V base() const { return base_; } constexpr auto begin() { #if RANGES_CXX_IF_CONSTEXPR >= RANGES_CXX_IF_CONSTEXPR_17 if constexpr(forward_range) return outer_iterator()>{*this, ranges::begin(base_)}; else { this->curr_ = ranges::begin(base_); return outer_iterator{*this}; } #else return begin_(meta::bool_>{}); #endif } CPP_member constexpr auto begin() const -> CPP_ret(outer_iterator)( // requires forward_range && forward_range) { return {*this, ranges::begin(base_)}; } CPP_member constexpr auto end() -> CPP_ret(outer_iterator()>)( // requires forward_range && common_range) { return outer_iterator()>{*this, ranges::end(base_)}; } constexpr auto end() const { #if RANGES_CXX_IF_CONSTEXPR >= RANGES_CXX_IF_CONSTEXPR_17 if constexpr(forward_range && forward_range && common_range) return outer_iterator{*this, ranges::end(base_)}; else return default_sentinel; #else return end_(meta::bool_ < forward_range && forward_range && common_range> {}); #endif } }; #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17 CPP_template(typename R, typename P)( // requires input_range && forward_range

&& viewable_range && viewable_range

&& indirectly_comparable, iterator_t

, ranges::equal_to> && (forward_range || detail::tiny_range

)) // split_view(R &&, P &&) ->split_view, views::all_t

>; CPP_template(typename R)( // requires input_range) // split_view(R &&, range_value_t) ->split_view, single_view>>; #endif namespace views { struct split_fn { private: friend view_access; template static constexpr auto bind(split_fn split, T t) { return make_pipeable(bind_back(split, std::move(t))); } public: template constexpr auto operator()(Rng && rng, range_value_t val) const -> CPP_ret(split_view, single_view>>)( // requires viewable_range && input_range && indirectly_comparable, range_value_t const *, ranges::equal_to>) { return {all(static_cast(rng)), single(std::move(val))}; } template constexpr auto operator()(Rng && rng, Pattern && pattern) const -> CPP_ret(split_view, all_t>)( // requires viewable_range && input_range && viewable_range && forward_range && indirectly_comparable, iterator_t, ranges::equal_to> && (forward_range || detail::tiny_range)) { return {all((Rng &&) rng), all((Pattern &&) pattern)}; } }; /// \relates split_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(view, split) } // namespace views namespace cpp20 { namespace views { using ranges::views::split; } CPP_template(typename Rng, typename Pattern)( // requires input_range && forward_range && view_ && view_ && indirectly_comparable< iterator_t, iterator_t, ranges::equal_to> && (forward_range || ranges::detail::tiny_range)) using split_view = ranges::split_view; } // namespace cpp20 /// @} } // namespace ranges #include RANGES_SATISFY_BOOST_RANGE(::ranges::split_view) #endif