/// \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 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_ { views::all_t inner_ = views::all_t(); constexpr views::all_t & update_inner_(Inner && inner) { return (inner_ = views::all(static_cast(inner))); } constexpr views::all_t & get_inner_(ignore_t) noexcept { return inner_; } }; struct pass_thru_inner_ { // Intentionally promote xvalues to lvalues here: template static constexpr Inner & update_inner_(Inner && inner) noexcept { return inner; } template static constexpr decltype(auto) get_inner_(OuterIt && outer_it) { return *outer_it; } }; template using join_view_inner = if_then_t>::value, store_inner_>, pass_thru_inner_>; // clang-format off CPP_def ( template(typename I) concept has_member_arrow_, requires (I i) ( i.operator->() ) ); CPP_def ( template(typename I) concept has_arrow_, input_iterator && (std::is_pointer::value || has_member_arrow_) ); // 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>); CPP_assert(viewable_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_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 = detail::if_then_t; using COuter = detail::if_then_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(); } CPP_template(bool Other)( // requires Const && (!Other) && convertible_to, iterator_t> && 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< iterator_t> && 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 constexpr auto begin_cursor() const -> CPP_ret(cursor)( // requires Const && input_range> && std::is_reference>>::value) { 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 constexpr auto CPP_fun(end_cursor)()( const requires Const && input_range> && std::is_reference>>::value) { 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()> { 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_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; using Inner = views::all_t>; Outer outer_{}; Inner inner_{}; 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; rng_->inner_ = views::all(*outer_it_); ranges::emplace<1>(cur_, ranges::begin(rng_->inner_)); } else { if(ranges::get<1>(cur_) != ranges::end(rng_->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_)) { rng->inner_ = views::all(*outer_it_); ranges::emplace<1>(cur_, ranges::begin(rng->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_); RANGES_ASSERT(it != ranges::end(rng_->inner_)); ++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 CPP_def ( template(typename Rng) concept joinable_range, viewable_range && input_range && input_range> && viewable_range> ); CPP_def ( template(typename Rng, typename ValRng) concept joinable_with_range, joinable_range && viewable_range && forward_range && common_with, range_value_t>> && semiregular< common_type_t< range_value_t, range_value_t>>> && common_reference_with< range_reference_t, range_reference_t>> && common_reference_with< range_rvalue_reference_t, range_rvalue_reference_t>> ); // clang-format on /// \endcond struct cpp20_join_fn { template auto operator()(Rng && rng) const -> CPP_ret(join_view>)( // requires joinable_range) { return join_view>{all(static_cast(rng))}; } }; struct join_fn : cpp20_join_fn { private: friend view_access; template static auto CPP_fun(bind)(join_fn join, T && t)( // requires(!joinable_range)) { return make_pipeable(bind_back(join, static_cast(t))); } #ifdef RANGES_WORKAROUND_MSVC_OLD_LAMBDA template struct lamduh { T (&val_)[N]; template auto operator()(Rng && rng) const -> invoke_result_t { return join_fn{}(static_cast(rng), val_); } }; template static lamduh bind(join_fn, T (&val)[N]) { return {val}; } #else // ^^^ workaround / no workaround vvv template static auto bind(join_fn, T (&val)[N]) { return [&val](auto && rng) -> invoke_result_t { return join_fn{}(static_cast(rng), val); }; } #endif // RANGES_WORKAROUND_MSVC_OLD_LAMBDA template using inner_value_t = range_value_t>; public: using cpp20_join_fn::operator(); template auto operator()(Rng && rng, inner_value_t v) const -> CPP_ret(join_with_view, single_view>>)( // requires joinable_with_range>>) { return {all(static_cast(rng)), single(std::move(v))}; } template auto operator()(Rng && rng, ValRng && val) const -> CPP_ret(join_with_view, all_t>)( // requires joinable_with_range) { return {all(static_cast(rng)), all(static_cast(val))}; } }; /// \relates join_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(view, join) } // namespace views /// @} #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17 CPP_template(typename Rng)( // requires views::joinable_range) // explicit join_view(Rng &&) ->join_view>; CPP_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, join) } CPP_template(typename Rng)( // requires input_range && view_ && input_range>> && (std::is_reference>>::value || view_>>)) // using join_view = ranges::join_view; } // namespace cpp20 } // namespace ranges #include RANGES_SATISFY_BOOST_RANGE(::ranges::join_view) RANGES_SATISFY_BOOST_RANGE(::ranges::join_with_view) #endif