/// \file // Range v3 library // // Copyright Eric Niebler 2014-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_JOIN_HPP #define RANGES_V3_VIEW_JOIN_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { /// \cond namespace detail { // Compute the cardinality of a joined range constexpr cardinality join_cardinality_( cardinality Outer, cardinality Inner, cardinality Joiner = static_cast(0)) noexcept { return Outer == infinite || Inner == infinite || (Joiner == infinite && Outer != 0 && Outer != 1) ? infinite : Outer == unknown || Inner == unknown || (Joiner == unknown && Outer != 0 && Outer != 1) ? unknown : Outer == finite || Inner == finite || (Joiner == finite && Outer != 0 && Outer != 1) ? finite : static_cast( Outer * Inner + (Outer == 0 ? 0 : (Outer - 1) * Joiner)); } template constexpr cardinality join_cardinality() noexcept { return detail::join_cardinality_( range_cardinality::value, range_cardinality>::value); } template constexpr cardinality join_cardinality() noexcept { return detail::join_cardinality_( range_cardinality::value, range_cardinality>::value, range_cardinality::value); } template struct store_inner_ { non_propagating_cache> inner_ = {}; template constexpr auto && update_inner_(OuterIt && it) { return inner_.emplace_deref(it); } constexpr Inner & get_inner_(ignore_t) noexcept { return *inner_; } }; struct pass_thru_inner_ { // Intentionally promote xvalues to lvalues here: template static constexpr auto && update_inner_(OuterIt && it) noexcept { return *it; } template static constexpr decltype(auto) get_inner_(OuterIt && outer_it) { return *outer_it; } }; template using join_view_inner = meta::conditional_t>::value, store_inner_>, pass_thru_inner_>; // clang-format off /// \concept has_member_arrow_ /// \brief The \c has_member_arrow_ concept template CPP_requires(has_member_arrow_, requires(I i) // ( i.operator->() )); /// \concept has_arrow_ /// \brief The \c has_arrow_ concept template CPP_concept has_arrow_ = input_iterator && (std::is_pointer::value || CPP_requires_ref(detail::has_member_arrow_, I)); // clang-format on } // namespace detail /// \endcond /// \addtogroup group-views /// @{ // Join a range of ranges template struct RANGES_EMPTY_BASES join_view : view_facade, detail::join_cardinality()> , private detail::join_view_inner { CPP_assert(input_range && view_); CPP_assert(input_range>); join_view() = default; explicit join_view(Rng rng) : outer_(views::all(std::move(rng))) {} // Not to spec CPP_member static constexpr auto size() // -> CPP_ret(std::size_t)( requires (detail::join_cardinality() >= 0)) { return static_cast(detail::join_cardinality()); } // Not to spec CPP_auto_member constexpr auto CPP_fun(size)()( requires(detail::join_cardinality() < 0) && (range_cardinality::value >= 0) && forward_range && sized_range>) { range_size_t> n = 0; RANGES_FOR(auto && inner, outer_) n += ranges::size(inner); return n; } // // ericniebler/stl2#605 constexpr Rng base() const { return outer_; } private: friend range_access; Rng outer_{}; template struct cursor { private: using Parent = meta::conditional_t; using COuter = meta::conditional_t; using CInner = range_reference_t; using ref_is_glvalue = std::is_reference; Parent * rng_ = nullptr; iterator_t outer_it_{}; iterator_t inner_it_{}; void satisfy() { for(; outer_it_ != ranges::end(rng_->outer_); ++outer_it_) { auto && inner = rng_->update_inner_(outer_it_); inner_it_ = ranges::begin(inner); if(inner_it_ != ranges::end(inner)) return; } if(RANGES_CONSTEXPR_IF(ref_is_glvalue::value)) inner_it_ = iterator_t(); } public: using single_pass = meta::bool_> || single_pass_iterator_> || !ref_is_glvalue::value>; cursor() = default; template constexpr cursor(Parent * rng, BeginOrEnd begin_or_end) : rng_{rng} , outer_it_(begin_or_end(rng->outer_)) { satisfy(); } template(bool Other)( requires Const AND CPP_NOT(Other) AND convertible_to, iterator_t> AND convertible_to>, iterator_t>) constexpr cursor(cursor that) : rng_(that.rng_) , outer_it_(std::move(that.outer_it_)) , inner_it_(std::move(that.inner_it_)) {} CPP_member constexpr auto arrow() // -> CPP_ret(iterator_t)( requires detail::has_arrow_>) { return inner_it_; } constexpr bool equal(default_sentinel_t) const { return outer_it_ == ranges::end(rng_->outer_); } CPP_member constexpr auto equal(cursor const & that) const // -> CPP_ret(bool)( requires ref_is_glvalue::value && // equality_comparable> && // equality_comparable>) { return outer_it_ == that.outer_it_ && inner_it_ == that.inner_it_; } constexpr void next() { auto && inner_rng = rng_->get_inner_(outer_it_); if(++inner_it_ == ranges::end(inner_rng)) { ++outer_it_; satisfy(); } } CPP_member constexpr auto prev() // -> CPP_ret(void)( requires ref_is_glvalue::value && // bidirectional_range && // bidirectional_range && // common_range) // ericniebler/stl2#606 { if(outer_it_ == ranges::end(rng_->outer_)) inner_it_ = ranges::end(*--outer_it_); while(inner_it_ == ranges::begin(*outer_it_)) inner_it_ = ranges::end(*--outer_it_); --inner_it_; } // clang-format off constexpr auto CPP_auto_fun(read)()(const) ( return *inner_it_ ) constexpr auto CPP_auto_fun(move)()(const) ( return iter_move(inner_it_) ) // clang-format on }; static constexpr bool use_const_always() noexcept { return simple_view() && std::is_reference>::value; } struct end_cursor_fn { constexpr auto operator()(join_view * this_, std::true_type) const { return cursor{this_, ranges::end}; } constexpr auto operator()(join_view *, std::false_type) const { return default_sentinel_t{}; } }; struct cend_cursor_fn { constexpr auto operator()(join_view const * this_, std::true_type) const { return cursor{this_, ranges::end}; } constexpr auto operator()(join_view const *, std::false_type) const { return default_sentinel_t{}; } }; constexpr cursor begin_cursor() { return {this, ranges::begin}; } template(bool Const = true)( requires Const AND input_range> AND std::is_reference>>::value) constexpr cursor begin_cursor() const { return {this, ranges::begin}; } constexpr auto end_cursor() { using cond = meta::bool_>::value && forward_range && forward_range> && common_range && common_range>>; return end_cursor_fn{}(this, cond{}); } template(bool Const = true)( requires Const AND input_range> AND std::is_reference>>::value) constexpr auto end_cursor() const { using CRng = meta::const_if_c; using cond = meta::bool_>::value && forward_range && forward_range> && common_range && common_range>>; return cend_cursor_fn{}(this, cond{}); } }; // Join a range of ranges, inserting a range of values between them. // TODO: Support const iteration when range_reference_t is a true reference. template struct join_with_view : view_facade, detail::join_cardinality()> , private detail::join_view_inner { CPP_assert(input_range); CPP_assert(input_range>); CPP_assert(forward_range); CPP_assert( common_with>, range_value_t>); CPP_assert(semiregular>, range_value_t>>); join_with_view() = default; join_with_view(Rng rng, ValRng val) : outer_(views::all(std::move(rng))) , val_(views::all(std::move(val))) {} CPP_member static constexpr auto size() // -> CPP_ret(std::size_t)( requires (detail::join_cardinality() >= 0)) { return static_cast(detail::join_cardinality()); } CPP_auto_member auto CPP_fun(size)()(const // requires(detail::join_cardinality() < 0) && (range_cardinality::value >= 0) && forward_range && sized_range> && sized_range) { range_size_t> n = 0; RANGES_FOR(auto && inner, outer_) n += ranges::size(inner); return n + (range_cardinality::value == 0 ? 0 : ranges::size(val_) * (range_cardinality::value - 1)); } private: friend range_access; using Outer = views::all_t; // Intentionally promote xvalues to lvalues here: using Inner = views::all_t &>; Outer outer_{}; views::all_t val_{}; class cursor { join_with_view * rng_ = nullptr; iterator_t outer_it_{}; variant, iterator_t> cur_{}; void satisfy() { while(true) { if(cur_.index() == 0) { if(ranges::get<0>(cur_) != ranges::end(rng_->val_)) break; // Intentionally promote xvalues to lvalues here: auto && inner = rng_->update_inner_(outer_it_); ranges::emplace<1>(cur_, ranges::begin(inner)); } else { auto && inner = rng_->get_inner_(outer_it_); if(ranges::get<1>(cur_) != ranges::end(inner)) break; if(++outer_it_ == ranges::end(rng_->outer_)) break; ranges::emplace<0>(cur_, ranges::begin(rng_->val_)); } } } public: using value_type = common_type_t, range_value_t>; using reference = common_reference_t, range_reference_t>; using rvalue_reference = common_reference_t, range_rvalue_reference_t>; using single_pass = std::true_type; cursor() = default; cursor(join_with_view * rng) : rng_{rng} , outer_it_(ranges::begin(rng->outer_)) { if(outer_it_ != ranges::end(rng->outer_)) { auto && inner = rng_->update_inner_(outer_it_); ranges::emplace<1>(cur_, ranges::begin(inner)); satisfy(); } } bool equal(default_sentinel_t) const { return outer_it_ == ranges::end(rng_->outer_); } void next() { // visit(cur_, [](auto& it){ ++it; }); if(cur_.index() == 0) { auto & it = ranges::get<0>(cur_); RANGES_ASSERT(it != ranges::end(rng_->val_)); ++it; } else { auto & it = ranges::get<1>(cur_); #ifndef NDEBUG auto && inner = rng_->get_inner_(outer_it_); RANGES_ASSERT(it != ranges::end(inner)); #endif ++it; } satisfy(); } reference read() const { // return visit(cur_, [](auto& it) -> reference { return *it; }); if(cur_.index() == 0) return *ranges::get<0>(cur_); else return *ranges::get<1>(cur_); } rvalue_reference move() const { // return visit(cur_, [](auto& it) -> rvalue_reference { return // iter_move(it); }); if(cur_.index() == 0) return iter_move(ranges::get<0>(cur_)); else return iter_move(ranges::get<1>(cur_)); } }; cursor begin_cursor() { return {this}; } }; namespace views { /// \cond // Don't forget to update views::for_each whenever this set // of concepts changes // clang-format off /// \concept joinable_range_ /// \brief The \c joinable_range_ concept template(typename Rng)( concept (joinable_range_)(Rng), input_range> ); /// \concept joinable_range /// \brief The \c joinable_range concept template CPP_concept joinable_range = viewable_range && input_range && CPP_concept_ref(views::joinable_range_, Rng); /// \concept joinable_with_range_ /// \brief The \c joinable_with_range_ concept template(typename Rng, typename ValRng)( concept (joinable_with_range_)(Rng, ValRng), common_with< range_value_t, range_value_t>> AND semiregular< common_type_t< range_value_t, range_value_t>>> AND common_reference_with< range_reference_t, range_reference_t>> AND common_reference_with< range_rvalue_reference_t, range_rvalue_reference_t>> ); /// \concept joinable_with_range /// \brief The \c joinable_with_range concept template CPP_concept joinable_with_range = joinable_range && viewable_range && forward_range && CPP_concept_ref(views::joinable_with_range_, Rng, ValRng); // clang-format on /// \endcond struct cpp20_join_fn { template(typename Rng)( requires joinable_range) join_view> operator()(Rng && rng) const { return join_view>{all(static_cast(rng))}; } }; struct join_base_fn : cpp20_join_fn { private: template using inner_value_t = range_value_t>; public: using cpp20_join_fn::operator(); template(typename Rng)( requires joinable_with_range>>) join_with_view, single_view>> // operator()(Rng && rng, inner_value_t v) const { return {all(static_cast(rng)), single(std::move(v))}; } template(typename Rng, typename ValRng)( requires joinable_with_range) join_with_view, all_t> // operator()(Rng && rng, ValRng && val) const { return {all(static_cast(rng)), all(static_cast(val))}; } /// \cond template invoke_result_t // operator()(Rng && rng, detail::reference_wrapper_ r) const { return (*this)(static_cast(rng), r.get()); } /// \endcond }; struct join_bind_fn { template(typename T)( requires (!joinable_range)) // TODO: underconstrained constexpr auto operator()(T && t)const { return make_view_closure(bind_back(join_base_fn{}, static_cast(t))); } template(typename T)( requires (!joinable_range) AND range) constexpr auto operator()(T & t) const { return make_view_closure(bind_back(join_base_fn{}, detail::reference_wrapper_(t))); } }; struct RANGES_EMPTY_BASES join_fn : join_base_fn, join_bind_fn { using join_base_fn::operator(); using join_bind_fn::operator(); }; /// \relates join_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(view_closure, join) } // namespace views /// @} #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17 template(typename Rng)( requires views::joinable_range) explicit join_view(Rng &&) ->join_view>; template(typename Rng, typename ValRng)( requires views::joinable_with_range) explicit join_with_view(Rng &&, ValRng &&) ->join_with_view, views::all_t>; #endif namespace cpp20 { namespace views { RANGES_INLINE_VARIABLE( ranges::views::view_closure, join) } template(typename Rng)( requires input_range AND view_ AND input_range>>) // using join_view = ranges::join_view; } // namespace cpp20 } // namespace ranges #include #include RANGES_SATISFY_BOOST_RANGE(::ranges::join_view) RANGES_SATISFY_BOOST_RANGE(::ranges::join_with_view) #endif