/// \file // Range v3 library // // Copyright Eric Niebler 2013-2014. // // 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_CONCAT_HPP #define RANGES_V3_VIEW_CONCAT_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { /// \cond namespace detail { template using concat_cardinality_ = std::integral_constant< cardinality, State::value == infinite || Value::value == infinite ? infinite : State::value == unknown || Value::value == unknown ? unknown : State::value == finite || Value::value == finite ? finite : static_cast(State::value + Value::value)>; template using concat_cardinality = meta::fold...>, std::integral_constant(0)>, meta::quote>; } // namespace detail /// \endcond /// \addtogroup group-views /// @{ template struct concat_view : view_facade, detail::concat_cardinality::value> { private: friend range_access; using difference_type_ = common_type_t...>; static constexpr std::size_t cranges{sizeof...(Rngs)}; std::tuple rngs_; template struct cursor; template struct sentinel { private: friend struct sentinel; friend struct cursor; template using constify_if = meta::const_if_c; using concat_view_t = constify_if; sentinel_t>>> end_; public: sentinel() = default; sentinel(concat_view_t * rng, end_tag) : end_(end(std::get(rng->rngs_))) {} CPP_template(bool Other)( // requires IsConst && (!Other)) sentinel(sentinel that) : end_(std::move(that.end_)) {} }; template struct cursor { using difference_type = common_type_t...>; private: friend struct cursor; template using constify_if = meta::const_if_c; using concat_view_t = constify_if; concat_view_t * rng_; variant>...> its_; template void satisfy(meta::size_t) { RANGES_EXPECT(its_.index() == N); if(ranges::get(its_) == end(std::get(rng_->rngs_))) { ranges::emplace(its_, begin(std::get(rng_->rngs_))); this->satisfy(meta::size_t{}); } } void satisfy(meta::size_t) { RANGES_EXPECT(its_.index() == cranges - 1); } struct next_fun { cursor * pos; template auto operator()(indexed_element it) const -> CPP_ret(void)( // requires input_iterator) { RANGES_ASSERT(it.get() != end(std::get(pos->rng_->rngs_))); ++it.get(); pos->satisfy(meta::size_t{}); } }; struct prev_fun { cursor * pos; template auto operator()(indexed_element it) const -> CPP_ret(void)( // requires bidirectional_iterator) { RANGES_ASSERT(it.get() != begin(std::get<0>(pos->rng_->rngs_))); --it.get(); } template auto operator()(indexed_element it) const -> CPP_ret(void)( // requires(N != 0) && bidirectional_iterator) { if(it.get() == begin(std::get(pos->rng_->rngs_))) { auto && rng = std::get(pos->rng_->rngs_); ranges::emplace( pos->its_, ranges::next(ranges::begin(rng), ranges::end(rng))); pos->its_.visit_i(*this); } else --it.get(); } }; struct advance_fwd_fun { cursor * pos; difference_type n; template auto operator()(indexed_element it) const -> CPP_ret(void)( // requires random_access_iterator) { ranges::advance(it.get(), n); } template auto operator()(indexed_element it) const -> CPP_ret(void)( // requires random_access_iterator) { auto last = ranges::end(std::get(pos->rng_->rngs_)); // BUGBUG If distance(it, last) > n, then using bounded advance // is O(n) when it need not be since the last iterator position // is actually not interesting. Only the "rest" is needed, which // can sometimes be O(1). auto rest = ranges::advance(it.get(), n, std::move(last)); pos->satisfy(meta::size_t{}); if(rest != 0) pos->its_.visit_i(advance_fwd_fun{pos, rest}); } }; struct advance_rev_fun { cursor * pos; difference_type n; template auto operator()(indexed_element it) const -> CPP_ret(void)( // requires random_access_iterator) { ranges::advance(it.get(), n); } template auto operator()(indexed_element it) const -> CPP_ret(void)( // requires random_access_iterator) { auto first = ranges::begin(std::get(pos->rng_->rngs_)); if(it.get() == first) { auto && rng = std::get(pos->rng_->rngs_); ranges::emplace( pos->its_, ranges::next(ranges::begin(rng), ranges::end(rng))); pos->its_.visit_i(*this); } else { auto rest = ranges::advance(it.get(), n, std::move(first)); if(rest != 0) pos->its_.visit_i(advance_rev_fun{pos, rest}); } } }; [[noreturn]] static difference_type distance_to_(meta::size_t, cursor const &, cursor const &) { RANGES_EXPECT(false); } template static difference_type distance_to_(meta::size_t, cursor const & from, cursor const & to) { if(from.its_.index() > N) return cursor::distance_to_(meta::size_t{}, from, to); if(from.its_.index() == N) { if(to.its_.index() == N) return distance(ranges::get(from.its_), ranges::get(to.its_)); return distance(ranges::get(from.its_), end(std::get(from.rng_->rngs_))) + cursor::distance_to_(meta::size_t{}, from, to); } if(from.its_.index() < N && to.its_.index() > N) return distance(std::get(from.rng_->rngs_)) + cursor::distance_to_(meta::size_t{}, from, to); RANGES_EXPECT(to.its_.index() == N); return distance(begin(std::get(from.rng_->rngs_)), ranges::get(to.its_)); } public: // BUGBUG what about rvalue_reference and common_reference? using reference = common_reference_t>...>; using single_pass = meta::or_c>...>; cursor() = default; cursor(concat_view_t * rng, begin_tag) : rng_(rng) , its_{emplaced_index<0>, begin(std::get<0>(rng->rngs_))} { this->satisfy(meta::size_t<0>{}); } cursor(concat_view_t * rng, end_tag) : rng_(rng) , its_{emplaced_index, end(std::get(rng->rngs_))} {} CPP_template(bool Other)( // requires IsConst && (!Other)) // cursor(cursor that) : rng_(that.rng_) , its_(std::move(that.its_)) {} reference read() const { // Kind of a dumb implementation. Surely there's a better way. return ranges::get<0>(unique_variant(its_.visit( compose(convert_to{}, detail::dereference_fn{})))); } void next() { its_.visit_i(next_fun{this}); } CPP_member auto equal(cursor const & pos) const -> CPP_ret(bool)( // requires equality_comparable>...>>) { return its_ == pos.its_; } bool equal(sentinel const & pos) const { return its_.index() == cranges - 1 && ranges::get(its_) == pos.end_; } CPP_member auto prev() -> CPP_ret(void)( // requires and_v...>) { its_.visit_i(prev_fun{this}); } CPP_member auto advance(difference_type n) -> CPP_ret(void)( // requires and_v...>) { if(n > 0) its_.visit_i(advance_fwd_fun{this, n}); else if(n < 0) its_.visit_i(advance_rev_fun{this, n}); } CPP_member auto distance_to(cursor const & that) const -> CPP_ret(difference_type)( // requires and_v, iterator_t>...>) { if(its_.index() <= that.its_.index()) return cursor::distance_to_(meta::size_t<0>{}, *this, that); return -cursor::distance_to_(meta::size_t<0>{}, that, *this); } }; cursor()...>::value> begin_cursor() { return {this, begin_tag{}}; } meta::if_...>, cursor()...>::value>, sentinel()...>::value>> end_cursor() { return {this, end_tag{}}; } CPP_member auto begin_cursor() const -> CPP_ret(cursor)( // requires and_v...>) { return {this, begin_tag{}}; } CPP_member auto end_cursor() const -> CPP_ret( meta::if_...>, cursor, sentinel>)( // requires and_v...>) { return {this, end_tag{}}; } public: concat_view() = default; explicit concat_view(Rngs... rngs) : rngs_{std::move(rngs)...} {} CPP_member constexpr auto size() const -> CPP_ret(std::size_t)( // requires(detail::concat_cardinality::value >= 0)) { return static_cast(detail::concat_cardinality::value); } CPP_member constexpr auto CPP_fun(size)()( const requires(detail::concat_cardinality::value < 0) && and_v...>) { using size_type = common_type_t...>; return tuple_foldl( tuple_transform(rngs_, [](auto && r) -> size_type { return ranges::size(r); }), size_type{0}, plus{}); } CPP_member constexpr auto CPP_fun(size)()( requires(detail::concat_cardinality::value < 0) && and_v...>) { using size_type = common_type_t...>; return tuple_foldl( tuple_transform(rngs_, [](auto && r) -> size_type { return ranges::size(r); }), size_type{0}, plus{}); } }; #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17 template concat_view(Rng &&...)->concat_view...>; #endif namespace views { struct concat_fn { template auto operator()(Rngs &&... rngs) const -> CPP_ret(concat_view...>)( // requires and_v<(viewable_range && input_range)...>) { return concat_view...>{all(static_cast(rngs))...}; } }; /// \relates concat_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(concat_fn, concat) } // namespace views /// @} } // namespace ranges #include RANGES_SATISFY_BOOST_RANGE(::ranges::concat_view) #endif