/// \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_ZIP_WITH_HPP #define RANGES_V3_VIEW_ZIP_WITH_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { /// \cond namespace detail { struct equal_to_ { template bool operator()(T const & t, U const & u) const { return static_cast(t == u); } }; RANGES_INLINE_VARIABLE(equal_to_, equal_to) struct dec_ { template void operator()(T & t) const { --t; } }; RANGES_INLINE_VARIABLE(dec_, dec) struct inc_ { template void operator()(T & t) const { ++t; } }; RANGES_INLINE_VARIABLE(inc_, inc) struct _advance_ { template auto operator()(I & i, Diff n) const -> CPP_ret(void)( // requires input_or_output_iterator && integer_like_) { advance(i, static_cast>(n)); } }; RANGES_INLINE_VARIABLE(_advance_, advance_) struct distance_to_ { template constexpr auto operator()(T const & t, T const & u) const -> decltype(u - t) { return u - t; } }; RANGES_INLINE_VARIABLE(distance_to_, distance_to) struct _min_ { template constexpr auto operator()(T const & t, U const & u) const -> decltype(true ? t : u) { return u < t ? u : t; } }; RANGES_INLINE_VARIABLE(_min_, min_) struct _max_ { template constexpr auto operator()(T const & t, U const & u) const -> decltype(true ? u : t) { return u < t ? t : u; } }; RANGES_INLINE_VARIABLE(_max_, max_) template using zip_cardinality = std::integral_constant< cardinality, State::value >= 0 || Value::value >= 0 ? (State::value >= 0 && Value::value >= 0 ? min_(State::value, Value::value) : finite) : State::value == finite || Value::value == finite ? finite : State::value == unknown || Value::value == unknown ? unknown : infinite>; } // namespace detail /// \endcond namespace views { // clang-format off CPP_def ( template(typename Fun, typename ...Rngs) (concept zippable_with)(Fun, Rngs...), and_v...> && copy_constructible && invocable...> && invocable...> && invocable...> ); // clang-format on } // namespace views /// \addtogroup group-views /// @{ template struct iter_zip_with_view : view_facade, meta::fold...>, std::integral_constant, meta::quote>::value> { private: CPP_assert(sizeof...(Rngs) != 0); friend range_access; semiregular_box_t fun_; std::tuple rngs_; using difference_type_ = common_type_t...>; template struct cursor; template struct sentinel { private: friend struct cursor; friend struct sentinel; std::tuple>...> ends_; public: sentinel() = default; sentinel(detail::ignore_t, std::tuple>...> ends) : ends_(std::move(ends)) {} CPP_template(bool Other)( // requires Const && (!Other)) sentinel(sentinel that) : ends_(std::move(that.ends_)) {} }; template struct cursor { private: friend struct cursor; using fun_ref_ = semiregular_box_ref_or_val_t; fun_ref_ fun_; std::tuple>...> its_; public: using difference_type = common_type_t>...>; using single_pass = meta::or_c<( bool)single_pass_iterator_>>...>; using value_type = detail::decay_t>...>>; cursor() = default; cursor(fun_ref_ fun, std::tuple>...> its) : fun_(std::move(fun)) , its_(std::move(its)) {} CPP_template(bool Other)( // requires Const && (!Other)) cursor(cursor that) : fun_(std::move(that.fun_)) , its_(std::move(that.its_)) {} // clang-format off auto CPP_auto_fun(read)()(const) ( return tuple_apply(fun_, its_) ) // clang-format on void next() { tuple_for_each(its_, detail::inc); } CPP_member auto equal(cursor const & that) const -> CPP_ret(bool)( // requires and_v< sentinel_for>, iterator_t>>...>) { // By returning true if *any* of the iterators are equal, we allow // zipped ranges to be of different lengths, stopping when the first // one reaches the last. return tuple_foldl(tuple_transform(its_, that.its_, detail::equal_to), false, [](bool a, bool b) { return a || b; }); } bool equal(sentinel const & s) const { // By returning true if *any* of the iterators are equal, we allow // zipped ranges to be of different lengths, stopping when the first // one reaches the last. return tuple_foldl(tuple_transform(its_, s.ends_, detail::equal_to), false, [](bool a, bool b) { return a || b; }); } CPP_member auto prev() -> CPP_ret(void)( // requires and_v>...>) { tuple_for_each(its_, detail::dec); } CPP_member auto advance(difference_type n) -> CPP_ret(void)( // requires and_v>...>) { tuple_for_each(its_, bind_back(detail::advance_, n)); } CPP_member auto distance_to(cursor const & that) const -> CPP_ret(difference_type)( // requires and_v< sized_sentinel_for>, iterator_t>>...>) { // Return the smallest distance (in magnitude) of any of the iterator // pairs. This is to accommodate zippers of sequences of different length. if(0 < std::get<0>(that.its_) - std::get<0>(its_)) return tuple_foldl( tuple_transform(its_, that.its_, detail::distance_to), (std::numeric_limits::max)(), detail::min_); else return tuple_foldl( tuple_transform(its_, that.its_, detail::distance_to), (std::numeric_limits::min)(), detail::max_); } // clang-format off template auto CPP_auto_fun(move_)(meta::index_sequence)(const) ( return invoke(fun_, move_tag{}, std::get(its_)...) ) // clang-format on auto move() const noexcept(noexcept(std::declval().move_( meta::make_index_sequence{}))) -> decltype(std::declval().move_( meta::make_index_sequence{})) { return move_(meta::make_index_sequence{}); } }; template using end_cursor_t = meta::if_c..., !(bool)single_pass_iterator_>...>, cursor, sentinel>; cursor begin_cursor() { return {fun_, tuple_transform(rngs_, ranges::begin)}; } end_cursor_t end_cursor() { return {fun_, tuple_transform(rngs_, ranges::end)}; } template auto begin_cursor() const -> CPP_ret(cursor)( // requires Const && and_v...> && views::zippable_with...>) { return {fun_, tuple_transform(rngs_, ranges::begin)}; } template auto end_cursor() const -> CPP_ret(end_cursor_t)( // requires Const && and_v...> && views::zippable_with...>) { return {fun_, tuple_transform(rngs_, ranges::end)}; } public: iter_zip_with_view() = default; explicit iter_zip_with_view(Rngs... rngs) : fun_(Fun{}) , rngs_{std::move(rngs)...} {} explicit iter_zip_with_view(Fun fun, Rngs... rngs) : fun_(std::move(fun)) , rngs_{std::move(rngs)...} {} CPP_member constexpr auto CPP_fun(size)()(const requires and_v...>) { using size_type = common_type_t...>; return range_cardinality::value >= 0 ? size_type{( std::size_t)range_cardinality::value} : tuple_foldl(tuple_transform(rngs_, [](auto && r) -> size_type { return ranges::size(r); }), (std::numeric_limits::max)(), detail::min_); } }; template struct zip_with_view : iter_zip_with_view, Rngs...> { CPP_assert(sizeof...(Rngs) != 0); zip_with_view() = default; explicit zip_with_view(Rngs... rngs) : iter_zip_with_view, Rngs...>{{Fun{}}, std::move(rngs)...} {} explicit zip_with_view(Fun fun, Rngs... rngs) : iter_zip_with_view, Rngs...>{{std::move(fun)}, std::move(rngs)...} {} }; #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17 CPP_template(typename Fun, typename... Rng)(requires copy_constructible) zip_with_view(Fun, Rng &&...) ->zip_with_view...>; #endif namespace views { struct iter_zip_with_fn { template auto operator()(Fun fun, Rngs &&... rngs) const -> CPP_ret( iter_zip_with_view...>)( // requires and_v...> && zippable_with && (sizeof...(Rngs) != 0)) { return iter_zip_with_view...>{ std::move(fun), all(static_cast(rngs))...}; } template constexpr auto operator()(Fun) const noexcept -> CPP_ret(empty_view>)( // requires zippable_with) { return {}; } }; /// \relates iter_zip_with_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(iter_zip_with_fn, iter_zip_with) struct zip_with_fn { template auto operator()(Fun fun, Rngs &&... rngs) const -> CPP_ret(zip_with_view...>)( // requires and_v...> && and_v...> && copy_constructible && invocable &&...> && (sizeof...(Rngs) != 0)) { return zip_with_view...>{ std::move(fun), all(static_cast(rngs))...}; } template constexpr auto operator()(Fun) const noexcept -> CPP_ret(empty_view>)( // requires copy_constructible && invocable) { return {}; } }; /// \relates zip_with_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(zip_with_fn, zip_with) } // namespace views /// @} } // namespace ranges #include RANGES_SATISFY_BOOST_RANGE(::ranges::iter_zip_with_view) RANGES_SATISFY_BOOST_RANGE(::ranges::zip_with_view) #endif