libstdc++: Implement ranges::concat_view from P2542R7
libstdc++-v3/ChangeLog: * include/bits/version.def (ranges_concat): Define. * include/bits/version.h: Regenerate. * include/std/ranges (__detail::__concat_reference_t): Define for C++26. (__detail::__concat_value_t): Likewise. (__detail::__concat_rvalue_reference_t): Likewise. (__detail::__concat_indirectly_readable_impl): Likewise. (__detail::__concat_indirectly_readable): Likewise. (__detail::__concatable): Likewise. (__detail::__all_but_last_common): Likewise. (__detail::__concat_is_random_access): Likewise. (__detail::__concat_is_bidirectional): Likewise. (__detail::__last_is_common): Likewise. (concat_view): Likewise. (__detail::__concat_view_iter_cat): Likewise. (concat_view::iterator): Likewise. (views::__detail::__can_concat_view): Likewise. (views::_Concat, views::concat): Likewise. * testsuite/std/ranges/concat/1.cc: New test. Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
This commit is contained in:
parent
83bb9ad465
commit
66d2a76dcf
4 changed files with 672 additions and 0 deletions
|
@ -1805,6 +1805,14 @@ ftms = {
|
|||
};
|
||||
};
|
||||
|
||||
ftms = {
|
||||
name = ranges_concat;
|
||||
values = {
|
||||
v = 202403;
|
||||
cxxmin = 26;
|
||||
};
|
||||
};
|
||||
|
||||
// Standard test specifications.
|
||||
stds[97] = ">= 199711L";
|
||||
stds[03] = ">= 199711L";
|
||||
|
|
|
@ -2013,4 +2013,14 @@
|
|||
#endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_string) */
|
||||
#undef __glibcxx_want_to_string
|
||||
|
||||
#if !defined(__cpp_lib_ranges_concat)
|
||||
# if (__cplusplus > 202302L)
|
||||
# define __glibcxx_ranges_concat 202403L
|
||||
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_concat)
|
||||
# define __cpp_lib_ranges_concat 202403L
|
||||
# endif
|
||||
# endif
|
||||
#endif /* !defined(__cpp_lib_ranges_concat) && defined(__glibcxx_want_ranges_concat) */
|
||||
#undef __glibcxx_want_ranges_concat
|
||||
|
||||
#undef __glibcxx_want_all
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#define __glibcxx_want_ranges_as_const
|
||||
#define __glibcxx_want_ranges_as_rvalue
|
||||
#define __glibcxx_want_ranges_cartesian_product
|
||||
#define __glibcxx_want_ranges_concat
|
||||
#define __glibcxx_want_ranges_chunk
|
||||
#define __glibcxx_want_ranges_chunk_by
|
||||
#define __glibcxx_want_ranges_enumerate
|
||||
|
@ -9514,6 +9515,585 @@ namespace __detail
|
|||
} // namespace ranges
|
||||
#endif // __cpp_lib_ranges_to_container
|
||||
|
||||
#if __cpp_lib_ranges_concat // C++ >= C++26
|
||||
namespace ranges
|
||||
{
|
||||
namespace __detail
|
||||
{
|
||||
template<typename... _Rs>
|
||||
using __concat_reference_t = common_reference_t<range_reference_t<_Rs>...>;
|
||||
|
||||
template<typename... _Rs>
|
||||
using __concat_value_t = common_type_t<range_value_t<_Rs>...>;
|
||||
|
||||
template<typename... _Rs>
|
||||
using __concat_rvalue_reference_t
|
||||
= common_reference_t<range_rvalue_reference_t<_Rs>...>;
|
||||
|
||||
template<typename _Ref, typename _RRef, typename _It>
|
||||
concept __concat_indirectly_readable_impl = requires(const _It __it) {
|
||||
{ *__it } -> convertible_to<_Ref>;
|
||||
{ ranges::iter_move(__it) } -> convertible_to<_RRef>;
|
||||
};
|
||||
|
||||
template<typename... _Rs>
|
||||
concept __concat_indirectly_readable
|
||||
= common_reference_with<__concat_reference_t<_Rs...>&&, __concat_value_t<_Rs...>&>
|
||||
&& common_reference_with<__concat_reference_t<_Rs...>&&,
|
||||
__concat_rvalue_reference_t<_Rs...>&&>
|
||||
&& common_reference_with<__concat_rvalue_reference_t<_Rs...>&&,
|
||||
__concat_value_t<_Rs...> const&>
|
||||
&& (__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>,
|
||||
__concat_rvalue_reference_t<_Rs...>,
|
||||
iterator_t<_Rs>>
|
||||
&& ...);
|
||||
|
||||
template<typename... _Rs>
|
||||
concept __concatable = requires {
|
||||
typename __concat_reference_t<_Rs...>;
|
||||
typename __concat_value_t<_Rs...>;
|
||||
typename __concat_rvalue_reference_t<_Rs...>;
|
||||
} && __concat_indirectly_readable<_Rs...>;
|
||||
|
||||
template<bool _Const, typename _Range, typename... _Rs>
|
||||
struct __all_but_last_common
|
||||
{
|
||||
static inline constexpr bool value
|
||||
= requires { requires (common_range<__maybe_const_t<_Const, _Range>>
|
||||
&& __all_but_last_common<_Const, _Rs...>::value); };
|
||||
};
|
||||
|
||||
template<bool _Const, typename _Range>
|
||||
struct __all_but_last_common<_Const, _Range>
|
||||
{ static inline constexpr bool value = true; };
|
||||
|
||||
template<bool _Const, typename... _Rs>
|
||||
concept __concat_is_random_access = __all_random_access<_Const, _Rs...>
|
||||
&& __all_but_last_common<_Const, _Rs...>::value;
|
||||
|
||||
template<bool _Const, typename... _Rs>
|
||||
concept __concat_is_bidirectional = __all_bidirectional<_Const, _Rs...>
|
||||
&& __all_but_last_common<_Const, _Rs...>::value;
|
||||
|
||||
template<typename _Range, typename... _Rs>
|
||||
struct __last_is_common
|
||||
{ static inline constexpr bool value = __last_is_common<_Rs...>::value; };
|
||||
|
||||
template<typename _Range>
|
||||
struct __last_is_common<_Range>
|
||||
{ static inline constexpr bool value = common_range<_Range>; };
|
||||
} // namespace __detail
|
||||
|
||||
template<input_range... _Vs>
|
||||
requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...>
|
||||
class concat_view : public view_interface<concat_view<_Vs...>>
|
||||
{
|
||||
tuple<_Vs...> _M_views;
|
||||
|
||||
template<bool _Const> class iterator;
|
||||
|
||||
public:
|
||||
constexpr concat_view() = default;
|
||||
|
||||
constexpr explicit
|
||||
concat_view(_Vs... __views)
|
||||
: _M_views(std::move(__views)...)
|
||||
{ }
|
||||
|
||||
constexpr iterator<false>
|
||||
begin() requires(!(__detail::__simple_view<_Vs> && ...))
|
||||
{
|
||||
iterator<false> __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views)));
|
||||
__it.template _M_satisfy<0>();
|
||||
return __it;
|
||||
}
|
||||
|
||||
constexpr iterator<true>
|
||||
begin() const requires (range<const _Vs> && ...) && __detail::__concatable<const _Vs...>
|
||||
{
|
||||
iterator<true> __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views)));
|
||||
__it.template _M_satisfy<0>();
|
||||
return __it;
|
||||
}
|
||||
|
||||
constexpr auto
|
||||
end() requires(!(__detail::__simple_view<_Vs> && ...))
|
||||
{
|
||||
if constexpr (__detail::__last_is_common<_Vs...>::value)
|
||||
{
|
||||
constexpr auto __n = sizeof...(_Vs);
|
||||
return iterator<false>(this, in_place_index<__n - 1>,
|
||||
ranges::end(std::get<__n - 1>(_M_views)));
|
||||
}
|
||||
else
|
||||
return default_sentinel;
|
||||
}
|
||||
|
||||
constexpr auto
|
||||
end() const requires (range<const _Vs> && ...) && __detail::__concatable<const _Vs...>
|
||||
{
|
||||
if constexpr (__detail::__last_is_common<const _Vs...>::value)
|
||||
{
|
||||
constexpr auto __n = sizeof...(_Vs);
|
||||
return iterator<true>(this, in_place_index<__n - 1>,
|
||||
ranges::end(std::get<__n - 1>(_M_views)));
|
||||
}
|
||||
else
|
||||
return default_sentinel;
|
||||
}
|
||||
|
||||
constexpr auto
|
||||
size() requires (sized_range<_Vs>&&...)
|
||||
{
|
||||
return std::apply([](auto... __sizes) {
|
||||
using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>;
|
||||
return (_CT(__sizes) + ...);
|
||||
}, __detail::__tuple_transform(ranges::size, _M_views));
|
||||
}
|
||||
|
||||
constexpr auto
|
||||
size() const requires (sized_range<const _Vs>&&...)
|
||||
{
|
||||
return std::apply([](auto... __sizes) {
|
||||
using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>;
|
||||
return (_CT(__sizes) + ...);
|
||||
}, __detail::__tuple_transform(ranges::size, _M_views));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... _Rs>
|
||||
concat_view(_Rs&&...) -> concat_view<views::all_t<_Rs>...>;
|
||||
|
||||
namespace __detail
|
||||
{
|
||||
template<bool _Const, typename... _Vs>
|
||||
struct __concat_view_iter_cat
|
||||
{ };
|
||||
|
||||
template<bool _Const, typename... _Vs>
|
||||
requires __detail::__all_forward<_Const, _Vs...>
|
||||
struct __concat_view_iter_cat<_Const, _Vs...>
|
||||
{
|
||||
static auto
|
||||
_S_iter_cat()
|
||||
{
|
||||
if constexpr (!is_reference_v<__concat_reference_t<__maybe_const_t<_Const, _Vs>...>>)
|
||||
return input_iterator_tag{};
|
||||
else
|
||||
return []<typename... _Cats>(_Cats... __cats) {
|
||||
if constexpr ((derived_from<_Cats, random_access_iterator_tag> && ...)
|
||||
&& __concat_is_random_access<_Const, _Vs...>)
|
||||
return random_access_iterator_tag{};
|
||||
else if constexpr ((derived_from<_Cats, bidirectional_iterator_tag> && ...)
|
||||
&& __concat_is_bidirectional<_Const, _Vs...>)
|
||||
return bidirectional_iterator_tag{};
|
||||
else if constexpr ((derived_from<_Cats, forward_iterator_tag> && ...))
|
||||
return forward_iterator_tag{};
|
||||
else
|
||||
return input_iterator_tag{};
|
||||
}(typename iterator_traits<iterator_t<__maybe_const_t<_Const, _Vs>>>
|
||||
::iterator_category{}...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<input_range... _Vs>
|
||||
requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...>
|
||||
template<bool _Const>
|
||||
class concat_view<_Vs...>::iterator
|
||||
: public __detail::__concat_view_iter_cat<_Const, _Vs...>
|
||||
{
|
||||
static auto
|
||||
_S_iter_concept()
|
||||
{
|
||||
if constexpr (__detail::__concat_is_random_access<_Const, _Vs...>)
|
||||
return random_access_iterator_tag{};
|
||||
else if constexpr (__detail::__concat_is_bidirectional<_Const, _Vs...>)
|
||||
return bidirectional_iterator_tag{};
|
||||
else if constexpr (__detail::__all_forward<_Const, _Vs...>)
|
||||
return forward_iterator_tag{};
|
||||
else
|
||||
return input_iterator_tag{};
|
||||
}
|
||||
|
||||
friend concat_view;
|
||||
friend iterator<!_Const>;
|
||||
|
||||
public:
|
||||
// iterator_category defined in __concat_view_iter_cat
|
||||
using iterator_concept = decltype(_S_iter_concept());
|
||||
using value_type = __detail::__concat_value_t<__maybe_const_t<_Const, _Vs>...>;
|
||||
using difference_type = common_type_t<range_difference_t<__maybe_const_t<_Const, _Vs>>...>;
|
||||
|
||||
private:
|
||||
using __base_iter = variant<iterator_t<__maybe_const_t<_Const, _Vs>>...>;
|
||||
|
||||
__maybe_const_t<_Const, concat_view>* _M_parent = nullptr;
|
||||
__base_iter _M_it;
|
||||
|
||||
template<size_t _Nm>
|
||||
constexpr void
|
||||
_M_satisfy()
|
||||
{
|
||||
if constexpr (_Nm < (sizeof...(_Vs) - 1))
|
||||
{
|
||||
if (std::get<_Nm>(_M_it) == ranges::end(std::get<_Nm>(_M_parent->_M_views)))
|
||||
{
|
||||
_M_it.template emplace<_Nm + 1>(ranges::begin
|
||||
(std::get<_Nm + 1>(_M_parent->_M_views)));
|
||||
_M_satisfy<_Nm + 1>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t _Nm>
|
||||
constexpr void
|
||||
_M_prev()
|
||||
{
|
||||
if constexpr (_Nm == 0)
|
||||
--std::get<0>(_M_it);
|
||||
else
|
||||
{
|
||||
if (std::get<_Nm>(_M_it) == ranges::begin(std::get<_Nm>(_M_parent->_M_views)))
|
||||
{
|
||||
_M_it.template emplace<_Nm - 1>(ranges::end
|
||||
(std::get<_Nm - 1>(_M_parent->_M_views)));
|
||||
_M_prev<_Nm - 1>();
|
||||
}
|
||||
else
|
||||
--std::get<_Nm>(_M_it);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t _Nm>
|
||||
constexpr void
|
||||
_M_advance_fwd(difference_type __offset, difference_type __steps)
|
||||
{
|
||||
using _Dt = iter_difference_t<variant_alternative_t<_Nm, __base_iter>>;
|
||||
if constexpr (_Nm == sizeof...(_Vs) - 1)
|
||||
std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps);
|
||||
else
|
||||
{
|
||||
auto __n_size = ranges::distance(std::get<_Nm>(_M_parent->_M_views));
|
||||
if (__offset + __steps < __n_size)
|
||||
std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps);
|
||||
else
|
||||
{
|
||||
_M_it.template emplace<_Nm + 1>(ranges::begin
|
||||
(std::get<_Nm + 1>(_M_parent->_M_views)));
|
||||
_M_advance_fwd<_Nm + 1>(0, __offset + __steps - __n_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t _Nm>
|
||||
constexpr void
|
||||
_M_advance_bwd(difference_type __offset, difference_type __steps)
|
||||
{
|
||||
using _Dt = iter_difference_t<variant_alternative_t<_Nm, __base_iter>>;
|
||||
if constexpr (_Nm == 0)
|
||||
std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps);
|
||||
else {
|
||||
if (__offset >= __steps)
|
||||
std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps);
|
||||
else
|
||||
{
|
||||
auto __prev_size = ranges::distance(std::get<_Nm - 1>(_M_parent->_M_views));
|
||||
_M_it.template emplace<_Nm - 1>(ranges::end
|
||||
(std::get<_Nm - 1>(_M_parent->_M_views)));
|
||||
_M_advance_bwd<_Nm - 1>(__prev_size, __steps - __offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke the function object __f, which has a call operator with a size_t
|
||||
// template parameter (corresponding to an index into the pack of views),
|
||||
// using the runtime value of __index as the template argument.
|
||||
template<typename _Fp>
|
||||
static constexpr auto
|
||||
_S_invoke_with_runtime_index(_Fp&& __f, size_t __index)
|
||||
{
|
||||
return [&__f, __index]<size_t _Idx>(this auto&& __self) {
|
||||
if (_Idx == __index)
|
||||
return __f.template operator()<_Idx>();
|
||||
if constexpr (_Idx + 1 < sizeof...(_Vs))
|
||||
return __self.template operator()<_Idx + 1>();
|
||||
}.template operator()<0>();
|
||||
}
|
||||
|
||||
template<typename _Fp>
|
||||
constexpr auto
|
||||
_M_invoke_with_runtime_index(_Fp&& __f)
|
||||
{ return _S_invoke_with_runtime_index(std::forward<_Fp>(__f), _M_it.index()); }
|
||||
|
||||
template<typename... _Args>
|
||||
explicit constexpr
|
||||
iterator(__maybe_const_t<_Const, concat_view>* __parent, _Args&&... __args)
|
||||
requires constructible_from<__base_iter, _Args&&...>
|
||||
: _M_parent(__parent), _M_it(std::forward<_Args>(__args)...)
|
||||
{ }
|
||||
|
||||
public:
|
||||
iterator() = default;
|
||||
|
||||
constexpr
|
||||
iterator(iterator<!_Const> __it)
|
||||
requires _Const && (convertible_to<iterator_t<_Vs>, iterator_t<const _Vs>> && ...)
|
||||
: _M_parent(__it._M_parent)
|
||||
{
|
||||
_M_invoke_with_runtime_index([this, &__it]<size_t _Idx>() {
|
||||
_M_it.template emplace<_Idx>(std::get<_Idx>(std::move(__it._M_it)));
|
||||
});
|
||||
}
|
||||
|
||||
constexpr decltype(auto)
|
||||
operator*() const
|
||||
{
|
||||
__glibcxx_assert(!_M_it.valueless_by_exception());
|
||||
using reference = __detail::__concat_reference_t<__maybe_const_t<_Const, _Vs>...>;
|
||||
return std::visit([](auto&& __it) -> reference { return *__it; }, _M_it);
|
||||
}
|
||||
|
||||
constexpr iterator&
|
||||
operator++()
|
||||
{
|
||||
_M_invoke_with_runtime_index([this]<size_t _Idx>() {
|
||||
++std::get<_Idx>(_M_it);
|
||||
_M_satisfy<_Idx>();
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr void
|
||||
operator++(int)
|
||||
{ ++*this; }
|
||||
|
||||
constexpr iterator
|
||||
operator++(int)
|
||||
requires __detail::__all_forward<_Const, _Vs...>
|
||||
{
|
||||
auto __tmp = *this;
|
||||
++*this;
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
constexpr iterator&
|
||||
operator--()
|
||||
requires __detail::__concat_is_bidirectional<_Const, _Vs...>
|
||||
{
|
||||
__glibcxx_assert(!_M_it.valueless_by_exception());
|
||||
_M_invoke_with_runtime_index([this]<size_t _Idx>() {
|
||||
_M_prev<_Idx>();
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr iterator
|
||||
operator--(int)
|
||||
requires __detail::__concat_is_bidirectional<_Const, _Vs...>
|
||||
{
|
||||
auto __tmp = *this;
|
||||
--*this;
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
constexpr iterator&
|
||||
operator+=(difference_type __n)
|
||||
requires __detail::__concat_is_random_access<_Const, _Vs...>
|
||||
{
|
||||
__glibcxx_assert(!_M_it.valueless_by_exception());
|
||||
_M_invoke_with_runtime_index([this, __n]<size_t _Idx>() {
|
||||
auto __begin = ranges::begin(std::get<_Idx>(_M_parent->_M_views));
|
||||
if (__n > 0)
|
||||
_M_advance_fwd<_Idx>(std::get<_Idx>(_M_it) - __begin, __n);
|
||||
else if (__n < 0)
|
||||
_M_advance_bwd<_Idx>(std::get<_Idx>(_M_it) - __begin, -__n);
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr iterator&
|
||||
operator-=(difference_type __n)
|
||||
requires __detail::__concat_is_random_access<_Const, _Vs...>
|
||||
{
|
||||
*this += -__n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr decltype(auto)
|
||||
operator[](difference_type __n) const
|
||||
requires __detail::__concat_is_random_access<_Const, _Vs...>
|
||||
{ return *((*this) + __n); }
|
||||
|
||||
friend constexpr bool
|
||||
operator==(const iterator& __x, const iterator& __y)
|
||||
requires (equality_comparable<iterator_t<__maybe_const_t<_Const, _Vs>>> && ...)
|
||||
{
|
||||
__glibcxx_assert(!__x._M_it.valueless_by_exception());
|
||||
__glibcxx_assert(!__y._M_it.valueless_by_exception());
|
||||
return __x._M_it == __y._M_it;
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
operator==(const iterator& __it, default_sentinel_t)
|
||||
{
|
||||
__glibcxx_assert(!__it._M_it.valueless_by_exception());
|
||||
constexpr auto __last_idx = sizeof...(_Vs) - 1;
|
||||
return (__it._M_it.index() == __last_idx
|
||||
&& (std::get<__last_idx>(__it._M_it)
|
||||
== ranges::end(std::get<__last_idx>(__it._M_parent->_M_views))));
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
operator<(const iterator& __x, const iterator& __y)
|
||||
requires __detail::__all_random_access<_Const, _Vs...>
|
||||
{ return __x._M_it < __y._M_it; }
|
||||
|
||||
friend constexpr bool
|
||||
operator>(const iterator& __x, const iterator& __y)
|
||||
requires __detail::__all_random_access<_Const, _Vs...>
|
||||
{ return __x._M_it > __y._M_it; }
|
||||
|
||||
friend constexpr bool
|
||||
operator<=(const iterator& __x, const iterator& __y)
|
||||
requires __detail::__all_random_access<_Const, _Vs...>
|
||||
{ return __x._M_it <= __y._M_it; }
|
||||
|
||||
friend constexpr bool
|
||||
operator>=(const iterator& __x, const iterator& __y)
|
||||
requires __detail::__all_random_access<_Const, _Vs...>
|
||||
{ return __x._M_it >= __y._M_it; }
|
||||
|
||||
friend constexpr auto
|
||||
operator<=>(const iterator& __x, const iterator& __y)
|
||||
requires __detail::__all_random_access<_Const, _Vs...>
|
||||
&& (three_way_comparable<iterator_t<__maybe_const_t<_Const, _Vs>>> && ...)
|
||||
{ return __x._M_it <=> __y._M_it; }
|
||||
|
||||
friend constexpr iterator
|
||||
operator+(const iterator& __it, difference_type __n)
|
||||
requires __detail::__concat_is_random_access<_Const, _Vs...>
|
||||
{ return auto(__it) += __n; }
|
||||
|
||||
friend constexpr iterator
|
||||
operator+(difference_type __n, const iterator& __it)
|
||||
requires __detail::__concat_is_random_access<_Const, _Vs...>
|
||||
{ return __it + __n; }
|
||||
|
||||
friend constexpr iterator
|
||||
operator-(const iterator& __it, difference_type __n)
|
||||
requires __detail::__concat_is_random_access<_Const, _Vs...>
|
||||
{ return auto(__it) -= __n; }
|
||||
|
||||
friend constexpr difference_type
|
||||
operator-(const iterator& __x, const iterator& __y)
|
||||
requires __detail::__concat_is_random_access<_Const, _Vs...>
|
||||
{
|
||||
return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> difference_type {
|
||||
return _S_invoke_with_runtime_index([&]<size_t _Iy>() -> difference_type {
|
||||
if constexpr (_Ix > _Iy)
|
||||
{
|
||||
auto __dy = ranges::distance(std::get<_Iy>(__y._M_it),
|
||||
ranges::end(std::get<_Iy>(__y._M_parent
|
||||
->_M_views)));
|
||||
auto __dx = ranges::distance(ranges::begin(std::get<_Ix>(__x._M_parent
|
||||
->_M_views)),
|
||||
std::get<_Ix>(__x._M_it));
|
||||
difference_type __s = 0;
|
||||
[&]<size_t _Idx = _Iy + 1>(this auto&& __self) {
|
||||
if constexpr (_Idx < _Ix)
|
||||
{
|
||||
__s += ranges::size(std::get<_Idx>(__x._M_parent->_M_views));
|
||||
__self.template operator()<_Idx + 1>();
|
||||
}
|
||||
}();
|
||||
return __dy + __s + __dx;
|
||||
}
|
||||
else if constexpr (_Ix < _Iy)
|
||||
return -(__y - __x);
|
||||
else
|
||||
return std::get<_Ix>(__x._M_it) - std::get<_Iy>(__y._M_it);
|
||||
}, __y._M_it.index());
|
||||
}, __x._M_it.index());
|
||||
}
|
||||
|
||||
friend constexpr difference_type
|
||||
operator-(const iterator& __x, default_sentinel_t)
|
||||
requires __detail::__concat_is_random_access<_Const, _Vs...>
|
||||
&& __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value
|
||||
{
|
||||
return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> difference_type {
|
||||
auto __dx = ranges::distance(std::get<_Ix>(__x._M_it),
|
||||
ranges::end(std::get<_Ix>(__x._M_parent->_M_views)));
|
||||
difference_type __s = 0;
|
||||
[&]<size_t _Idx = _Ix + 1>(this auto&& __self) {
|
||||
if constexpr (_Idx < sizeof...(_Vs))
|
||||
{
|
||||
__s += ranges::size(std::get<_Idx>(__x._M_parent->_M_views));
|
||||
__self.template operator()<_Idx + 1>();
|
||||
}
|
||||
}();
|
||||
return -(__dx + __s);
|
||||
}, __x._M_it.index());
|
||||
}
|
||||
|
||||
friend constexpr difference_type
|
||||
operator-(default_sentinel_t, const iterator& __x)
|
||||
requires __detail::__concat_is_random_access<_Const, _Vs...>
|
||||
&& __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value
|
||||
{ return -(__x - default_sentinel); }
|
||||
|
||||
friend constexpr decltype(auto)
|
||||
iter_move(const iterator& __it)
|
||||
{
|
||||
using _Res = __detail::__concat_rvalue_reference_t<__maybe_const_t<_Const, _Vs>...>;
|
||||
return std::visit([](const auto& __i) -> _Res {
|
||||
return ranges::iter_move(__i);
|
||||
}, __it._M_it);
|
||||
}
|
||||
|
||||
friend constexpr void
|
||||
iter_swap(const iterator& __x, const iterator& __y)
|
||||
requires swappable_with<iter_reference_t<iterator>, iter_reference_t<iterator>>
|
||||
&& (... && indirectly_swappable<iterator_t<__maybe_const_t<_Const, _Vs>>>)
|
||||
{
|
||||
std::visit([&]<typename _Tp, typename _Up>(const _Tp& __it1, const _Up& __it2) {
|
||||
if constexpr (is_same_v<_Tp, _Up>)
|
||||
ranges::iter_swap(__it1, __it2);
|
||||
else
|
||||
ranges::swap(*__it1, *__it2);
|
||||
}, __x._M_it, __y._M_it);
|
||||
}
|
||||
};
|
||||
|
||||
namespace views
|
||||
{
|
||||
namespace __detail
|
||||
{
|
||||
template<typename... _Ts>
|
||||
concept __can_concat_view = requires { concat_view(std::declval<_Ts>()...); };
|
||||
}
|
||||
|
||||
struct _Concat
|
||||
{
|
||||
template<typename... _Ts>
|
||||
requires __detail::__can_concat_view<_Ts...>
|
||||
constexpr auto
|
||||
operator() [[nodiscard]] (_Ts&&... __ts) const
|
||||
{
|
||||
if constexpr (sizeof...(_Ts) == 1)
|
||||
return views::all(std::forward<_Ts>(__ts)...);
|
||||
else
|
||||
return concat_view(std::forward<_Ts>(__ts)...);
|
||||
}
|
||||
};
|
||||
|
||||
inline constexpr _Concat concat;
|
||||
}
|
||||
|
||||
} // namespace ranges
|
||||
#endif // __cpp_lib_ranges_concat
|
||||
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
} // namespace std
|
||||
#endif // library concepts
|
||||
|
|
74
libstdc++-v3/testsuite/std/ranges/concat/1.cc
Normal file
74
libstdc++-v3/testsuite/std/ranges/concat/1.cc
Normal file
|
@ -0,0 +1,74 @@
|
|||
// { dg-do run { target c++26 } }
|
||||
// { dg-add-options no_pch }
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#if __cpp_lib_ranges_concat != 202403L
|
||||
# error "Feature-test macro __cpp_lib_ranges_concat has wrong value in <ranges>"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_iterators.h>
|
||||
|
||||
namespace ranges = std::ranges;
|
||||
namespace views = std::views;
|
||||
|
||||
constexpr bool
|
||||
test01()
|
||||
{
|
||||
std::vector<int> v1{1, 2, 3}, v2{4, 5}, v3{};
|
||||
std::array a{6, 7, 8};
|
||||
auto s = views::single(9);
|
||||
|
||||
auto v = views::concat(v1, v2, v3, a, s);
|
||||
VERIFY( ranges::size(v) == 9 );
|
||||
VERIFY( ranges::size(std::as_const(v)) == 9 );
|
||||
VERIFY( ranges::equal(v, views::iota(1, 10)) );
|
||||
VERIFY( ranges::equal(v | views::reverse,
|
||||
views::iota(1, 10) | views::reverse) );
|
||||
|
||||
auto it0 = v.begin();
|
||||
auto cit = std::as_const(v).begin();
|
||||
VERIFY( it0 == it0 );
|
||||
VERIFY( cit == cit );
|
||||
VERIFY( it0 == cit );
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
VERIFY( it0 + i - it0 == i );
|
||||
VERIFY( it0 + i - (it0 + 1) == i - 1 );
|
||||
VERIFY( it0 + i - (it0 + 3) == i - 3 );
|
||||
VERIFY( it0 + i - (it0 + 5) == i - 5 );
|
||||
VERIFY( it0 + i - i + i == it0 + i );
|
||||
VERIFY( it0 + i - (it0 + i) == 0 );
|
||||
}
|
||||
VERIFY( std::default_sentinel - it0 == 9 );
|
||||
VERIFY( it0 + 9 == std::default_sentinel );
|
||||
|
||||
auto it5 = it0+5;
|
||||
ranges::iter_swap(it0, it5);
|
||||
VERIFY( *it0 == 6 && *it5 == 1 );
|
||||
ranges::iter_swap(it0, it5);
|
||||
*it0 = ranges::iter_move(it0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
test02()
|
||||
{
|
||||
int x[] = {1, 2, 3, 4, 5};
|
||||
__gnu_test::test_input_range rx(x);
|
||||
auto v = views::concat(views::single(0), rx, views::empty<int>);
|
||||
static_assert(!ranges::forward_range<decltype(v)>);
|
||||
VERIFY( ranges::equal(v | views::drop(1), x) );
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
static_assert(test01());
|
||||
test02();
|
||||
}
|
Loading…
Add table
Reference in a new issue