Files
allhaileris afb81b8278
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
init
2026-02-16 15:50:16 +03:00

453 lines
17 KiB
C++

/// \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 <tuple>
#include <type_traits>
#include <utility>
#include <meta/meta.hpp>
#include <range/v3/range_fwd.hpp>
#include <range/v3/functional/arithmetic.hpp>
#include <range/v3/functional/compose.hpp>
#include <range/v3/iterator/operations.hpp>
#include <range/v3/range/access.hpp>
#include <range/v3/range/concepts.hpp>
#include <range/v3/range/primitives.hpp>
#include <range/v3/range/traits.hpp>
#include <range/v3/utility/static_const.hpp>
#include <range/v3/utility/tuple_algorithm.hpp>
#include <range/v3/utility/variant.hpp>
#include <range/v3/view/all.hpp>
#include <range/v3/view/facade.hpp>
#include <range/v3/view/view.hpp>
#include <range/v3/detail/prologue.hpp>
namespace ranges
{
/// \cond
namespace detail
{
template<typename State, typename Value>
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<cardinality>(State::value + Value::value)>;
template<typename... Rngs>
using concat_cardinality =
meta::fold<meta::list<range_cardinality<Rngs>...>,
std::integral_constant<cardinality, static_cast<cardinality>(0)>,
meta::quote<concat_cardinality_>>;
} // namespace detail
/// \endcond
/// \addtogroup group-views
/// @{
template<typename... Rngs>
struct concat_view
: view_facade<concat_view<Rngs...>, detail::concat_cardinality<Rngs...>::value>
{
private:
friend range_access;
using difference_type_ = common_type_t<range_difference_t<Rngs>...>;
static constexpr std::size_t cranges{sizeof...(Rngs)};
std::tuple<Rngs...> rngs_;
template<bool IsConst>
struct cursor;
template<bool IsConst>
struct sentinel
{
private:
friend struct sentinel<!IsConst>;
friend struct cursor<IsConst>;
template<typename T>
using constify_if = meta::const_if_c<IsConst, T>;
using concat_view_t = constify_if<concat_view>;
sentinel_t<constify_if<meta::back<meta::list<Rngs...>>>> end_;
public:
sentinel() = default;
sentinel(concat_view_t * rng, end_tag)
: end_(end(std::get<cranges - 1>(rng->rngs_)))
{}
template(bool Other)(
requires IsConst AND CPP_NOT(Other)) //
sentinel(sentinel<Other> that)
: end_(std::move(that.end_))
{}
};
template<bool IsConst>
struct cursor
{
using difference_type = common_type_t<range_difference_t<Rngs>...>;
private:
friend struct cursor<!IsConst>;
template<typename T>
using constify_if = meta::const_if_c<IsConst, T>;
using concat_view_t = constify_if<concat_view>;
concat_view_t * rng_;
variant<iterator_t<constify_if<Rngs>>...> its_;
template<std::size_t N>
void satisfy(meta::size_t<N>)
{
RANGES_EXPECT(its_.index() == N);
if(ranges::get<N>(its_) == end(std::get<N>(rng_->rngs_)))
{
ranges::emplace<N + 1>(its_, begin(std::get<N + 1>(rng_->rngs_)));
this->satisfy(meta::size_t<N + 1>{});
}
}
void satisfy(meta::size_t<cranges - 1>)
{
RANGES_EXPECT(its_.index() == cranges - 1);
}
struct next_fun
{
cursor * pos;
template(typename I, std::size_t N)(
requires input_iterator<I>)
void operator()(indexed_element<I, N> it) const
{
RANGES_ASSERT(it.get() != end(std::get<N>(pos->rng_->rngs_)));
++it.get();
pos->satisfy(meta::size_t<N>{});
}
};
struct prev_fun
{
cursor * pos;
template(typename I)(
requires bidirectional_iterator<I>)
void operator()(indexed_element<I, 0> it) const
{
RANGES_ASSERT(it.get() != begin(std::get<0>(pos->rng_->rngs_)));
--it.get();
}
template(typename I, std::size_t N)(
requires (N != 0) AND bidirectional_iterator<I>)
void operator()(indexed_element<I, N> it) const
{
if(it.get() == begin(std::get<N>(pos->rng_->rngs_)))
{
auto && rng = std::get<N - 1>(pos->rng_->rngs_);
ranges::emplace<N - 1>(
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(typename I)(
requires random_access_iterator<I>)
void operator()(indexed_element<I, cranges - 1> it) const
{
ranges::advance(it.get(), n);
}
template(typename I, std::size_t N)(
requires random_access_iterator<I>)
void operator()(indexed_element<I, N> it) const
{
auto last = ranges::end(std::get<N>(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<N>{});
if(rest != 0)
pos->its_.visit_i(advance_fwd_fun{pos, rest});
}
};
struct advance_rev_fun
{
cursor * pos;
difference_type n;
template(typename I)(
requires random_access_iterator<I>)
void operator()(indexed_element<I, 0> it) const
{
ranges::advance(it.get(), n);
}
template(typename I, std::size_t N)(
requires random_access_iterator<I>)
void operator()(indexed_element<I, N> it) const
{
auto first = ranges::begin(std::get<N>(pos->rng_->rngs_));
if(it.get() == first)
{
auto && rng = std::get<N - 1>(pos->rng_->rngs_);
ranges::emplace<N - 1>(
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<cranges>,
cursor const &,
cursor const &)
{
RANGES_EXPECT(false);
}
template<std::size_t N>
static difference_type distance_to_(meta::size_t<N>, cursor const & from,
cursor const & to)
{
if(from.its_.index() > N)
return cursor::distance_to_(meta::size_t<N + 1>{}, from, to);
if(from.its_.index() == N)
{
if(to.its_.index() == N)
return distance(ranges::get<N>(from.its_),
ranges::get<N>(to.its_));
return distance(ranges::get<N>(from.its_),
end(std::get<N>(from.rng_->rngs_))) +
cursor::distance_to_(meta::size_t<N + 1>{}, from, to);
}
if(from.its_.index() < N && to.its_.index() > N)
return distance(std::get<N>(from.rng_->rngs_)) +
cursor::distance_to_(meta::size_t<N + 1>{}, from, to);
RANGES_EXPECT(to.its_.index() == N);
return distance(begin(std::get<N>(from.rng_->rngs_)),
ranges::get<N>(to.its_));
}
public:
// BUGBUG what about rvalue_reference and common_reference?
using reference = common_reference_t<range_reference_t<constify_if<Rngs>>...>;
using single_pass = meta::or_c<single_pass_iterator_<iterator_t<Rngs>>...>;
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<cranges - 1>, end(std::get<cranges - 1>(rng->rngs_))}
{}
template(bool Other)(
requires IsConst && CPP_NOT(Other)) //
cursor(cursor<Other> 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<reference>{}, 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<variant<iterator_t<constify_if<Rngs>>...>>)
{
return its_ == pos.its_;
}
bool equal(sentinel<IsConst> const & pos) const
{
return its_.index() == cranges - 1 &&
ranges::get<cranges - 1>(its_) == pos.end_;
}
CPP_member
auto prev() //
-> CPP_ret(void)(
requires and_v<bidirectional_range<Rngs>...>)
{
its_.visit_i(prev_fun{this});
}
CPP_member
auto advance(difference_type n) //
-> CPP_ret(void)(
requires and_v<random_access_range<Rngs>...>)
{
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<sized_sentinel_for<iterator_t<Rngs>,
iterator_t<Rngs>>...>)
{
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<meta::and_c<simple_view<Rngs>()...>::value> begin_cursor()
{
return {this, begin_tag{}};
}
meta::if_<meta::and_c<(bool)common_range<Rngs>...>,
cursor<meta::and_c<simple_view<Rngs>()...>::value>,
sentinel<meta::and_c<simple_view<Rngs>()...>::value>>
end_cursor()
{
return {this, end_tag{}};
}
CPP_member
auto begin_cursor() const //
-> CPP_ret(cursor<true>)(
requires and_v<range<Rngs const>...>)
{
return {this, begin_tag{}};
}
CPP_member
auto end_cursor() const //
-> CPP_ret(
meta::if_<meta::and_c<(bool)common_range<Rngs const>...>, //
cursor<true>, //
sentinel<true>>)(
requires and_v<range<Rngs const>...>)
{
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<Rngs...>::value >= 0))
{
return static_cast<std::size_t>(detail::concat_cardinality<Rngs...>::value);
}
CPP_auto_member
constexpr auto CPP_fun(size)()(const //
requires(detail::concat_cardinality<Rngs...>::value < 0) &&
and_v<sized_range<Rngs const>...>)
{
using size_type = common_type_t<range_size_t<Rngs const>...>;
return tuple_foldl(
tuple_transform(rngs_,
[](auto && r) -> size_type { return ranges::size(r); }),
size_type{0},
plus{});
}
CPP_auto_member
constexpr auto CPP_fun(size)()(
requires (detail::concat_cardinality<Rngs...>::value < 0) &&
and_v<sized_range<Rngs>...>)
{
using size_type = common_type_t<range_size_t<Rngs>...>;
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<typename... Rng>
concat_view(Rng &&...) //
-> concat_view<views::all_t<Rng>...>;
#endif
namespace views
{
struct concat_fn
{
template(typename... Rngs)(
requires and_v<(viewable_range<Rngs> && input_range<Rngs>)...>)
concat_view<all_t<Rngs>...> operator()(Rngs &&... rngs) const
{
return concat_view<all_t<Rngs>...>{all(static_cast<Rngs &&>(rngs))...};
}
template(typename Rng)(
requires viewable_range<Rng> AND input_range<Rng>)
all_t<Rng> operator()(Rng && rng) const //
{
return all(static_cast<Rng &&>(rng));
}
// MSVC doesn't like variadics in operator() for some reason
#if defined(_MSC_VER)
template(typename Rng0, typename Rng1)(
requires viewable_range<Rng0> AND input_range<Rng0> AND
viewable_range<Rng1> AND input_range<Rng1>)
concat_view<all_t<Rng0>, all_t<Rng1>> operator()(Rng0 && rng0, Rng1 && rng1)
const
{
return concat_view<all_t<Rng0>, all_t<Rng1>>{
all(static_cast<Rng0 &&>(rng0)),
all(static_cast<Rng1 &&>(rng1))};
}
template(typename Rng0, typename Rng1, typename Rng2)(
requires viewable_range<Rng0> AND input_range<Rng0> AND
viewable_range<Rng1> AND input_range<Rng1> AND
viewable_range<Rng2> AND input_range<Rng2>)
concat_view<all_t<Rng0>, all_t<Rng1>, all_t<Rng2>> //
operator()(Rng0 && rng0, Rng1 && rng1, Rng2 && rng2) const
{
return concat_view<all_t<Rng0>, all_t<Rng1>, all_t<Rng2>>{
all(static_cast<Rng0 &&>(rng0)),
all(static_cast<Rng1 &&>(rng1)),
all(static_cast<Rng2 &&>(rng2))};
}
#endif
};
/// \relates concat_fn
/// \ingroup group-views
RANGES_INLINE_VARIABLE(concat_fn, concat)
} // namespace views
/// @}
} // namespace ranges
#include <range/v3/detail/epilogue.hpp>
#include <range/v3/detail/satisfy_boost_range.hpp>
RANGES_SATISFY_BOOST_RANGE(::ranges::concat_view)
#endif