[libc++] Implement views::join_with (#65536)
* Implement "P2441R2 `views::join_with`" (https://wg21.link/P2441R2), closes #105185 * Implement LWG4074 (https://wg21.link/LWG4074), closes #105346 * Complete implementation of "P2711R1 Making multi-param constructors of views explicit" (https://wg21.link/P2711R1), closes #105252 * Complete implementation of "P2770R0 Stashing stashing iterators for proper flattening" (https://wg21.link/P2770R0), closes #105250
This commit is contained in:
committed by
GitHub
parent
d6a486c221
commit
1bb2328fd3
@@ -376,7 +376,7 @@ Status
|
||||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_ranges_iota`` ``202202L``
|
||||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_ranges_join_with`` *unimplemented*
|
||||
``__cpp_lib_ranges_join_with`` ``202202L``
|
||||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_ranges_repeat`` ``202207L``
|
||||
---------------------------------------------------------- -----------------
|
||||
|
||||
@@ -47,6 +47,9 @@ Implemented Papers
|
||||
- P1222R4: A Standard ``flat_set`` (`Github <https://github.com/llvm/llvm-project/issues/105193>`__)
|
||||
- P2897R7: ``aligned_accessor``: An mdspan accessor expressing pointer over-alignment (`Github <https://github.com/llvm/llvm-project/issues/118372>`__)
|
||||
- P3247R2: Deprecate the notion of trivial types (`Github <https://github.com/llvm/llvm-project/issues/118387>`__)
|
||||
- P2441R2: ``views::join_with`` (`Github <https://github.com/llvm/llvm-project/issues/105185>`__)
|
||||
- P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github <https://github.com/llvm/llvm-project/issues/105252>`__)
|
||||
- P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github <https://github.com/llvm/llvm-project/issues/105250>`__)
|
||||
|
||||
Improvements and New Features
|
||||
-----------------------------
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16",""
|
||||
"`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19",""
|
||||
"`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","Only ``ranges::iota`` is implemented."
|
||||
"`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|In Progress|","",""
|
||||
"`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21",""
|
||||
"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","",""
|
||||
"`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18",""
|
||||
"","","","","",""
|
||||
@@ -103,9 +103,9 @@
|
||||
"`P2708R1 <https://wg21.link/P2708R1>`__","No Further Fundamentals TSes","2022-11 (Kona)","|Nothing To Do|","",""
|
||||
"","","","","",""
|
||||
"`P0290R4 <https://wg21.link/P0290R4>`__","``apply()`` for ``synchronized_value<T>``","2023-02 (Issaquah)","","",""
|
||||
"`P2770R0 <https://wg21.link/P2770R0>`__","Stashing stashing ``iterators`` for proper flattening","2023-02 (Issaquah)","|Partial|","","``join_with_view`` hasn't been done yet since this type isn't implemented yet"
|
||||
"`P2770R0 <https://wg21.link/P2770R0>`__","Stashing stashing ``iterators`` for proper flattening","2023-02 (Issaquah)","|Complete|","21",""
|
||||
"`P2164R9 <https://wg21.link/P2164R9>`__","``views::enumerate``","2023-02 (Issaquah)","","",""
|
||||
"`P2711R1 <https://wg21.link/P2711R1>`__","Making multi-param constructors of ``views`` ``explicit``","2023-02 (Issaquah)","|In Progress|","","``join_with_view`` hasn't been done yet since this type isn't implemented yet"
|
||||
"`P2711R1 <https://wg21.link/P2711R1>`__","Making multi-param constructors of ``views`` ``explicit``","2023-02 (Issaquah)","|Complete|","21",""
|
||||
"`P2609R3 <https://wg21.link/P2609R3>`__","Relaxing Ranges Just A Smidge","2023-02 (Issaquah)","|Complete|","20","Implemented as a DR in C++20. Other implementations will do the same."
|
||||
"`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","2023-02 (Issaquah)","|Complete|","19",""
|
||||
"`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","2023-02 (Issaquah)","|Complete|","17",""
|
||||
|
||||
|
@@ -66,7 +66,7 @@
|
||||
"`LWG4060 <https://wg21.link/LWG4060>`__","``submdspan`` preconditions do not forbid creating invalid pointer","2024-06 (St. Louis)","","",""
|
||||
"`LWG4061 <https://wg21.link/LWG4061>`__","Should ``std::basic_format_context`` be default-constructible/copyable/movable?","2024-06 (St. Louis)","|Complete|","19",""
|
||||
"`LWG4071 <https://wg21.link/LWG4071>`__","``reference_wrapper`` comparisons are not SFINAE-friendly","2024-06 (St. Louis)","|Complete|","19",""
|
||||
"`LWG4074 <https://wg21.link/LWG4074>`__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","","",""
|
||||
"`LWG4074 <https://wg21.link/LWG4074>`__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","|Complete|","21",""
|
||||
"`LWG4076 <https://wg21.link/LWG4076>`__","``concat_view`` should be freestanding","2024-06 (St. Louis)","","",""
|
||||
"`LWG4079 <https://wg21.link/LWG4079>`__","Missing Preconditions in ``concat_view::iterator``\`s conversion constructor","2024-06 (St. Louis)","","",""
|
||||
"`LWG4082 <https://wg21.link/LWG4082>`__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","","",""
|
||||
|
||||
|
@@ -706,6 +706,7 @@ set(files
|
||||
__ranges/iota_view.h
|
||||
__ranges/istream_view.h
|
||||
__ranges/join_view.h
|
||||
__ranges/join_with_view.h
|
||||
__ranges/lazy_split_view.h
|
||||
__ranges/movable_box.h
|
||||
__ranges/non_propagating_cache.h
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#ifndef _LIBCPP___RANGES_CONCEPTS_H
|
||||
#define _LIBCPP___RANGES_CONCEPTS_H
|
||||
|
||||
#include <__concepts/common_reference_with.h>
|
||||
#include <__concepts/constructible.h>
|
||||
#include <__concepts/convertible_to.h>
|
||||
#include <__concepts/movable.h>
|
||||
#include <__concepts/same_as.h>
|
||||
#include <__config>
|
||||
@@ -25,6 +27,8 @@
|
||||
#include <__ranges/enable_view.h>
|
||||
#include <__ranges/size.h>
|
||||
#include <__type_traits/add_pointer.h>
|
||||
#include <__type_traits/common_reference.h>
|
||||
#include <__type_traits/common_type.h>
|
||||
#include <__type_traits/is_reference.h>
|
||||
#include <__type_traits/remove_cvref.h>
|
||||
#include <__type_traits/remove_reference.h>
|
||||
@@ -133,6 +137,42 @@ concept viewable_range =
|
||||
(is_lvalue_reference_v<_Tp> ||
|
||||
(movable<remove_reference_t<_Tp>> && !__is_std_initializer_list<remove_cvref_t<_Tp>>))));
|
||||
|
||||
# if _LIBCPP_STD_VER >= 23
|
||||
|
||||
template <class... _Rs>
|
||||
using __concat_reference_t _LIBCPP_NODEBUG = common_reference_t<range_reference_t<_Rs>...>;
|
||||
|
||||
template <class... _Rs>
|
||||
using __concat_value_t _LIBCPP_NODEBUG = common_type_t<range_value_t<_Rs>...>;
|
||||
|
||||
template <class... _Rs>
|
||||
using __concat_rvalue_reference_t _LIBCPP_NODEBUG = common_reference_t<range_rvalue_reference_t<_Rs>...>;
|
||||
|
||||
template <class _Ref, class _RRef, class _It>
|
||||
concept __concat_indirectly_readable_impl = requires(const _It __it) {
|
||||
{ *__it } -> convertible_to<_Ref>;
|
||||
{ ranges::iter_move(__it) } -> convertible_to<_RRef>;
|
||||
};
|
||||
|
||||
template <class... _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...>&&, const __concat_value_t<_Rs...>&> &&
|
||||
(__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>,
|
||||
__concat_rvalue_reference_t<_Rs...>,
|
||||
iterator_t<_Rs>> &&
|
||||
...);
|
||||
|
||||
template <class... _Rs>
|
||||
concept __concatable = requires {
|
||||
typename __concat_reference_t<_Rs...>;
|
||||
typename __concat_value_t<_Rs...>;
|
||||
typename __concat_rvalue_reference_t<_Rs...>;
|
||||
} && __concat_indirectly_readable<_Rs...>;
|
||||
|
||||
# endif // _LIBCPP_STD_VER >= 23
|
||||
|
||||
} // namespace ranges
|
||||
|
||||
#endif // _LIBCPP_STD_VER >= 20
|
||||
|
||||
460
libcxx/include/__ranges/join_with_view.h
Normal file
460
libcxx/include/__ranges/join_with_view.h
Normal file
@@ -0,0 +1,460 @@
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___RANGES_JOIN_WITH_VIEW_H
|
||||
#define _LIBCPP___RANGES_JOIN_WITH_VIEW_H
|
||||
|
||||
#include <__concepts/common_reference_with.h>
|
||||
#include <__concepts/common_with.h>
|
||||
#include <__concepts/constructible.h>
|
||||
#include <__concepts/convertible_to.h>
|
||||
#include <__concepts/derived_from.h>
|
||||
#include <__concepts/equality_comparable.h>
|
||||
#include <__config>
|
||||
#include <__functional/bind_back.h>
|
||||
#include <__iterator/concepts.h>
|
||||
#include <__iterator/incrementable_traits.h>
|
||||
#include <__iterator/iter_move.h>
|
||||
#include <__iterator/iter_swap.h>
|
||||
#include <__iterator/iterator_traits.h>
|
||||
#include <__memory/addressof.h>
|
||||
#include <__ranges/access.h>
|
||||
#include <__ranges/all.h>
|
||||
#include <__ranges/concepts.h>
|
||||
#include <__ranges/non_propagating_cache.h>
|
||||
#include <__ranges/range_adaptor.h>
|
||||
#include <__ranges/single_view.h>
|
||||
#include <__ranges/view_interface.h>
|
||||
#include <__type_traits/conditional.h>
|
||||
#include <__type_traits/decay.h>
|
||||
#include <__type_traits/is_reference.h>
|
||||
#include <__type_traits/maybe_const.h>
|
||||
#include <__utility/as_const.h>
|
||||
#include <__utility/as_lvalue.h>
|
||||
#include <__utility/empty.h>
|
||||
#include <__utility/forward.h>
|
||||
#include <__utility/move.h>
|
||||
#include <variant>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_PUSH_MACROS
|
||||
#include <__undef_macros>
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER >= 23
|
||||
|
||||
namespace ranges {
|
||||
template <class _Range>
|
||||
concept __bidirectional_common = bidirectional_range<_Range> && common_range<_Range>;
|
||||
|
||||
template <input_range _View, forward_range _Pattern>
|
||||
requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
|
||||
__concatable<range_reference_t<_View>, _Pattern>
|
||||
class join_with_view : public view_interface<join_with_view<_View, _Pattern>> {
|
||||
using _InnerRng _LIBCPP_NODEBUG = range_reference_t<_View>;
|
||||
|
||||
_LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
|
||||
|
||||
static constexpr bool _UseOuterItCache = !forward_range<_View>;
|
||||
using _OuterItCache _LIBCPP_NODEBUG =
|
||||
_If<_UseOuterItCache, __non_propagating_cache<iterator_t<_View>>, __empty_cache>;
|
||||
_LIBCPP_NO_UNIQUE_ADDRESS _OuterItCache __outer_it_;
|
||||
|
||||
static constexpr bool _UseInnerCache = !is_reference_v<_InnerRng>;
|
||||
using _InnerCache _LIBCPP_NODEBUG =
|
||||
_If<_UseInnerCache, __non_propagating_cache<remove_cvref_t<_InnerRng>>, __empty_cache>;
|
||||
_LIBCPP_NO_UNIQUE_ADDRESS _InnerCache __inner_;
|
||||
|
||||
_LIBCPP_NO_UNIQUE_ADDRESS _Pattern __pattern_ = _Pattern();
|
||||
|
||||
template <bool _Const>
|
||||
struct __iterator;
|
||||
|
||||
template <bool _Const>
|
||||
struct __sentinel;
|
||||
|
||||
public:
|
||||
_LIBCPP_HIDE_FROM_ABI join_with_view()
|
||||
requires default_initializable<_View> && default_initializable<_Pattern>
|
||||
= default;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr explicit join_with_view(_View __base, _Pattern __pattern)
|
||||
: __base_(std::move(__base)), __pattern_(std::move(__pattern)) {}
|
||||
|
||||
template <input_range _Range>
|
||||
requires constructible_from<_View, views::all_t<_Range>> &&
|
||||
constructible_from<_Pattern, single_view<range_value_t<_InnerRng>>>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr explicit join_with_view(_Range&& __r, range_value_t<_InnerRng> __e)
|
||||
: __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
|
||||
requires copy_constructible<_View>
|
||||
{
|
||||
return __base_;
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
|
||||
if constexpr (forward_range<_View>) {
|
||||
constexpr bool __use_const = __simple_view<_View> && is_reference_v<_InnerRng> && __simple_view<_Pattern>;
|
||||
return __iterator<__use_const>{*this, ranges::begin(__base_)};
|
||||
} else {
|
||||
__outer_it_.__emplace(ranges::begin(__base_));
|
||||
return __iterator<false>{*this};
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
|
||||
requires forward_range<const _View> && forward_range<const _Pattern> &&
|
||||
is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>> &&
|
||||
__concatable<range_reference_t<const _View>, const _Pattern>
|
||||
{
|
||||
return __iterator<true>{*this, ranges::begin(__base_)};
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
|
||||
constexpr bool __use_const = __simple_view<_View> && __simple_view<_Pattern>;
|
||||
if constexpr (forward_range<_View> && is_reference_v<_InnerRng> && forward_range<_InnerRng> &&
|
||||
common_range<_View> && common_range<_InnerRng>)
|
||||
return __iterator<__use_const>{*this, ranges::end(__base_)};
|
||||
else
|
||||
return __sentinel<__use_const>{*this};
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
|
||||
requires forward_range<const _View> && forward_range<const _Pattern> &&
|
||||
is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>> &&
|
||||
__concatable<range_reference_t<const _View>, const _Pattern>
|
||||
{
|
||||
using _InnerConstRng = range_reference_t<const _View>;
|
||||
if constexpr (forward_range<_InnerConstRng> && common_range<const _View> && common_range<_InnerConstRng>)
|
||||
return __iterator<true>{*this, ranges::end(__base_)};
|
||||
else
|
||||
return __sentinel<true>{*this};
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Range, class _Pattern>
|
||||
join_with_view(_Range&&, _Pattern&&) -> join_with_view<views::all_t<_Range>, views::all_t<_Pattern>>;
|
||||
|
||||
template <input_range _Range>
|
||||
join_with_view(_Range&&, range_value_t<range_reference_t<_Range>>)
|
||||
-> join_with_view<views::all_t<_Range>, single_view<range_value_t<range_reference_t<_Range>>>>;
|
||||
|
||||
template <class _Base, class _PatternBase, class _InnerBase = range_reference_t<_Base>>
|
||||
struct __join_with_view_iterator_category {};
|
||||
|
||||
template <class _Base, class _PatternBase, class _InnerBase>
|
||||
requires is_reference_v<_InnerBase> && forward_range<_Base> && forward_range<_InnerBase>
|
||||
struct __join_with_view_iterator_category<_Base, _PatternBase, _InnerBase> {
|
||||
private:
|
||||
static consteval auto __get_iterator_category() noexcept {
|
||||
using _OuterC = iterator_traits<iterator_t<_Base>>::iterator_category;
|
||||
using _InnerC = iterator_traits<iterator_t<_InnerBase>>::iterator_category;
|
||||
using _PatternC = iterator_traits<iterator_t<_PatternBase>>::iterator_category;
|
||||
|
||||
if constexpr (!is_reference_v<common_reference_t<iter_reference_t<iterator_t<_InnerBase>>,
|
||||
iter_reference_t<iterator_t<_PatternBase>>>>)
|
||||
return input_iterator_tag{};
|
||||
else if constexpr (derived_from<_OuterC, bidirectional_iterator_tag> &&
|
||||
derived_from<_InnerC, bidirectional_iterator_tag> &&
|
||||
derived_from<_PatternC, bidirectional_iterator_tag> && common_range<_InnerBase> &&
|
||||
common_range<_PatternBase>)
|
||||
return bidirectional_iterator_tag{};
|
||||
else if constexpr (derived_from<_OuterC, forward_iterator_tag> && derived_from<_InnerC, forward_iterator_tag> &&
|
||||
derived_from<_PatternC, forward_iterator_tag>)
|
||||
return forward_iterator_tag{};
|
||||
else
|
||||
return input_iterator_tag{};
|
||||
}
|
||||
|
||||
public:
|
||||
using iterator_category = decltype(__get_iterator_category());
|
||||
};
|
||||
|
||||
template <input_range _View, forward_range _Pattern>
|
||||
requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
|
||||
__concatable<range_reference_t<_View>, _Pattern>
|
||||
template <bool _Const>
|
||||
struct join_with_view<_View, _Pattern>::__iterator
|
||||
: public __join_with_view_iterator_category<__maybe_const<_Const, _View>, __maybe_const<_Const, _Pattern>> {
|
||||
private:
|
||||
friend join_with_view;
|
||||
|
||||
using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, join_with_view>;
|
||||
using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
|
||||
using _InnerBase _LIBCPP_NODEBUG = range_reference_t<_Base>;
|
||||
using _PatternBase _LIBCPP_NODEBUG = __maybe_const<_Const, _Pattern>;
|
||||
|
||||
using _OuterIter _LIBCPP_NODEBUG = iterator_t<_Base>;
|
||||
using _InnerIter _LIBCPP_NODEBUG = iterator_t<_InnerBase>;
|
||||
using _PatternIter _LIBCPP_NODEBUG = iterator_t<_PatternBase>;
|
||||
|
||||
static_assert(!_Const || forward_range<_Base>, "Const can only be true when Base models forward_range.");
|
||||
|
||||
static constexpr bool __ref_is_glvalue = is_reference_v<_InnerBase>;
|
||||
|
||||
_Parent* __parent_ = nullptr;
|
||||
|
||||
static constexpr bool _OuterIterPresent = forward_range<_Base>;
|
||||
using _OuterIterType _LIBCPP_NODEBUG = _If<_OuterIterPresent, _OuterIter, std::__empty>;
|
||||
_LIBCPP_NO_UNIQUE_ADDRESS _OuterIterType __outer_it_ = _OuterIterType();
|
||||
|
||||
variant<_PatternIter, _InnerIter> __inner_it_;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, _OuterIter __outer)
|
||||
requires forward_range<_Base>
|
||||
: __parent_(std::addressof(__parent)), __outer_it_(std::move(__outer)) {
|
||||
if (__get_outer() != ranges::end(__parent_->__base_)) {
|
||||
__inner_it_.template emplace<1>(ranges::begin(__update_inner()));
|
||||
__satisfy();
|
||||
}
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Parent& __parent)
|
||||
requires(!forward_range<_Base>)
|
||||
: __parent_(std::addressof(__parent)) {
|
||||
if (__get_outer() != ranges::end(__parent_->__base_)) {
|
||||
__inner_it_.template emplace<1>(ranges::begin(__update_inner()));
|
||||
__satisfy();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _OuterIter& __get_outer() {
|
||||
if constexpr (forward_range<_Base>)
|
||||
return __outer_it_;
|
||||
else
|
||||
return *__parent_->__outer_it_;
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _OuterIter& __get_outer() const {
|
||||
if constexpr (forward_range<_Base>)
|
||||
return __outer_it_;
|
||||
else
|
||||
return *__parent_->__outer_it_;
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __update_inner() {
|
||||
if constexpr (__ref_is_glvalue)
|
||||
return std::__as_lvalue(*__get_outer());
|
||||
else
|
||||
return __parent_->__inner_.__emplace_from([this]() -> decltype(auto) { return *__get_outer(); });
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __get_inner() {
|
||||
if constexpr (__ref_is_glvalue)
|
||||
return std::__as_lvalue(*__get_outer());
|
||||
else
|
||||
return *__parent_->__inner_;
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr void __satisfy() {
|
||||
while (true) {
|
||||
if (__inner_it_.index() == 0) {
|
||||
if (std::get<0>(__inner_it_) != ranges::end(__parent_->__pattern_))
|
||||
break;
|
||||
|
||||
__inner_it_.template emplace<1>(ranges::begin(__update_inner()));
|
||||
} else {
|
||||
if (std::get<1>(__inner_it_) != ranges::end(__get_inner()))
|
||||
break;
|
||||
|
||||
if (++__get_outer() == ranges::end(__parent_->__base_)) {
|
||||
if constexpr (__ref_is_glvalue)
|
||||
__inner_it_.template emplace<0>();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
__inner_it_.template emplace<0>(ranges::begin(__parent_->__pattern_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static consteval auto __get_iterator_concept() noexcept {
|
||||
if constexpr (__ref_is_glvalue && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> &&
|
||||
__bidirectional_common<_PatternBase>)
|
||||
return bidirectional_iterator_tag{};
|
||||
else if constexpr (__ref_is_glvalue && forward_range<_Base> && forward_range<_InnerBase>)
|
||||
return forward_iterator_tag{};
|
||||
else
|
||||
return input_iterator_tag{};
|
||||
}
|
||||
|
||||
public:
|
||||
using iterator_concept = decltype(__get_iterator_concept());
|
||||
using value_type = common_type_t<iter_value_t<_InnerIter>, iter_value_t<_PatternIter>>;
|
||||
using difference_type =
|
||||
common_type_t<iter_difference_t<_OuterIter>, iter_difference_t<_InnerIter>, iter_difference_t<_PatternIter>>;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI __iterator() = default;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
|
||||
requires _Const && convertible_to<iterator_t<_View>, _OuterIter> &&
|
||||
convertible_to<iterator_t<_InnerRng>, _InnerIter> && convertible_to<iterator_t<_Pattern>, _PatternIter>
|
||||
: __parent_(__i.__parent_), __outer_it_(std::move(__i.__outer_it_)) {
|
||||
if (__i.__inner_it_.index() == 0) {
|
||||
__inner_it_.template emplace<0>(std::get<0>(std::move(__i.__inner_it_)));
|
||||
} else {
|
||||
__inner_it_.template emplace<1>(std::get<1>(std::move(__i.__inner_it_)));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const {
|
||||
using __reference = common_reference_t<iter_reference_t<_InnerIter>, iter_reference_t<_PatternIter>>;
|
||||
return std::visit([](auto& __it) -> __reference { return *__it; }, __inner_it_);
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
|
||||
std::visit([](auto& __it) { ++__it; }, __inner_it_);
|
||||
__satisfy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
|
||||
requires __ref_is_glvalue && forward_iterator<_OuterIter> && forward_iterator<_InnerIter>
|
||||
{
|
||||
__iterator __tmp = *this;
|
||||
++*this;
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
|
||||
requires __ref_is_glvalue
|
||||
&& bidirectional_range<_Base> && __bidirectional_common<_InnerBase> && __bidirectional_common<_PatternBase>
|
||||
{
|
||||
if (__outer_it_ == ranges::end(__parent_->__base_)) {
|
||||
auto&& __inner = *--__outer_it_;
|
||||
__inner_it_.template emplace<1>(ranges::end(__inner));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (__inner_it_.index() == 0) {
|
||||
auto& __it = std::get<0>(__inner_it_);
|
||||
if (__it == ranges::begin(__parent_->__pattern_)) {
|
||||
auto&& __inner = *--__outer_it_;
|
||||
__inner_it_.template emplace<1>(ranges::end(__inner));
|
||||
} else
|
||||
break;
|
||||
} else {
|
||||
auto& __it = std::get<1>(__inner_it_);
|
||||
auto&& __inner = *__outer_it_;
|
||||
if (__it == ranges::begin(__inner))
|
||||
__inner_it_.template emplace<0>(ranges::end(__parent_->__pattern_));
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::visit([](auto& __it) { --__it; }, __inner_it_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
|
||||
requires __ref_is_glvalue
|
||||
&& bidirectional_range<_Base> && __bidirectional_common<_InnerBase> && __bidirectional_common<_PatternBase>
|
||||
{
|
||||
__iterator __tmp = *this;
|
||||
--*this;
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
|
||||
requires __ref_is_glvalue && forward_range<_Base> && equality_comparable<_InnerIter>
|
||||
{
|
||||
return __x.__outer_it_ == __y.__outer_it_ && __x.__inner_it_ == __y.__inner_it_;
|
||||
}
|
||||
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto) iter_move(const __iterator& __x) {
|
||||
using __rvalue_reference =
|
||||
common_reference_t<iter_rvalue_reference_t<_InnerIter>, iter_rvalue_reference_t<_PatternIter>>;
|
||||
return std::visit<__rvalue_reference>(ranges::iter_move, __x.__inner_it_);
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y)
|
||||
requires indirectly_swappable<_InnerIter, _PatternIter>
|
||||
{
|
||||
std::visit(ranges::iter_swap, __x.__inner_it_, __y.__inner_it_);
|
||||
}
|
||||
};
|
||||
|
||||
template <input_range _View, forward_range _Pattern>
|
||||
requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
|
||||
__concatable<range_reference_t<_View>, _Pattern>
|
||||
template <bool _Const>
|
||||
struct join_with_view<_View, _Pattern>::__sentinel {
|
||||
private:
|
||||
friend join_with_view;
|
||||
|
||||
using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, join_with_view>;
|
||||
using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
|
||||
|
||||
_LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
|
||||
|
||||
template <bool _OtherConst>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto& __get_outer_of(const __iterator<_OtherConst>& __x) {
|
||||
return __x.__get_outer();
|
||||
}
|
||||
|
||||
public:
|
||||
_LIBCPP_HIDE_FROM_ABI __sentinel() = default;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
|
||||
requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
|
||||
: __end_(std::move(__s.__end_)) {}
|
||||
|
||||
template <bool _OtherConst>
|
||||
requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
|
||||
operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
|
||||
return __get_outer_of(__x) == __y.__end_;
|
||||
}
|
||||
};
|
||||
|
||||
namespace views {
|
||||
namespace __join_with_view {
|
||||
struct __fn {
|
||||
template <class _Range, class _Pattern>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pattern&& __pattern) const
|
||||
noexcept(noexcept(/**/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))))
|
||||
-> decltype(/*--*/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))) {
|
||||
return /*-------------*/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern));
|
||||
}
|
||||
|
||||
template <class _Pattern>
|
||||
requires constructible_from<decay_t<_Pattern>, _Pattern>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pattern&& __pattern) const
|
||||
noexcept(is_nothrow_constructible_v<decay_t<_Pattern>, _Pattern>) {
|
||||
return __pipeable(std::__bind_back(*this, std::forward<_Pattern>(__pattern)));
|
||||
}
|
||||
};
|
||||
} // namespace __join_with_view
|
||||
|
||||
inline namespace __cpo {
|
||||
inline constexpr auto join_with = __join_with_view::__fn{};
|
||||
} // namespace __cpo
|
||||
} // namespace views
|
||||
} // namespace ranges
|
||||
|
||||
#endif // _LIBCPP_STD_VER >= 23
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
_LIBCPP_POP_MACROS
|
||||
|
||||
#endif // _LIBCPP___RANGES_JOIN_WITH_VIEW_H
|
||||
@@ -1865,6 +1865,7 @@ module std [system] {
|
||||
module iota_view { header "__ranges/iota_view.h" }
|
||||
module istream_view { header "__ranges/istream_view.h" }
|
||||
module join_view { header "__ranges/join_view.h" }
|
||||
module join_with_view { header "__ranges/join_with_view.h" }
|
||||
module lazy_split_view {
|
||||
header "__ranges/lazy_split_view.h"
|
||||
export std.functional.bind_back
|
||||
|
||||
@@ -285,6 +285,15 @@ namespace std::ranges {
|
||||
requires view<V> && input_range<range_reference_t<V>>
|
||||
class join_view;
|
||||
|
||||
// [range.join.with], join with view
|
||||
template<input_range V, forward_range Pattern>
|
||||
requires view<V> && input_range<range_reference_t<V>>
|
||||
&& view<Pattern>
|
||||
&& concatable<range_reference_t<V>, Pattern>
|
||||
class join_with_view; // since C++23
|
||||
|
||||
namespace views { inline constexpr unspecified join_with = unspecified; } // since C++23
|
||||
|
||||
// [range.lazy.split], lazy split view
|
||||
template<class R>
|
||||
concept tiny-range = see below; // exposition only
|
||||
@@ -427,6 +436,7 @@ namespace std {
|
||||
# include <__ranges/as_rvalue_view.h>
|
||||
# include <__ranges/chunk_by_view.h>
|
||||
# include <__ranges/from_range.h>
|
||||
# include <__ranges/join_with_view.h>
|
||||
# include <__ranges/repeat_view.h>
|
||||
# include <__ranges/to.h>
|
||||
# include <__ranges/zip_view.h>
|
||||
|
||||
@@ -519,7 +519,7 @@ __cpp_lib_void_t 201411L <type_traits>
|
||||
# define __cpp_lib_ranges_contains 202207L
|
||||
# define __cpp_lib_ranges_find_last 202207L
|
||||
# define __cpp_lib_ranges_iota 202202L
|
||||
// # define __cpp_lib_ranges_join_with 202202L
|
||||
# define __cpp_lib_ranges_join_with 202202L
|
||||
# define __cpp_lib_ranges_repeat 202207L
|
||||
// # define __cpp_lib_ranges_slide 202202L
|
||||
# define __cpp_lib_ranges_starts_ends_with 202106L
|
||||
|
||||
@@ -223,13 +223,16 @@ export namespace std {
|
||||
namespace views {
|
||||
using std::ranges::views::join;
|
||||
} // namespace views
|
||||
#if 0
|
||||
|
||||
#if _LIBCPP_STD_VER >= 23
|
||||
// [range.join.with]
|
||||
using std::ranges::join_with_view;
|
||||
|
||||
namespace views {
|
||||
using std::ranges::views::join_with;
|
||||
} // namespace views
|
||||
#endif
|
||||
#endif // _LIBCPP_STD_VER >= 23
|
||||
|
||||
using std::ranges::lazy_split_view;
|
||||
|
||||
// [range.split], split view
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// Test the libc++ extension that std::ranges::join_with_view::iterator<Const>::operator* is marked as [[nodiscard]].
|
||||
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
void test() {
|
||||
char range[3][2] = {{'x', 'x'}, {'y', 'y'}, {'z', 'z'}};
|
||||
char pattern[2] = {',', ' '};
|
||||
|
||||
std::ranges::join_with_view view(range, pattern);
|
||||
|
||||
// clang-format off
|
||||
*view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
*std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// Test the libc++ extension that std::ranges::join_with_view::iterator<Const>::operator== is marked as [[nodiscard]].
|
||||
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
void test() {
|
||||
char16_t range[3][1] = {{u'x'}, {u'y'}, {u'z'}};
|
||||
char16_t pattern[1] = {u'-'};
|
||||
|
||||
std::ranges::join_with_view view(range, pattern);
|
||||
|
||||
// clang-format off
|
||||
(view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
(std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
(view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
(std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// Test the libc++ extension that std::ranges::join_with_view::iterator<Const>::iter_move is marked as [[nodiscard]].
|
||||
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
void test() {
|
||||
long range[2][1] = {{0L}, {2L}};
|
||||
long pattern[1] = {1L};
|
||||
|
||||
std::ranges::join_with_view view(range, pattern);
|
||||
|
||||
// clang-format off
|
||||
iter_move(view.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
iter_move(std::as_const(view).begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// This test ensures that we use `[[no_unique_address]]` in `join_with_view::iterator`.
|
||||
|
||||
#include <cstddef>
|
||||
#include <ranges>
|
||||
#include <variant>
|
||||
|
||||
struct IntRange : std::ranges::view_base {
|
||||
int* begin();
|
||||
int* end();
|
||||
};
|
||||
|
||||
class Iter {
|
||||
public:
|
||||
using value_type = IntRange;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
Iter& operator++();
|
||||
void operator++(int);
|
||||
value_type& operator*() const;
|
||||
bool operator==(std::default_sentinel_t) const;
|
||||
|
||||
private:
|
||||
int* ptr_;
|
||||
};
|
||||
|
||||
static_assert(std::input_iterator<Iter>);
|
||||
static_assert(!std::forward_iterator<Iter>);
|
||||
|
||||
struct View : std::ranges::view_base {
|
||||
Iter begin();
|
||||
std::default_sentinel_t end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<View>);
|
||||
static_assert(!std::ranges::forward_range<View>);
|
||||
|
||||
using JWV = std::ranges::join_with_view<View, IntRange>;
|
||||
|
||||
// Expected JWV::iterator layout:
|
||||
// _Parent* __parent_; // offset: 0
|
||||
// [[no_unique_address]] __empty __outer_it; // 0
|
||||
// variant<_PatternIter, _InnerIter> __pattern_; // sizeof(pointer)
|
||||
static_assert(sizeof(std::ranges::iterator_t<JWV>) ==
|
||||
sizeof(void*) + sizeof(std::variant<int*, int*>)); // sizeof(__parent_) + sizeof(__inner_it_)
|
||||
@@ -0,0 +1,33 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// Test the libc++ extension that std::views::join_with is marked as [[nodiscard]].
|
||||
|
||||
#include <ranges>
|
||||
|
||||
void test() {
|
||||
int range[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
|
||||
int pattern_base[2] = {-1, -1};
|
||||
auto pattern = std::views::all(pattern_base);
|
||||
|
||||
// clang-format off
|
||||
std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::views::join_with(range, pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
range | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::views::reverse | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
|
||||
std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::views::join_with(range, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
range | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::views::reverse | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// Test the libc++ extension that std::ranges::join_with_view::sentinel<Const>::operator== is marked as [[nodiscard]].
|
||||
|
||||
#include <array>
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
#include "test_iterators.h"
|
||||
#include "test_range.h"
|
||||
|
||||
void test() {
|
||||
std::array<test_range<cpp20_input_iterator>, 0> range;
|
||||
std::array<int, 0> pattern;
|
||||
|
||||
std::ranges::join_with_view view(range, pattern);
|
||||
static_assert(!std::ranges::common_range<decltype(view)>);
|
||||
|
||||
// clang-format off
|
||||
(view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
(std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
(view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
(std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// XFAIL: msvc
|
||||
|
||||
// <ranges>
|
||||
|
||||
// This test ensures that we use `[[no_unique_address]]` in `join_with_view::sentinel`.
|
||||
|
||||
#include <cstddef>
|
||||
#include <ranges>
|
||||
#include <string_view>
|
||||
|
||||
template <bool Const>
|
||||
struct Iter {
|
||||
using value_type = std::string_view;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
Iter& operator++();
|
||||
Iter operator++(int);
|
||||
value_type& operator*() const;
|
||||
bool operator==(const Iter&) const;
|
||||
bool operator==(std::default_sentinel_t) const;
|
||||
};
|
||||
|
||||
struct View : std::ranges::view_base {
|
||||
Iter<false> begin();
|
||||
Iter<true> begin() const;
|
||||
std::default_sentinel_t end() const;
|
||||
};
|
||||
|
||||
using JWV = std::ranges::join_with_view<View, std::string_view>;
|
||||
|
||||
template <class View>
|
||||
struct Test {
|
||||
[[no_unique_address]] std::ranges::sentinel_t<View> se;
|
||||
unsigned char pad;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Test<JWV>) == 1);
|
||||
static_assert(sizeof(Test<const JWV>) == 1);
|
||||
@@ -0,0 +1,30 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// Test the libc++ extension that std::ranges::join_with_view::base is marked as [[nodiscard]].
|
||||
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
void test() {
|
||||
int range[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
|
||||
int pattern[2] = {-1, -1};
|
||||
|
||||
std::ranges::join_with_view view(range, pattern);
|
||||
|
||||
// clang-format off
|
||||
view.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::as_const(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::move(std::as_const(view)).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::move(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// Test the libc++ extension that std::ranges::join_with_view::begin is marked as [[nodiscard]].
|
||||
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
void test() {
|
||||
int range[3][2] = {{1, 3}, {4, 6}, {7, 9}};
|
||||
int pattern[1] = {-2};
|
||||
|
||||
std::ranges::join_with_view view(range, pattern);
|
||||
|
||||
// clang-format off
|
||||
view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// Test the libc++ extension that std::ranges::join_with_view::end is marked as [[nodiscard]].
|
||||
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
void test() {
|
||||
int range[3][2] = {{1, 2}, {4, 5}, {7, 8}};
|
||||
int pattern[1] = {-3};
|
||||
|
||||
std::ranges::join_with_view view(range, pattern);
|
||||
|
||||
// clang-format off
|
||||
view.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::as_const(view).end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// XFAIL: msvc
|
||||
|
||||
// <ranges>
|
||||
|
||||
// This test ensures that we use `[[no_unique_address]]` in `join_with_view`.
|
||||
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
|
||||
struct ForwardView : std::ranges::view_base {
|
||||
std::string* begin() const;
|
||||
std::string* end() const;
|
||||
};
|
||||
|
||||
static_assert(std::ranges::forward_range<ForwardView>);
|
||||
static_assert(std::is_reference_v<std::ranges::range_reference_t<ForwardView>>);
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
char* begin() const;
|
||||
char* end() const;
|
||||
};
|
||||
|
||||
template <class View>
|
||||
struct Test {
|
||||
[[no_unique_address]] View view;
|
||||
unsigned char pad;
|
||||
};
|
||||
|
||||
using JWV = std::ranges::join_with_view<ForwardView, Pattern>;
|
||||
|
||||
// Expected JWV layout:
|
||||
// [[no_unique_address]] _View __base_ // offset: 0
|
||||
// [[no_unique_address]] __empty_cache __outer_it; // 0
|
||||
// [[no_unique_address]] __empty_cache __inner_; // 1
|
||||
// [[no_unique_address]] _Patter __pattern_ // 0
|
||||
static_assert(sizeof(JWV) == 2);
|
||||
static_assert(sizeof(Test<JWV>) == 2);
|
||||
@@ -278,18 +278,12 @@
|
||||
# error "__cpp_lib_ranges_concat should not be defined before c++26"
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_ranges_join_with
|
||||
# error "__cpp_lib_ranges_join_with should be defined in c++23"
|
||||
# endif
|
||||
# if __cpp_lib_ranges_join_with != 202202L
|
||||
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
|
||||
# endif
|
||||
# else
|
||||
# ifdef __cpp_lib_ranges_join_with
|
||||
# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_ranges_repeat
|
||||
# error "__cpp_lib_ranges_repeat should be defined in c++23"
|
||||
@@ -406,18 +400,12 @@
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_ranges_join_with
|
||||
# error "__cpp_lib_ranges_join_with should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_ranges_join_with != 202202L
|
||||
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
|
||||
# endif
|
||||
# else
|
||||
# ifdef __cpp_lib_ranges_join_with
|
||||
# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_ranges_repeat
|
||||
# error "__cpp_lib_ranges_repeat should be defined in c++26"
|
||||
|
||||
@@ -5633,18 +5633,12 @@
|
||||
# error "__cpp_lib_ranges_iota should have the value 202202L in c++23"
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_ranges_join_with
|
||||
# error "__cpp_lib_ranges_join_with should be defined in c++23"
|
||||
# endif
|
||||
# if __cpp_lib_ranges_join_with != 202202L
|
||||
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
|
||||
# endif
|
||||
# else
|
||||
# ifdef __cpp_lib_ranges_join_with
|
||||
# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_ranges_repeat
|
||||
# error "__cpp_lib_ranges_repeat should be defined in c++23"
|
||||
@@ -7549,18 +7543,12 @@
|
||||
# error "__cpp_lib_ranges_iota should have the value 202202L in c++26"
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_ranges_join_with
|
||||
# error "__cpp_lib_ranges_join_with should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_ranges_join_with != 202202L
|
||||
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
|
||||
# endif
|
||||
# else
|
||||
# ifdef __cpp_lib_ranges_join_with
|
||||
# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_ranges_repeat
|
||||
# error "__cpp_lib_ranges_repeat should be defined in c++26"
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// iterator() = default;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <cassert>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../types.h"
|
||||
#include "test_comparisons.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
constexpr bool test() {
|
||||
{ // `V` and `Pattern` model forward range
|
||||
using Inner = BasicVectorView<int, ViewProperties{}, forward_iterator>;
|
||||
using V = BasicVectorView<Inner, ViewProperties{}, forward_iterator>;
|
||||
using Pattern = Inner;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using ConstIter = std::ranges::iterator_t<const JWV>;
|
||||
|
||||
// Default constructor of iterator<false> should not be explicit
|
||||
Iter iter = {};
|
||||
assert(testEquality(iter, Iter{}, true));
|
||||
|
||||
// Default constructor of iterator<true> should not be explicit
|
||||
ConstIter citer = {};
|
||||
assert(testEquality(citer, ConstIter{}, true));
|
||||
assert(testEquality(iter, citer, true));
|
||||
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V{Inner{1, 2}, Inner{2, 1}}, Pattern{3, 3});
|
||||
Iter jwv_iter = jwv.begin();
|
||||
ConstIter jwv_citer = std::as_const(jwv).begin();
|
||||
assert(testEquality(jwv_iter, jwv_citer, true));
|
||||
|
||||
assert(testEquality(jwv_iter, iter, false));
|
||||
assert(testEquality(jwv_iter, citer, false));
|
||||
assert(testEquality(jwv_citer, iter, false));
|
||||
assert(testEquality(jwv_citer, citer, false));
|
||||
}
|
||||
|
||||
{ // `InnerIter` is not default constructible (does not model forward iterator, JWV cannot be const-accessed)
|
||||
using Inner = BasicVectorView<char, ViewProperties{.common = false}, EqComparableInputIter>;
|
||||
using V = BasicVectorView<Inner, ViewProperties{.common = false}, forward_iterator>;
|
||||
using Pattern = BasicVectorView<char, ViewProperties{}, forward_iterator>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
|
||||
Iter iter;
|
||||
assert(testEquality(iter, Iter{}, true));
|
||||
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V{Inner{'a', 'b'}, Inner{'c', 'd'}}, Pattern{',', ' '});
|
||||
Iter jwv_iter = jwv.begin();
|
||||
assert(testEquality(jwv_iter, iter, false));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// constexpr iterator(iterator<!Const> i)
|
||||
// requires Const && convertible_to<iterator_t<V>, OuterIter> &&
|
||||
// convertible_to<iterator_t<InnerRng>, InnerIter> &&
|
||||
// convertible_to<iterator_t<Pattern>, PatternIter>;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
constexpr bool test() {
|
||||
{ // Regular conversion from `!Const` to `Const` iterator
|
||||
std::vector<std::vector<int>> vec = {{1, 2}, {3, 4}, {5, 6}};
|
||||
int pattern = 0;
|
||||
std::ranges::join_with_view jwv(vec, pattern);
|
||||
|
||||
using JWV = decltype(jwv);
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!std::same_as<Iter, CIter>);
|
||||
static_assert(std::convertible_to<Iter, CIter>);
|
||||
static_assert(std::constructible_from<CIter, Iter>);
|
||||
|
||||
Iter it = jwv.begin();
|
||||
assert(*it == 1);
|
||||
|
||||
const CIter cit1 = it; // `cit1` points to element of `V`; this constructor should not be explicit
|
||||
assert(*cit1 == 1);
|
||||
assert(cit1 == it);
|
||||
|
||||
std::ranges::advance(it, 2);
|
||||
assert(*it == 0);
|
||||
CIter cit2 = it; // `cit2` points to element of `Pattern`
|
||||
assert(*cit2 == 0);
|
||||
assert(cit2 == it);
|
||||
|
||||
++it;
|
||||
assert(*it == 3);
|
||||
CIter cit3 = it;
|
||||
assert(*cit3 == 3);
|
||||
assert(cit3 == it);
|
||||
|
||||
--cit3;
|
||||
assert(cit2 == cit3);
|
||||
}
|
||||
|
||||
{ // Test conversion from `Const` to `!Const` (should be invalid)
|
||||
using V = std::vector<std::vector<int>>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!std::convertible_to<CIter, Iter>);
|
||||
static_assert(!std::constructible_from<Iter, CIter>);
|
||||
}
|
||||
|
||||
{ // When `convertible_to<iterator_t<V>, OuterIter>` is not modeled
|
||||
using Inner = std::vector<short>;
|
||||
using V = ConstOppositeView<Inner>;
|
||||
using Pattern = std::ranges::single_view<short>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!std::convertible_to<CIter, Iter>);
|
||||
static_assert(!std::constructible_from<Iter, CIter>);
|
||||
}
|
||||
|
||||
{ // When `convertible_to<iterator_t<InnerRng>, InnerIter>` is not modeled
|
||||
using Inner = ConstOppositeView<long>;
|
||||
using V = std::vector<Inner>;
|
||||
using Pattern = std::ranges::single_view<long>;
|
||||
using JWV = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!std::convertible_to<CIter, Iter>);
|
||||
static_assert(!std::constructible_from<Iter, CIter>);
|
||||
}
|
||||
|
||||
{ // When `convertible_to<iterator_t<Pattern>, PatternIter>` is not modeled
|
||||
using V = std::vector<std::vector<long long>>;
|
||||
using Pattern = ConstOppositeView<long long>;
|
||||
using JWV = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!std::convertible_to<CIter, Iter>);
|
||||
static_assert(!std::constructible_from<Iter, CIter>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// constexpr iterator& operator--()
|
||||
// requires ref-is-glvalue && bidirectional_range<Base> &&
|
||||
// bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
|
||||
// constexpr iterator operator--(int)
|
||||
// requires ref-is-glvalue && bidirectional_range<Base> &&
|
||||
// bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <forward_list>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
template <class I>
|
||||
concept CanPreDecrement = requires(I& i) {
|
||||
{ --i } -> std::same_as<I&>;
|
||||
};
|
||||
|
||||
template <class I>
|
||||
concept CanPostDecrement = requires(I& i) {
|
||||
{ i-- } -> std::same_as<I>;
|
||||
};
|
||||
|
||||
template <class I>
|
||||
concept CanDecrement = CanPreDecrement<I> && CanPostDecrement<I>;
|
||||
|
||||
constexpr bool test() {
|
||||
{ // `V` and `Pattern` are not empty. Test return type too.
|
||||
using V = std::ranges::owning_view<std::vector<std::string>>;
|
||||
using Pattern = std::ranges::single_view<char>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(CanDecrement<Iter>);
|
||||
static_assert(CanDecrement<CIter>);
|
||||
|
||||
JWV jwv(V{{"01", "23", "45"}}, Pattern{'_'});
|
||||
|
||||
{
|
||||
auto it = jwv.end();
|
||||
std::same_as<Iter&> decltype(auto) it_ref = --it;
|
||||
assert(it_ref == it);
|
||||
assert(*it == '5');
|
||||
std::same_as<Iter> decltype(auto) it_copy = it--;
|
||||
assert(--it_copy == it);
|
||||
--it;
|
||||
assert(*it == '_');
|
||||
it--;
|
||||
assert(*it == '3');
|
||||
--it;
|
||||
it--;
|
||||
assert(*it == '_');
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::as_const(jwv).end();
|
||||
std::same_as<CIter&> decltype(auto) cit_ref = --cit;
|
||||
assert(cit_ref == cit);
|
||||
assert(*cit == '5');
|
||||
std::same_as<CIter> decltype(auto) cit_copy = cit--;
|
||||
assert(--cit_copy == cit);
|
||||
--cit;
|
||||
assert(*cit == '_');
|
||||
cit--;
|
||||
assert(*cit == '3');
|
||||
--cit;
|
||||
cit--;
|
||||
assert(*cit == '_');
|
||||
}
|
||||
|
||||
assert(std::ranges::equal(std::views::reverse(std::move(jwv)), std::string_view{"54_32_10"}));
|
||||
}
|
||||
|
||||
{ // `Pattern` is empty, `V` is not.
|
||||
using Inner = std::array<int, 1>;
|
||||
using V = std::ranges::owning_view<std::array<Inner, 3>>;
|
||||
using Pattern = std::ranges::owning_view<std::array<int, 0>>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
JWV jwv(V{{Inner{-9}, Inner{-99}, Inner{-999}}}, Pattern{});
|
||||
|
||||
{
|
||||
auto it = jwv.end();
|
||||
--it;
|
||||
assert(*it == -999);
|
||||
it--;
|
||||
assert(*it == -99);
|
||||
--it;
|
||||
assert(*it == -9);
|
||||
assert(it == jwv.begin());
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::as_const(jwv).end();
|
||||
--cit;
|
||||
assert(*cit == -999);
|
||||
cit--;
|
||||
assert(*cit == -99);
|
||||
--cit;
|
||||
assert(*cit == -9);
|
||||
assert(cit == std::as_const(jwv).begin());
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(TEST_COMPILER_GCC) // GCC c++/101777
|
||||
{ // `V` has empty subrange in the middle, `Pattern` is not empty. Try to go back and forth.
|
||||
using V = std::array<std::vector<int>, 3>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
|
||||
|
||||
JWV jwv(V{{{5}, {}, {125}}}, Pattern{1});
|
||||
|
||||
{
|
||||
auto it = jwv.end();
|
||||
--it;
|
||||
assert(*it == 125);
|
||||
it--;
|
||||
assert(*it == 1);
|
||||
--it;
|
||||
assert(*it == 1);
|
||||
it--;
|
||||
assert(*it == 5);
|
||||
++it;
|
||||
assert(*it == 1);
|
||||
--it;
|
||||
assert(*it == 5);
|
||||
std::ranges::advance(it, 4);
|
||||
it--;
|
||||
assert(*it == 125);
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::as_const(jwv).end();
|
||||
--cit;
|
||||
assert(*cit == 125);
|
||||
cit--;
|
||||
assert(*cit == 1);
|
||||
--cit;
|
||||
assert(*cit == 1);
|
||||
cit--;
|
||||
assert(*cit == 5);
|
||||
++cit;
|
||||
assert(*cit == 1);
|
||||
--cit;
|
||||
assert(*cit == 5);
|
||||
std::ranges::advance(cit, 4);
|
||||
cit--;
|
||||
assert(*cit == 125);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Only first element of `V` is not empty. `Pattern` is empty. Try to go back and forth.
|
||||
using Inner = std::vector<int>;
|
||||
using V = std::ranges::owning_view<std::array<Inner, 3>>;
|
||||
using Pattern = std::ranges::empty_view<int>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
JWV jwv(V{{Inner{999}, {}, {}}}, Pattern{});
|
||||
|
||||
{
|
||||
auto it = jwv.end();
|
||||
--it;
|
||||
assert(*it == 999);
|
||||
++it;
|
||||
assert(it == jwv.end());
|
||||
it--;
|
||||
assert(*it == 999);
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::as_const(jwv).end();
|
||||
--cit;
|
||||
assert(*cit == 999);
|
||||
++cit;
|
||||
assert(cit == std::as_const(jwv).end());
|
||||
cit--;
|
||||
assert(*cit == 999);
|
||||
}
|
||||
}
|
||||
#endif // !defined(TEST_COMPILER_GCC)
|
||||
|
||||
{ // `ref-is-glvalue` is false
|
||||
using V = RvalueVector<std::vector<int>>;
|
||||
using Pattern = std::ranges::empty_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
static_assert(!CanPreDecrement<Iter>);
|
||||
static_assert(!CanPostDecrement<Iter>);
|
||||
}
|
||||
|
||||
{ // `Base` does not model bidirectional range
|
||||
using V = std::ranges::owning_view<std::forward_list<std::vector<int>>>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!CanPreDecrement<Iter>);
|
||||
static_assert(!CanPostDecrement<Iter>);
|
||||
static_assert(!CanPreDecrement<CIter>);
|
||||
static_assert(!CanPostDecrement<CIter>);
|
||||
}
|
||||
|
||||
{ // InnerBase does not model bidirectional-common
|
||||
{ // InnerBase does not model bidirectional range
|
||||
using V = std::ranges::owning_view<std::vector<std::forward_list<int>>>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!CanPreDecrement<Iter>);
|
||||
static_assert(!CanPostDecrement<Iter>);
|
||||
static_assert(!CanPreDecrement<CIter>);
|
||||
static_assert(!CanPostDecrement<CIter>);
|
||||
}
|
||||
|
||||
{ // InnerBase does not model common range
|
||||
using InnerBase = BasicVectorView<int, ViewProperties{.common = false}, bidirectional_iterator>;
|
||||
using V = std::ranges::owning_view<std::vector<InnerBase>>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!CanPreDecrement<Iter>);
|
||||
static_assert(!CanPostDecrement<Iter>);
|
||||
static_assert(!CanPreDecrement<CIter>);
|
||||
static_assert(!CanPostDecrement<CIter>);
|
||||
}
|
||||
}
|
||||
|
||||
{ // PatternBase does not model bidirectional-common
|
||||
{ // PatternBase does not model bidirectional range
|
||||
using V = std::ranges::owning_view<std::vector<std::vector<int>>>;
|
||||
using Pattern = std::ranges::owning_view<std::forward_list<int>>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!CanPreDecrement<Iter>);
|
||||
static_assert(!CanPostDecrement<Iter>);
|
||||
static_assert(!CanPreDecrement<CIter>);
|
||||
static_assert(!CanPostDecrement<CIter>);
|
||||
}
|
||||
|
||||
{ // PatternBase does not model common range
|
||||
using V = std::ranges::owning_view<std::vector<std::vector<int>>>;
|
||||
using Pattern = BasicVectorView<int, ViewProperties{.common = false}, bidirectional_iterator>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!CanPreDecrement<Iter>);
|
||||
static_assert(!CanPostDecrement<Iter>);
|
||||
static_assert(!CanPreDecrement<CIter>);
|
||||
static_assert(!CanPostDecrement<CIter>);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// constexpr decltype(auto) operator*() const;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
struct ProxyRef {
|
||||
int& val;
|
||||
};
|
||||
|
||||
class CommonProxyRef {
|
||||
public:
|
||||
constexpr CommonProxyRef(ProxyRef i) : val(i.val) {}
|
||||
constexpr CommonProxyRef(int i) : val(i) {}
|
||||
|
||||
constexpr int get() const { return val; }
|
||||
|
||||
private:
|
||||
int val;
|
||||
};
|
||||
|
||||
template <template <class> class TQual, template <class> class UQual>
|
||||
struct std::basic_common_reference<ProxyRef, int, TQual, UQual> {
|
||||
using type = CommonProxyRef;
|
||||
};
|
||||
|
||||
template <template <class> class TQual, template <class> class UQual>
|
||||
struct std::basic_common_reference<int, ProxyRef, TQual, UQual> {
|
||||
using type = CommonProxyRef;
|
||||
};
|
||||
|
||||
static_assert(std::common_reference_with<int&, ProxyRef>);
|
||||
static_assert(std::common_reference_with<int&, CommonProxyRef>);
|
||||
|
||||
class ProxyIter {
|
||||
public:
|
||||
using value_type = int;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
constexpr ProxyIter() : ptr_(nullptr) {}
|
||||
constexpr explicit ProxyIter(int* p) : ptr_(p) {}
|
||||
|
||||
constexpr ProxyRef operator*() const { return ProxyRef{*ptr_}; }
|
||||
|
||||
constexpr ProxyIter& operator++() {
|
||||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ProxyIter operator++(int) {
|
||||
ProxyIter tmp = *this;
|
||||
++ptr_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const ProxyIter& other) const { return ptr_ == other.ptr_; }
|
||||
|
||||
private:
|
||||
int* ptr_;
|
||||
};
|
||||
|
||||
static_assert(std::forward_iterator<ProxyIter>);
|
||||
|
||||
constexpr bool test() {
|
||||
{ // Result of `operator*` is (maybe const) lvalue reference
|
||||
using V = std::ranges::owning_view<std::vector<std::string>>;
|
||||
using Pattern = std::ranges::owning_view<std::string>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
JWV jwv(V{{"ab", "cd", "ef"}}, Pattern{"><"});
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
std::same_as<char&> decltype(auto) v_ref = *std::as_const(it);
|
||||
assert(v_ref == 'a');
|
||||
std::ranges::advance(it, 2);
|
||||
std::same_as<char&> decltype(auto) pattern_ref = *it;
|
||||
assert(pattern_ref == '>');
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
std::same_as<const char&> decltype(auto) cv_ref = *cit;
|
||||
assert(cv_ref == 'a');
|
||||
std::ranges::advance(cit, 3);
|
||||
std::same_as<const char&> decltype(auto) cpattern_ref = *std::as_const(cit);
|
||||
assert(cpattern_ref == '<');
|
||||
}
|
||||
}
|
||||
|
||||
{ // Result of `operator*` is const lvalue reference
|
||||
using V = std::ranges::owning_view<std::vector<std::string_view>>;
|
||||
using Pattern = std::string_view;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
JWV jwv(V{{"123", "456", "789"}}, Pattern{"._."});
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
std::same_as<const char&> decltype(auto) v_ref = *it;
|
||||
assert(v_ref == '1');
|
||||
std::ranges::advance(it, 3);
|
||||
std::same_as<const char&> decltype(auto) pattern_ref = *std::as_const(it);
|
||||
assert(pattern_ref == '.');
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
std::same_as<const char&> decltype(auto) cv_ref = *std::as_const(cit);
|
||||
assert(cv_ref == '1');
|
||||
std::ranges::advance(cit, 4);
|
||||
std::same_as<const char&> decltype(auto) cpattern_ref = *cit;
|
||||
assert(cpattern_ref == '_');
|
||||
}
|
||||
}
|
||||
|
||||
{ // Result of `operator*` is prvalue
|
||||
using V = std::vector<std::string_view>;
|
||||
using Pattern = RvalueVector<char>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
JWV jwv(V{"x^2", "y^2", "z^2"}, Pattern{{' ', '+', ' '}});
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
std::same_as<char> decltype(auto) v_ref = *std::as_const(it);
|
||||
assert(v_ref == 'x');
|
||||
std::ranges::advance(it, 3);
|
||||
std::same_as<char> decltype(auto) pattern_ref = *it;
|
||||
assert(pattern_ref == ' ');
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
std::same_as<char> decltype(auto) cv_ref = *cit;
|
||||
assert(cv_ref == 'x');
|
||||
std::ranges::advance(cit, 4);
|
||||
std::same_as<char> decltype(auto) cpattern_ref = *std::as_const(cit);
|
||||
assert(cpattern_ref == '+');
|
||||
}
|
||||
}
|
||||
|
||||
{ // Result of `operator*` is (maybe const) rvalue reference
|
||||
using Inner = std::ranges::as_rvalue_view<std::ranges::owning_view<std::string>>;
|
||||
using V = std::ranges::owning_view<std::vector<Inner>>;
|
||||
using Pattern = std::ranges::as_rvalue_view<std::ranges::owning_view<std::array<char, 2>>>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
std::vector<Inner> vec;
|
||||
vec.emplace_back(Inner{{"x*y"}});
|
||||
vec.emplace_back(Inner{{"y*z"}});
|
||||
vec.emplace_back(Inner{{"z*x"}});
|
||||
JWV jwv(V(std::move(vec)), Pattern(std::array{',', ' '}));
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
std::same_as<char&&> decltype(auto) v_ref = *it;
|
||||
assert(v_ref == 'x');
|
||||
std::ranges::advance(it, 3);
|
||||
std::same_as<char&&> decltype(auto) pattern_ref = *std::as_const(it);
|
||||
assert(pattern_ref == ',');
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
std::same_as<const char&&> decltype(auto) cv_ref = *std::as_const(cit);
|
||||
assert(cv_ref == 'x');
|
||||
std::ranges::advance(cit, 4);
|
||||
std::same_as<const char&&> decltype(auto) cpattern_ref = *cit;
|
||||
assert(cpattern_ref == ' ');
|
||||
}
|
||||
}
|
||||
|
||||
{ // Result of `operator*` is type different from range_reference_t<InnerRng> and range_reference_t<Pattern>
|
||||
using Inner = std::vector<int>;
|
||||
using V = std::vector<Inner>;
|
||||
using Pattern = std::ranges::subrange<ProxyIter, ProxyIter>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
|
||||
|
||||
static_assert(!std::same_as<std::ranges::range_reference_t<V>, std::ranges::range_reference_t<JWV>>);
|
||||
static_assert(!std::same_as<std::ranges::range_reference_t<Pattern>, std::ranges::range_reference_t<JWV>>);
|
||||
|
||||
std::array<int, 2> pattern = {-1, -1};
|
||||
Pattern pattern_as_subrange(ProxyIter{pattern.data()}, ProxyIter{pattern.data() + pattern.size()});
|
||||
|
||||
JWV jwv(V{Inner{1, 1}, Inner{2, 2}, Inner{3, 3}}, pattern_as_subrange);
|
||||
|
||||
auto it = jwv.begin();
|
||||
std::same_as<CommonProxyRef> decltype(auto) v_ref = *it;
|
||||
assert(v_ref.get() == 1);
|
||||
std::ranges::advance(it, 7);
|
||||
std::same_as<CommonProxyRef> decltype(auto) pattern_ref = *std::as_const(it);
|
||||
assert(pattern_ref.get() == -1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// friend constexpr bool operator==(const iterator& x, const iterator& y)
|
||||
// requires ref-is-glvalue && forward_range<Base> &&
|
||||
// equality_comparable<InnerIter>;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
#include "../types.h"
|
||||
#include "test_comparisons.h"
|
||||
|
||||
template <class I1, class I2 = I1>
|
||||
concept CanEq = requires(const I1& i1, const I2& i2) {
|
||||
{ i1 == i2 } -> std::same_as<bool>;
|
||||
{ i2 == i1 } -> std::same_as<bool>;
|
||||
{ i1 != i2 } -> std::same_as<bool>;
|
||||
{ i2 != i1 } -> std::same_as<bool>;
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
{ // `V` and `Pattern` are not empty. Test return types too.
|
||||
using V = std::array<std::array<int, 2>, 3>;
|
||||
using Pattern = std::array<long, 1>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!std::same_as<Iter, CIter>);
|
||||
static_assert(CanEq<Iter>);
|
||||
static_assert(CanEq<CIter>);
|
||||
static_assert(CanEq<Iter, CIter>);
|
||||
|
||||
JWV jwv(V{{{9, 8}, {7, 6}, {5, 4}}}, Pattern{0L});
|
||||
|
||||
Iter it1 = jwv.begin();
|
||||
assert(*it1 == 9);
|
||||
assert(testEquality(it1, it1, true));
|
||||
|
||||
Iter it2 = std::ranges::prev(jwv.end());
|
||||
assert(*it2 == 4);
|
||||
assert(testEquality(it2, it2, true));
|
||||
assert(testEquality(it1, it2, false));
|
||||
|
||||
CIter cit1 = std::as_const(jwv).begin();
|
||||
assert(*cit1 == 9);
|
||||
assert(testEquality(cit1, cit1, true));
|
||||
assert(testEquality(it1, cit1, true));
|
||||
assert(testEquality(it2, cit1, false));
|
||||
|
||||
CIter cit2 = std::ranges::prev(std::as_const(jwv).end());
|
||||
assert(*cit2 == 4);
|
||||
assert(testEquality(cit2, cit2, true));
|
||||
assert(testEquality(cit1, cit2, false));
|
||||
assert(testEquality(it1, cit2, false));
|
||||
assert(testEquality(it2, cit2, true));
|
||||
|
||||
// `it1.inner_it_` and `it2.inner_it_` are equal, but `it1.outer_it_` and `it2.outer_it_` are not.
|
||||
std::ranges::advance(it1, 2);
|
||||
assert(*it1 == 0);
|
||||
std::ranges::advance(it2, -2);
|
||||
assert(*it2 == 0);
|
||||
assert(testEquality(it1, it2, false));
|
||||
|
||||
// `cit1.inner_it_` and `cit2.inner_it_` are equal, but `cit1.outer_it_` and `cit2.outer_it_` are not.
|
||||
std::ranges::advance(cit1, 2);
|
||||
assert(*cit1 == 0);
|
||||
assert(testEquality(it1, cit1, true));
|
||||
std::ranges::advance(cit2, -2);
|
||||
assert(*cit2 == 0);
|
||||
assert(testEquality(it2, cit2, true));
|
||||
assert(testEquality(cit1, cit2, false));
|
||||
|
||||
// `it1.inner_it_` and `it2.inner_it_` are equal, `it1.outer_it_` and `it2.outer_it_` are equal too.
|
||||
// `it1.inner_it_index()` and `it2.inner_it_index()` are equal to 1.
|
||||
++it1;
|
||||
assert(*it1 == 7);
|
||||
std::ranges::advance(it2, -2);
|
||||
assert(*it2 == 7);
|
||||
assert(testEquality(it1, it2, true));
|
||||
|
||||
// `cit1.inner_it_` and `cit2.inner_it_` are equal, `cit1.outer_it_` and `cit2.outer_it_` are equal too.
|
||||
// `cit1.inner_it_index()` and `cit2.inner_it_index()` are equal to 1.
|
||||
++cit1;
|
||||
assert(*cit1 == 7);
|
||||
assert(testEquality(it1, cit1, true));
|
||||
std::ranges::advance(cit2, -2);
|
||||
assert(*cit2 == 7);
|
||||
assert(testEquality(it2, cit2, true));
|
||||
assert(testEquality(cit1, cit2, true));
|
||||
|
||||
// `it1.inner_it_` and `it2.inner_it_` are equal, `it1.outer_it_` and `it2.outer_it_` are equal too.
|
||||
// `it1.inner_it_index()` and `it2.inner_it_index()` are equal to 0.
|
||||
--it1;
|
||||
assert(*it1 == 0);
|
||||
--it2;
|
||||
assert(*it2 == 0);
|
||||
assert(testEquality(it1, it2, true));
|
||||
|
||||
// `cit1.inner_it_` and `cit2.inner_it_` are equal, `cit1.outer_it_` and `cit2.outer_it_` are equal too.
|
||||
// `cit1.inner_it_index()` and `cit2.inner_it_index()` are equal to 0.
|
||||
--cit1;
|
||||
assert(*cit1 == 0);
|
||||
assert(testEquality(it1, cit1, true));
|
||||
--cit2;
|
||||
assert(*cit2 == 0);
|
||||
assert(testEquality(it2, cit2, true));
|
||||
assert(testEquality(cit2, cit2, true));
|
||||
}
|
||||
|
||||
{ // `InnerIter` models input iterator and equality comparable. `Pattern` is empty.
|
||||
using Inner = BasicVectorView<int, ViewProperties{.common = false}, EqComparableInputIter>;
|
||||
using V = std::vector<Inner>;
|
||||
using Pattern = std::ranges::empty_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!std::same_as<Iter, CIter>);
|
||||
static_assert(CanEq<Iter>);
|
||||
static_assert(CanEq<CIter>);
|
||||
static_assert(!CanEq<CIter, Iter>);
|
||||
|
||||
JWV jwv(V{Inner{1, 2}, Inner{5, 6}, Inner{9, 0}}, Pattern{});
|
||||
|
||||
{
|
||||
Iter it1 = jwv.begin();
|
||||
assert(*it1 == 1);
|
||||
Iter it2 = std::ranges::next(jwv.begin(), 2);
|
||||
assert(*it2 == 5);
|
||||
assert(testEquality(it1, it2, false));
|
||||
++it1;
|
||||
++it1;
|
||||
assert(testEquality(it1, it2, true));
|
||||
++it1;
|
||||
assert(testEquality(it1, it2, false));
|
||||
}
|
||||
|
||||
{
|
||||
CIter cit1 = std::as_const(jwv).begin();
|
||||
assert(*cit1 == 1);
|
||||
CIter cit2 = std::ranges::next(std::as_const(jwv).begin(), 2);
|
||||
assert(*cit2 == 5);
|
||||
assert(testEquality(cit1, cit2, false));
|
||||
++cit1;
|
||||
++cit1;
|
||||
assert(testEquality(cit1, cit2, true));
|
||||
++cit1;
|
||||
assert(testEquality(cit1, cit2, false));
|
||||
}
|
||||
}
|
||||
|
||||
{ // `Pattern` is not empty. Some elements of `V` are.
|
||||
using Inner = BasicVectorView<int, ViewProperties{.common = false}, EqComparableInputIter>;
|
||||
using V = BasicVectorView<Inner, ViewProperties{}, forward_iterator>;
|
||||
using Pattern = BasicVectorView<int, ViewProperties{.common = false}, forward_iterator>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!std::same_as<Iter, CIter>);
|
||||
static_assert(CanEq<Iter>);
|
||||
static_assert(CanEq<CIter>);
|
||||
static_assert(!CanEq<CIter, Iter>);
|
||||
|
||||
JWV jwv(V{Inner{1}, Inner{}, Inner{27}}, Pattern{0});
|
||||
|
||||
{
|
||||
Iter it1 = jwv.begin();
|
||||
assert(*it1 == 1);
|
||||
++it1;
|
||||
assert(*it1 == 0);
|
||||
Iter it2 = jwv.begin();
|
||||
assert(testEquality(it1, it2, false));
|
||||
++it2;
|
||||
assert(testEquality(it1, it2, true));
|
||||
|
||||
++it2;
|
||||
assert(*it1 == *it2);
|
||||
assert(testEquality(it1, it2, false));
|
||||
|
||||
std::ranges::advance(it1, 2);
|
||||
++it2;
|
||||
assert(*it1 == *it2);
|
||||
assert(testEquality(it1, it2, true));
|
||||
}
|
||||
|
||||
{
|
||||
CIter cit1 = std::as_const(jwv).begin();
|
||||
assert(*cit1 == 1);
|
||||
++cit1;
|
||||
assert(*cit1 == 0);
|
||||
CIter cit2 = std::as_const(jwv).begin();
|
||||
assert(testEquality(cit1, cit2, false));
|
||||
++cit2;
|
||||
assert(testEquality(cit1, cit2, true));
|
||||
|
||||
++cit2;
|
||||
assert(*cit1 == *cit2);
|
||||
assert(testEquality(cit1, cit2, false));
|
||||
|
||||
std::ranges::advance(cit1, 2);
|
||||
++cit2;
|
||||
assert(*cit1 == *cit2);
|
||||
assert(testEquality(cit1, cit2, true));
|
||||
}
|
||||
}
|
||||
|
||||
{ // `ref-is-glvalue` is false
|
||||
using Inner = std::vector<int>;
|
||||
using V = RvalueVector<Inner>;
|
||||
using Pattern = std::ranges::empty_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
static_assert(!CanEq<Iter>);
|
||||
}
|
||||
|
||||
{ // `Base` does not model forward range
|
||||
using Inner = std::vector<int>;
|
||||
using V = BasicVectorView<Inner, ViewProperties{}, DefaultCtorInputIter>;
|
||||
using Pattern = std::ranges::empty_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
static_assert(!CanEq<Iter>);
|
||||
}
|
||||
|
||||
{ // `InnerIter` does not model equality comparable
|
||||
using Inner = BasicVectorView<int, ViewProperties{.common = false}, cpp20_input_iterator>;
|
||||
using V = std::vector<Inner>;
|
||||
using Pattern = std::ranges::empty_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!CanEq<Iter>);
|
||||
static_assert(!CanEq<CIter>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// constexpr iterator& operator++();
|
||||
// constexpr void operator++(int);
|
||||
// constexpr iterator operator++(int)
|
||||
// requires ref-is-glvalue && forward_iterator<OuterIter> &&
|
||||
// forward_iterator<InnerIter>;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
template <class I>
|
||||
concept CanPreIncrement = requires(I& i) { ++i; };
|
||||
|
||||
template <class I>
|
||||
concept CanPostIncrement = requires(I& i) { i++; };
|
||||
|
||||
template <bool RefIsGlvalue, class Inner>
|
||||
using VRange = std::conditional_t<RefIsGlvalue, std::vector<Inner>, RvalueVector<Inner>>;
|
||||
|
||||
template <bool RefIsGlvalue>
|
||||
constexpr void test_pre_increment() {
|
||||
{ // `V` and `Pattern` are not empty. Test return type too.
|
||||
using V = VRange<RefIsGlvalue, std::array<int, 2>>;
|
||||
using Pattern = std::array<int, 2>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
JWV jwv(V{{1, 1}, {2, 2}, {3, 3}}, Pattern{0, 0});
|
||||
|
||||
{
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
static_assert(CanPreIncrement<Iter>);
|
||||
static_assert(!CanPreIncrement<const Iter>);
|
||||
|
||||
auto it = jwv.begin();
|
||||
assert(*it == 1);
|
||||
std::same_as<Iter&> decltype(auto) it_ref = ++it;
|
||||
if constexpr (RefIsGlvalue) {
|
||||
assert(it_ref == it);
|
||||
}
|
||||
|
||||
++it;
|
||||
assert(*it == 0);
|
||||
++it_ref;
|
||||
++it_ref;
|
||||
assert(*it_ref == 2);
|
||||
++it;
|
||||
++it_ref;
|
||||
assert(*it == 0);
|
||||
}
|
||||
|
||||
if constexpr (RefIsGlvalue) {
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(CanPreIncrement<CIter>);
|
||||
static_assert(!CanPreIncrement<const CIter>);
|
||||
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
assert(*cit == 1);
|
||||
std::same_as<CIter&> decltype(auto) cit_ref = ++cit;
|
||||
assert(cit_ref == cit);
|
||||
++cit;
|
||||
assert(*cit == 0);
|
||||
++cit_ref;
|
||||
++cit_ref;
|
||||
assert(*cit_ref == 2);
|
||||
++cit;
|
||||
++cit_ref;
|
||||
assert(*cit == 0);
|
||||
}
|
||||
}
|
||||
|
||||
{ // `V` and `Pattern` are empty.
|
||||
using V = VRange<RefIsGlvalue, std::ranges::empty_view<int>>;
|
||||
using Pattern = std::ranges::empty_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
JWV jwv = {};
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
assert(it == jwv.end());
|
||||
}
|
||||
|
||||
if constexpr (RefIsGlvalue) {
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
assert(cit == std::as_const(jwv).end());
|
||||
}
|
||||
}
|
||||
|
||||
{ // `Pattern` is empty, `V` is not.
|
||||
using V = VRange<RefIsGlvalue, std::vector<int>>;
|
||||
using Pattern = std::vector<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
JWV jwv(V{{{-1}, {-2}, {-3}}}, Pattern{});
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
assert(*it == -1);
|
||||
++it;
|
||||
assert(*it == -2);
|
||||
++it;
|
||||
assert(*it == -3);
|
||||
++it;
|
||||
assert(it == jwv.end());
|
||||
}
|
||||
|
||||
if constexpr (RefIsGlvalue) {
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
assert(*cit == -1);
|
||||
++cit;
|
||||
assert(*cit == -2);
|
||||
++cit;
|
||||
assert(*cit == -3);
|
||||
++cit;
|
||||
assert(cit == std::as_const(jwv).end());
|
||||
}
|
||||
}
|
||||
|
||||
{ // `V` has empty subrange in the middle, `Pattern` is not empty.
|
||||
using V = VRange<RefIsGlvalue, std::vector<int>>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
|
||||
|
||||
JWV jwv(V{{1}, {}, {3}}, Pattern{0});
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
assert(*it == 1);
|
||||
++it;
|
||||
assert(*it == 0);
|
||||
++it;
|
||||
assert(*it == 0);
|
||||
++it;
|
||||
assert(*it == 3);
|
||||
}
|
||||
|
||||
if constexpr (RefIsGlvalue) {
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
assert(*cit == 1);
|
||||
++cit;
|
||||
assert(*cit == 0);
|
||||
++cit;
|
||||
assert(*cit == 0);
|
||||
++cit;
|
||||
assert(*cit == 3);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Only last element of `V` is not empty. `Pattern` is not empty.
|
||||
using V = VRange<RefIsGlvalue, std::vector<int>>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
|
||||
|
||||
JWV jwv(V{{}, {}, {555}}, Pattern{1});
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
assert(*it == 1);
|
||||
++it;
|
||||
assert(*it == 1);
|
||||
++it;
|
||||
assert(*it == 555);
|
||||
++it;
|
||||
assert(it == jwv.end());
|
||||
}
|
||||
|
||||
if constexpr (RefIsGlvalue) {
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
assert(*cit == 1);
|
||||
++cit;
|
||||
assert(*cit == 1);
|
||||
++cit;
|
||||
assert(*cit == 555);
|
||||
++cit;
|
||||
assert(cit == std::as_const(jwv).end());
|
||||
}
|
||||
}
|
||||
|
||||
{ // Only first element of `V` is not empty. `Pattern` is empty.
|
||||
using V = VRange<RefIsGlvalue, std::vector<int>>;
|
||||
using Pattern = std::ranges::empty_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
|
||||
|
||||
JWV jwv(V{{777}, {}, {}}, Pattern{});
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
assert(*it == 777);
|
||||
++it;
|
||||
assert(it == jwv.end());
|
||||
}
|
||||
|
||||
if constexpr (RefIsGlvalue) {
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
assert(*cit == 777);
|
||||
++cit;
|
||||
assert(cit == std::as_const(jwv).end());
|
||||
}
|
||||
}
|
||||
|
||||
{ // Only last element of `V` is not empty. `Pattern` is empty. `V` models input range.
|
||||
using V = BasicView<VRange<RefIsGlvalue, std::string>, ViewProperties{}, DefaultCtorInputIter>;
|
||||
using Pattern = std::ranges::empty_view<char>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
JWV jwv(V{{}, {}, {'a'}}, Pattern{});
|
||||
|
||||
auto it = jwv.begin();
|
||||
assert(*it == 'a');
|
||||
++it;
|
||||
assert(it == jwv.end());
|
||||
}
|
||||
|
||||
{ // Only first element of `V` is not empty. `Pattern` is not empty. `V` models input range.
|
||||
using V = BasicView<VRange<RefIsGlvalue, std::string>, ViewProperties{}, DefaultCtorInputIter>;
|
||||
using Pattern = std::ranges::single_view<char>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
JWV jwv(V{{'b'}, {}, {}}, Pattern{'.'});
|
||||
|
||||
auto it = jwv.begin();
|
||||
assert(*it == 'b');
|
||||
++it;
|
||||
assert(*it == '.');
|
||||
++it;
|
||||
assert(*it == '.');
|
||||
++it;
|
||||
assert(it == jwv.end());
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void test_post_increment() {
|
||||
{ // `V` and `Pattern` are not empty. Return type should be `iterator`.
|
||||
using V = std::array<std::array<int, 3>, 2>;
|
||||
using Pattern = std::array<int, 1>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(CanPostIncrement<Iter>);
|
||||
static_assert(!CanPostIncrement<const Iter>);
|
||||
static_assert(CanPostIncrement<CIter>);
|
||||
static_assert(!CanPostIncrement<const CIter>);
|
||||
|
||||
JWV jwv(V{{{6, 5, 4}, {3, 2, 1}}}, Pattern{-5});
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
assert(*it == 6);
|
||||
std::same_as<Iter> decltype(auto) it_copy = it++;
|
||||
assert(++it_copy == it);
|
||||
it++;
|
||||
it++;
|
||||
assert(*it == -5);
|
||||
it_copy++;
|
||||
it_copy++;
|
||||
assert(*it_copy == -5);
|
||||
it++;
|
||||
it_copy++;
|
||||
assert(*it == 3);
|
||||
assert(*it_copy == 3);
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::as_const(jwv).begin();
|
||||
assert(*cit == 6);
|
||||
std::same_as<CIter> decltype(auto) cit_copy = cit++;
|
||||
assert(++cit_copy == cit);
|
||||
cit++;
|
||||
cit++;
|
||||
assert(*cit == -5);
|
||||
cit_copy++;
|
||||
cit_copy++;
|
||||
assert(*cit_copy == -5);
|
||||
cit++;
|
||||
cit_copy++;
|
||||
assert(*cit == 3);
|
||||
assert(*cit_copy == 3);
|
||||
}
|
||||
}
|
||||
|
||||
{ // `Pattern` is empty, `V` is not. Value of `ref-is-glvalue` is false (return type should be `void`).
|
||||
using Inner = std::vector<int>;
|
||||
using V = RvalueVector<Inner>;
|
||||
using Pattern = std::ranges::empty_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
JWV jwv(V{Inner{-3}, Inner{-2}, Inner{-1}}, Pattern{});
|
||||
|
||||
auto it = jwv.begin();
|
||||
assert(*it == -3);
|
||||
it++;
|
||||
assert(*it == -2);
|
||||
it++;
|
||||
assert(*it == -1);
|
||||
it++;
|
||||
assert(it == jwv.end());
|
||||
static_assert(std::is_void_v<decltype(it++)>);
|
||||
}
|
||||
|
||||
{ // `V` has empty subrange in the middle, `Pattern` is not empty.
|
||||
// OuterIter does not model forward iterator (return type should be `void`).
|
||||
using Inner = std::vector<int>;
|
||||
using V = BasicVectorView<Inner, ViewProperties{.common = false}, cpp20_input_iterator>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
|
||||
JWV jwv(V{Inner{7}, {}, Inner{9}}, Pattern{8});
|
||||
|
||||
auto it = jwv.begin();
|
||||
assert(*it == 7);
|
||||
it++;
|
||||
assert(*it == 8);
|
||||
it++;
|
||||
assert(*it == 8);
|
||||
it++;
|
||||
assert(*it == 9);
|
||||
it++;
|
||||
assert(it == jwv.end());
|
||||
static_assert(std::is_void_v<decltype(it++)>);
|
||||
}
|
||||
|
||||
#if !defined(TEST_COMPILER_GCC) // GCC c++/101777
|
||||
{ // Only first element of `V` is not empty. `Pattern` is empty. InnerIter does not model forward
|
||||
// iterator (return type should be `void`).
|
||||
using Inner = BasicVectorView<char32_t, ViewProperties{.common = false}, cpp17_input_iterator>;
|
||||
using V = std::array<Inner, 3>;
|
||||
using Pattern = std::ranges::empty_view<char32_t>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
|
||||
|
||||
JWV jwv(V{Inner{U'?'}, Inner{}, Inner{}}, Pattern{});
|
||||
|
||||
auto it = jwv.begin();
|
||||
assert(*it == U'?');
|
||||
it++;
|
||||
assert(it == jwv.end());
|
||||
static_assert(std::is_void_v<decltype(it++)>);
|
||||
}
|
||||
#endif // !defined(TEST_COMPILER_GCC)
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
test_pre_increment<false>();
|
||||
test_pre_increment<true>();
|
||||
test_post_increment();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// friend constexpr decltype(auto) iter_move(const iterator& x);
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
class MoveOnlyInt {
|
||||
public:
|
||||
enum Status { constructed, move_constructed, moved_from_this };
|
||||
|
||||
MoveOnlyInt() = default;
|
||||
constexpr MoveOnlyInt(int val) : val_(val) {}
|
||||
|
||||
constexpr MoveOnlyInt(MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) {
|
||||
other.val_ = -1;
|
||||
other.status_ = moved_from_this;
|
||||
}
|
||||
|
||||
constexpr MoveOnlyInt(const MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) {
|
||||
other.val_ = -1;
|
||||
other.status_ = moved_from_this;
|
||||
}
|
||||
|
||||
MoveOnlyInt(const MoveOnlyInt&) { assert(false); } // Should never be called in this test.
|
||||
|
||||
MoveOnlyInt& operator=(MoveOnlyInt&&) { // Should never be called in this test.
|
||||
assert(false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr bool was_normally_constructed() const { return status_ == constructed; }
|
||||
constexpr bool was_move_constructed() const { return status_ == move_constructed; }
|
||||
constexpr bool was_moved_from() const { return status_ == moved_from_this; }
|
||||
|
||||
friend constexpr bool operator==(const MoveOnlyInt& left, int right) { return left.val_ == right; }
|
||||
friend constexpr bool operator==(const MoveOnlyInt& left, const MoveOnlyInt& right) {
|
||||
return left.val_ == right.val_;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable int val_ = -1;
|
||||
mutable Status status_ = constructed;
|
||||
};
|
||||
|
||||
static_assert(std::movable<MoveOnlyInt>);
|
||||
|
||||
struct ProxyRvalueRef {
|
||||
MoveOnlyInt&& val;
|
||||
};
|
||||
|
||||
class CommonProxyRvalueRef {
|
||||
public:
|
||||
constexpr CommonProxyRvalueRef(ProxyRvalueRef i) : val_(std::move(i.val)) {}
|
||||
constexpr CommonProxyRvalueRef(MoveOnlyInt i) : val_(std::move(i)) {}
|
||||
|
||||
constexpr MoveOnlyInt&& get() { return std::move(val_); }
|
||||
|
||||
private:
|
||||
MoveOnlyInt val_;
|
||||
};
|
||||
|
||||
template <template <class> class TQual, template <class> class UQual>
|
||||
struct std::basic_common_reference<ProxyRvalueRef, MoveOnlyInt, TQual, UQual> {
|
||||
using type = CommonProxyRvalueRef;
|
||||
};
|
||||
|
||||
template <template <class> class TQual, template <class> class UQual>
|
||||
struct std::basic_common_reference<MoveOnlyInt, ProxyRvalueRef, TQual, UQual> {
|
||||
using type = CommonProxyRvalueRef;
|
||||
};
|
||||
|
||||
static_assert(std::common_reference_with<MoveOnlyInt&&, ProxyRvalueRef>);
|
||||
static_assert(std::common_reference_with<MoveOnlyInt&&, CommonProxyRvalueRef>);
|
||||
|
||||
class ProxyIter {
|
||||
public:
|
||||
using value_type = MoveOnlyInt;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
constexpr ProxyIter() : ptr_(nullptr) {}
|
||||
constexpr explicit ProxyIter(MoveOnlyInt* it) : ptr_(std::move(it)) {}
|
||||
|
||||
constexpr decltype(auto) operator*() const { return *ptr_; }
|
||||
|
||||
constexpr ProxyIter& operator++() {
|
||||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ProxyIter operator++(int) {
|
||||
ProxyIter copy = *this;
|
||||
++ptr_;
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr ProxyIter& operator--() {
|
||||
--ptr_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ProxyIter operator--(int) {
|
||||
ProxyIter copy = *this;
|
||||
--ptr_;
|
||||
return copy;
|
||||
}
|
||||
|
||||
friend bool operator==(const ProxyIter&, const ProxyIter&) = default;
|
||||
|
||||
friend constexpr ProxyRvalueRef iter_move(const ProxyIter iter) {
|
||||
return ProxyRvalueRef{std::ranges::iter_move(iter.ptr_)};
|
||||
}
|
||||
|
||||
private:
|
||||
MoveOnlyInt* ptr_;
|
||||
};
|
||||
|
||||
static_assert(std::forward_iterator<ProxyIter>);
|
||||
|
||||
template <std::forward_iterator Iter>
|
||||
class IterMoveTrackingIterator {
|
||||
public:
|
||||
using value_type = std::iter_value_t<Iter>;
|
||||
using difference_type = std::iter_difference_t<Iter>;
|
||||
|
||||
IterMoveTrackingIterator() = default;
|
||||
constexpr explicit IterMoveTrackingIterator(Iter iter, bool* flag = nullptr) : iter_(std::move(iter)), flag_(flag) {}
|
||||
|
||||
constexpr IterMoveTrackingIterator& operator++() {
|
||||
++iter_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr IterMoveTrackingIterator operator++(int) {
|
||||
auto tmp = *this;
|
||||
++*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
constexpr decltype(auto) operator*() const { return *iter_; }
|
||||
|
||||
constexpr bool operator==(const IterMoveTrackingIterator& other) const { return iter_ == other.iter_; }
|
||||
|
||||
friend constexpr decltype(auto) iter_move(const IterMoveTrackingIterator& iter) {
|
||||
assert(iter.flag_ != nullptr);
|
||||
*iter.flag_ = true;
|
||||
return std::ranges::iter_move(iter.iter_);
|
||||
}
|
||||
|
||||
private:
|
||||
Iter iter_ = Iter();
|
||||
bool* flag_ = nullptr;
|
||||
};
|
||||
|
||||
static_assert(std::forward_iterator<IterMoveTrackingIterator<int*>> &&
|
||||
!std::bidirectional_iterator<IterMoveTrackingIterator<int*>>);
|
||||
|
||||
constexpr bool test() {
|
||||
{ // Test `iter_move` when result is true rvalue reference. Test return types.
|
||||
using V = std::array<std::array<char, 1>, 2>;
|
||||
using Pattern = std::array<char, 1>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
JWV jwv(V{{{'0'}, {'1'}}}, Pattern{','});
|
||||
|
||||
{
|
||||
auto it = jwv.begin();
|
||||
std::same_as<char&&> decltype(auto) v_rref1 = iter_move(it);
|
||||
std::same_as<char&&> decltype(auto) v_rref2 = iter_move(std::as_const(it));
|
||||
std::same_as<char&&> decltype(auto) v_rref3 = std::ranges::iter_move(it);
|
||||
std::same_as<char&&> decltype(auto) v_rref4 = std::ranges::iter_move(std::as_const(it));
|
||||
assert(std::ranges::equal(std::array{v_rref1, v_rref2, v_rref3, v_rref4}, std::views::repeat('0', 4)));
|
||||
|
||||
++it; // `it` points to element of `Pattern` from here
|
||||
std::same_as<char&&> decltype(auto) pattern_rref1 = iter_move(it);
|
||||
std::same_as<char&&> decltype(auto) pattern_rref2 = iter_move(std::as_const(it));
|
||||
std::same_as<char&&> decltype(auto) pattern_rref3 = std::ranges::iter_move(it);
|
||||
std::same_as<char&&> decltype(auto) pattern_rref4 = std::ranges::iter_move(std::as_const(it));
|
||||
assert(std::ranges::equal(
|
||||
std::array{pattern_rref1, pattern_rref2, pattern_rref3, pattern_rref4}, std::views::repeat(',', 4)));
|
||||
}
|
||||
|
||||
{
|
||||
auto cit = std::prev(std::as_const(jwv).end());
|
||||
std::same_as<const char&&> decltype(auto) cv_rref1 = iter_move(cit);
|
||||
std::same_as<const char&&> decltype(auto) cv_rref2 = iter_move(std::as_const(cit));
|
||||
std::same_as<const char&&> decltype(auto) cv_rref3 = std::ranges::iter_move(cit);
|
||||
std::same_as<const char&&> decltype(auto) cv_rref4 = std::ranges::iter_move(std::as_const(cit));
|
||||
assert(std::ranges::equal(std::array{cv_rref1, cv_rref2, cv_rref3, cv_rref4}, std::views::repeat('1', 4)));
|
||||
|
||||
cit--; // `cit` points to element of `Pattern` from here
|
||||
std::same_as<const char&&> decltype(auto) cpattern_rref1 = iter_move(cit);
|
||||
std::same_as<const char&&> decltype(auto) cpattern_rref2 = iter_move(std::as_const(cit));
|
||||
std::same_as<const char&&> decltype(auto) cpattern_rref3 = std::ranges::iter_move(cit);
|
||||
std::same_as<const char&&> decltype(auto) cpattern_rref4 = std::ranges::iter_move(std::as_const(cit));
|
||||
assert(std::ranges::equal(
|
||||
std::array{cpattern_rref1, cpattern_rref2, cpattern_rref3, cpattern_rref4}, std::views::repeat(',', 4)));
|
||||
}
|
||||
}
|
||||
|
||||
{ // Test `iter_move` when result is true rvalue reference. Test moving.
|
||||
using Inner = std::vector<MoveOnlyInt>;
|
||||
using V = std::vector<Inner>;
|
||||
using Pattern = std::vector<MoveOnlyInt>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
|
||||
V v;
|
||||
v.reserve(2);
|
||||
v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
|
||||
v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
|
||||
JWV jwv(std::move(v), std::ranges::to<Pattern>(std::views::iota(4, 12)));
|
||||
assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_normally_constructed));
|
||||
|
||||
{
|
||||
std::vector<MoveOnlyInt> values;
|
||||
values.reserve(8);
|
||||
|
||||
auto it = jwv.begin();
|
||||
values.emplace_back(iter_move(it));
|
||||
++it;
|
||||
values.emplace_back(iter_move(std::as_const(it)));
|
||||
it++;
|
||||
values.emplace_back(std::ranges::iter_move(it));
|
||||
++it;
|
||||
values.emplace_back(std::ranges::iter_move(std::as_const(it)));
|
||||
it++; // `it` points to element of `Pattern` from here
|
||||
values.emplace_back(iter_move(it));
|
||||
++it;
|
||||
values.emplace_back(iter_move(std::as_const(it)));
|
||||
it++;
|
||||
values.emplace_back(std::ranges::iter_move(it));
|
||||
++it;
|
||||
values.emplace_back(std::ranges::iter_move(std::as_const(it)));
|
||||
|
||||
assert(std::ranges::equal(values, std::views::iota(0, 8)));
|
||||
assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<MoveOnlyInt> values;
|
||||
values.reserve(8);
|
||||
|
||||
auto cit = std::prev(std::as_const(jwv).end());
|
||||
values.emplace_back(iter_move(cit));
|
||||
cit--;
|
||||
values.emplace_back(iter_move(std::as_const(cit)));
|
||||
--cit;
|
||||
values.emplace_back(std::ranges::iter_move(cit));
|
||||
cit--;
|
||||
values.emplace_back(std::ranges::iter_move(std::as_const(cit)));
|
||||
--cit; // `it` points to element of `Pattern` from here
|
||||
values.emplace_back(iter_move(cit));
|
||||
cit--;
|
||||
values.emplace_back(iter_move(std::as_const(cit)));
|
||||
--cit;
|
||||
values.emplace_back(std::ranges::iter_move(cit));
|
||||
cit--;
|
||||
values.emplace_back(std::ranges::iter_move(std::as_const(cit)));
|
||||
|
||||
assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
|
||||
assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
|
||||
}
|
||||
|
||||
assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_moved_from));
|
||||
}
|
||||
|
||||
{ // Test `iter_move` when result is proxy rvalue reference type, which is different from
|
||||
// range_rvalue_reference_t<InnerRng> and range_rvalue_reference_t<Pattern>.
|
||||
using Inner = std::vector<MoveOnlyInt>;
|
||||
using V = std::vector<Inner>;
|
||||
using Pattern = std::ranges::subrange<ProxyIter, ProxyIter>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
|
||||
|
||||
static_assert(!std::same_as<std::ranges::range_rvalue_reference_t<V>, std::ranges::range_rvalue_reference_t<JWV>>);
|
||||
static_assert(
|
||||
!std::same_as<std::ranges::range_rvalue_reference_t<Pattern>, std::ranges::range_rvalue_reference_t<JWV>>);
|
||||
static_assert(std::same_as<CommonProxyRvalueRef, std::ranges::range_rvalue_reference_t<JWV>>);
|
||||
|
||||
V v;
|
||||
v.reserve(2);
|
||||
v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
|
||||
v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
|
||||
|
||||
auto pattern = std::ranges::to<std::vector<MoveOnlyInt>>(std::views::iota(4, 12));
|
||||
Pattern pattern_as_subrange(ProxyIter{pattern.data()}, ProxyIter{pattern.data() + pattern.size()});
|
||||
|
||||
JWV jwv(std::move(v), pattern_as_subrange);
|
||||
assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_normally_constructed));
|
||||
|
||||
{
|
||||
std::vector<MoveOnlyInt> values;
|
||||
values.reserve(8);
|
||||
|
||||
auto it = jwv.begin();
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref1 = iter_move(it);
|
||||
values.emplace_back(rref1.get());
|
||||
++it;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref2 = iter_move(std::as_const(it));
|
||||
values.emplace_back(rref2.get());
|
||||
it++;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref3 = std::ranges::iter_move(it);
|
||||
values.emplace_back(rref3.get());
|
||||
++it;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(it));
|
||||
values.emplace_back(rref4.get());
|
||||
it++; // `it` points to element of `Pattern` from here
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref5 = iter_move(it);
|
||||
values.emplace_back(rref5.get());
|
||||
++it;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref6 = iter_move(std::as_const(it));
|
||||
values.emplace_back(rref6.get());
|
||||
it++;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref7 = std::ranges::iter_move(it);
|
||||
values.emplace_back(rref7.get());
|
||||
++it;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(it));
|
||||
values.emplace_back(rref8.get());
|
||||
|
||||
assert(std::ranges::equal(values, std::views::iota(0, 8)));
|
||||
assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<MoveOnlyInt> values;
|
||||
values.reserve(8);
|
||||
|
||||
auto cit = std::prev(std::as_const(jwv).end());
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref1 = iter_move(cit);
|
||||
values.emplace_back(rref1.get());
|
||||
cit--;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref2 = iter_move(std::as_const(cit));
|
||||
values.emplace_back(rref2.get());
|
||||
--cit;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref3 = std::ranges::iter_move(cit);
|
||||
values.emplace_back(rref3.get());
|
||||
cit--;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(cit));
|
||||
values.emplace_back(rref4.get());
|
||||
--cit; // `it` points to element of `Pattern` from here
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref5 = iter_move(cit);
|
||||
values.emplace_back(rref5.get());
|
||||
cit--;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref6 = iter_move(std::as_const(cit));
|
||||
values.emplace_back(rref6.get());
|
||||
--cit;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref7 = std::ranges::iter_move(cit);
|
||||
values.emplace_back(rref7.get());
|
||||
cit--;
|
||||
std::same_as<CommonProxyRvalueRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(cit));
|
||||
values.emplace_back(rref8.get());
|
||||
|
||||
assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
|
||||
assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
|
||||
}
|
||||
|
||||
assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_moved_from));
|
||||
}
|
||||
|
||||
{ // Make sure `iter_move` calls underlying's iterator `iter_move` (not `std::move(*i)`).
|
||||
using Inner = std::vector<int>;
|
||||
using InnerTrackingIter = IterMoveTrackingIterator<Inner::iterator>;
|
||||
using TrackingInner = std::ranges::subrange<InnerTrackingIter>;
|
||||
using Pattern = std::array<int, 1>;
|
||||
using PatternTrackingIter = IterMoveTrackingIterator<Pattern::iterator>;
|
||||
using TrackingPattern = std::ranges::subrange<PatternTrackingIter>;
|
||||
using JWV = std::ranges::join_with_view<std::span<TrackingInner>, TrackingPattern>;
|
||||
|
||||
std::array<Inner, 2> v{{{1}, {2}}};
|
||||
Pattern pat{-1};
|
||||
|
||||
bool v_moved = false;
|
||||
std::array<TrackingInner, 2> tracking_v{
|
||||
TrackingInner(InnerTrackingIter(v[0].begin(), &v_moved), InnerTrackingIter(v[0].end())),
|
||||
TrackingInner(InnerTrackingIter(v[1].begin()), InnerTrackingIter(v[1].end()))};
|
||||
|
||||
bool pat_moved = false;
|
||||
TrackingPattern tracking_pat(PatternTrackingIter(pat.begin(), &pat_moved), PatternTrackingIter(pat.end()));
|
||||
|
||||
JWV jwv(tracking_v, tracking_pat);
|
||||
auto it = jwv.begin();
|
||||
|
||||
// Test calling `iter_move` when `it` points to element of `v`
|
||||
assert(!v_moved);
|
||||
assert(iter_move(it) == 1);
|
||||
assert(v_moved);
|
||||
|
||||
// Test calling `iter_move` when `it` points to element of `pat`
|
||||
++it;
|
||||
assert(!pat_moved);
|
||||
assert(iter_move(it) == -1);
|
||||
assert(pat_moved);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// friend constexpr void iter_swap(const iterator& x, const iterator& y);
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
template <class I>
|
||||
concept CanIterSwap = requires(I i) { iter_swap(i); };
|
||||
|
||||
enum class SwapKind { no_swap, with_same_type, with_different_type };
|
||||
enum class IterKind { inner_view, pattern };
|
||||
|
||||
template <std::forward_iterator Iter, IterKind Kind>
|
||||
class IterSwapTrackingIterator {
|
||||
public:
|
||||
using value_type = std::iter_value_t<Iter>;
|
||||
using difference_type = std::iter_difference_t<Iter>;
|
||||
|
||||
constexpr Iter get_iter() const { return iter_; }
|
||||
|
||||
constexpr SwapKind* get_flag() const { return flag_; }
|
||||
|
||||
IterSwapTrackingIterator() = default;
|
||||
constexpr explicit IterSwapTrackingIterator(Iter iter, SwapKind* flag = nullptr)
|
||||
: iter_(std::move(iter)), flag_(flag) {}
|
||||
|
||||
constexpr IterSwapTrackingIterator& operator++() {
|
||||
++iter_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr IterSwapTrackingIterator operator++(int) {
|
||||
auto tmp = *this;
|
||||
++*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
constexpr decltype(auto) operator*() const { return *iter_; }
|
||||
|
||||
constexpr bool operator==(const IterSwapTrackingIterator& other) const { return iter_ == other.iter_; }
|
||||
|
||||
friend constexpr decltype(auto) iter_swap(const IterSwapTrackingIterator& lhs, const IterSwapTrackingIterator& rhs) {
|
||||
assert(lhs.flag_ != nullptr && rhs.flag_ != nullptr);
|
||||
*lhs.flag_ = *rhs.flag_ = SwapKind::with_same_type;
|
||||
return std::ranges::iter_swap(lhs.iter_, rhs.iter_);
|
||||
}
|
||||
|
||||
template <std::indirectly_swappable<Iter> OtherIter, IterKind OtherKind>
|
||||
friend constexpr decltype(auto)
|
||||
iter_swap(const IterSwapTrackingIterator& lhs, const IterSwapTrackingIterator<OtherIter, OtherKind>& rhs) {
|
||||
assert(lhs.flag_ != nullptr && rhs.get_flag() != nullptr);
|
||||
*lhs.flag_ = *rhs.get_flag() = SwapKind::with_different_type;
|
||||
return std::ranges::iter_swap(lhs.iter_, rhs.get_iter());
|
||||
}
|
||||
|
||||
private:
|
||||
Iter iter_ = Iter();
|
||||
SwapKind* flag_ = nullptr;
|
||||
};
|
||||
|
||||
static_assert(std::forward_iterator<IterSwapTrackingIterator<int*, IterKind::inner_view>> &&
|
||||
!std::bidirectional_iterator<IterSwapTrackingIterator<int*, IterKind::inner_view>>);
|
||||
|
||||
constexpr bool test() {
|
||||
{ // Test common usage
|
||||
using V = std::vector<std::string>;
|
||||
using Pattern = std::string;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
JWV jwv(V{"std", "ranges", "views", "join_with_view"}, Pattern{":: "});
|
||||
assert(std::ranges::equal(jwv, "std:: ranges:: views:: join_with_view"sv));
|
||||
|
||||
auto it = jwv.begin();
|
||||
iter_swap(it, std::ranges::next(it, 2)); // Swap elements of the same inner range.
|
||||
assert(std::ranges::equal(jwv, "dts:: ranges:: views:: join_with_view"sv));
|
||||
|
||||
std::ranges::advance(it, 3);
|
||||
iter_swap(std::as_const(it), std::ranges::next(it, 2)); // Swap elements of the pattern.
|
||||
assert(std::ranges::equal(jwv, "dts ::ranges ::views ::join_with_view"sv));
|
||||
|
||||
std::ranges::advance(it, 3);
|
||||
const auto it2 = jwv.begin();
|
||||
iter_swap(std::as_const(it), it2); // Swap elements of different inner ranges.
|
||||
assert(std::ranges::equal(jwv, "rts ::danges ::views ::join_with_view"sv));
|
||||
|
||||
std::ranges::advance(it, 6);
|
||||
iter_swap(std::as_const(it), it2); // Swap element from inner range with element from the pattern.
|
||||
assert(std::ranges::equal(jwv, " tsr::dangesr::viewsr::join_with_view"sv));
|
||||
|
||||
static_assert(std::is_void_v<decltype(iter_swap(it, it))>);
|
||||
static_assert(std::is_void_v<decltype(iter_swap(it2, it2))>);
|
||||
static_assert(!CanIterSwap<std::ranges::iterator_t<const JWV>>);
|
||||
static_assert(!CanIterSwap<const std::ranges::iterator_t<const JWV>>);
|
||||
}
|
||||
|
||||
{ // Make sure `iter_swap` calls underlying's iterator `iter_swap` (not `ranges::swap(*i1, *i2)`).
|
||||
using Inner = std::vector<int>;
|
||||
using InnerTrackingIter = IterSwapTrackingIterator<Inner::iterator, IterKind::inner_view>;
|
||||
using TrackingInner = std::ranges::subrange<InnerTrackingIter>;
|
||||
using Pattern = std::array<int, 2>;
|
||||
using PatternTrackingIter = IterSwapTrackingIterator<Pattern::iterator, IterKind::pattern>;
|
||||
using TrackingPattern = std::ranges::subrange<PatternTrackingIter>;
|
||||
using JWV = std::ranges::join_with_view<std::span<TrackingInner>, TrackingPattern>;
|
||||
|
||||
std::array<Inner, 3> v{{{1, 2, 3}, {4, 5}}};
|
||||
Pattern pat{-1, -2};
|
||||
|
||||
SwapKind v_swap_kind = SwapKind::no_swap;
|
||||
std::array<TrackingInner, 2> tracking_v{
|
||||
TrackingInner(InnerTrackingIter(v[0].begin(), &v_swap_kind), InnerTrackingIter(v[0].end())),
|
||||
TrackingInner(InnerTrackingIter(v[1].begin(), &v_swap_kind), InnerTrackingIter(v[1].end()))};
|
||||
|
||||
SwapKind pat_swap_kind = SwapKind::no_swap;
|
||||
TrackingPattern tracking_pat(PatternTrackingIter(pat.begin(), &pat_swap_kind), PatternTrackingIter(pat.end()));
|
||||
|
||||
JWV jwv(tracking_v, tracking_pat);
|
||||
auto it1 = jwv.begin();
|
||||
auto it2 = std::ranges::next(it1);
|
||||
|
||||
// Test calling `iter_swap` when both `it1` and `it2` point to elements of `v`.
|
||||
assert(v_swap_kind == SwapKind::no_swap);
|
||||
iter_swap(it1, it2);
|
||||
assert(*it1 == 2 && *it2 == 1);
|
||||
assert(v_swap_kind == SwapKind::with_same_type && pat_swap_kind == SwapKind::no_swap);
|
||||
|
||||
// Test calling `iter_swap` when `it1` points to element of `v` and `it2` points to element of `pat`.
|
||||
std::ranges::advance(it2, 2);
|
||||
v_swap_kind = SwapKind::no_swap;
|
||||
assert(pat_swap_kind == SwapKind::no_swap);
|
||||
iter_swap(it1, it2);
|
||||
assert(*it1 == -1 && *it2 == 2);
|
||||
assert(v_swap_kind == SwapKind::with_different_type && pat_swap_kind == SwapKind::with_different_type);
|
||||
|
||||
// Test calling `iter_swap` when `it1` and `it2` point to elements of `pat`.
|
||||
std::ranges::advance(it1, 4);
|
||||
v_swap_kind = pat_swap_kind = SwapKind::no_swap;
|
||||
iter_swap(it1, it2);
|
||||
assert(*it1 == 2 && *it2 == -2);
|
||||
assert(v_swap_kind == SwapKind::no_swap && pat_swap_kind == SwapKind::with_same_type);
|
||||
|
||||
// Test calling `iter_swap` when `it1` points to element of `pat` and `it2` points to element of `v`.
|
||||
std::ranges::advance(it2, 3);
|
||||
v_swap_kind = pat_swap_kind = SwapKind::no_swap;
|
||||
iter_swap(it1, it2);
|
||||
assert(*it1 == 5 && *it2 == 2);
|
||||
assert(v_swap_kind == SwapKind::with_different_type && pat_swap_kind == SwapKind::with_different_type);
|
||||
}
|
||||
|
||||
{ // InnerIter and PatternIter don't model indirectly swappable
|
||||
using JWV = std::ranges::join_with_view<std::span<std::string>, std::string_view>;
|
||||
static_assert(!CanIterSwap<std::ranges::iterator_t<JWV>>);
|
||||
static_assert(!CanIterSwap<const std::ranges::iterator_t<JWV>>);
|
||||
static_assert(!CanIterSwap<std::ranges::iterator_t<const JWV>>);
|
||||
static_assert(!CanIterSwap<const std::ranges::iterator_t<const JWV>>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// using iterator_concept = see below;
|
||||
// using iterator_category = see below; // not always present
|
||||
// using value_type = see below;
|
||||
// using difference_type = see below;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
namespace test_iterator_concept {
|
||||
template <template <class> class InnerIt>
|
||||
using InnerRange = BasicView<std::vector<int>, ViewProperties{}, InnerIt>;
|
||||
|
||||
template <template <class> class It, template <class> class InnerIt>
|
||||
using View = BasicView<std::vector<InnerRange<InnerIt>>, ViewProperties{}, It>;
|
||||
|
||||
template <template <class> class It, template <class> class InnerIt>
|
||||
using RvalueView = BasicView<RvalueVector<InnerRange<InnerIt>>, ViewProperties{}, It>;
|
||||
|
||||
template <template <class> class It>
|
||||
using Pattern = BasicView<std::vector<int>, ViewProperties{}, It>;
|
||||
|
||||
template <class V, class Pat>
|
||||
using IteratorConcept = std::ranges::iterator_t<std::ranges::join_with_view<V, Pat>>::iterator_concept;
|
||||
|
||||
template <class V, class Pat, class Concept>
|
||||
concept IteratorConceptIs = std::same_as<IteratorConcept<V, Pat>, Concept>;
|
||||
|
||||
// When `iterator<false>::iterator_concept` is `bidirectional_iterator_tag`
|
||||
static_assert(IteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
|
||||
Pattern<bidirectional_iterator>,
|
||||
std::bidirectional_iterator_tag>);
|
||||
|
||||
// When `iterator<false>::iterator_concept` is `forward_iterator_tag`
|
||||
static_assert(IteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
|
||||
Pattern<bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
|
||||
Pattern<bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<View<forward_iterator, forward_iterator>,
|
||||
Pattern<bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<View<forward_iterator, forward_iterator>, //
|
||||
Pattern<forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
|
||||
// When `iterator<false>::iterator_concept` is `input_iterator_tag`
|
||||
static_assert(IteratorConceptIs<View<DefaultCtorInputIter, forward_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<View<forward_iterator, DefaultCtorInputIter>,
|
||||
Pattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<View<DefaultCtorInputIter, DefaultCtorInputIter>,
|
||||
Pattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<RvalueView<bidirectional_iterator, bidirectional_iterator>,
|
||||
Pattern<bidirectional_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(IteratorConceptIs<RvalueView<forward_iterator, forward_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
|
||||
template <class V, class Pat>
|
||||
using ConstIteratorConcept = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pat>>::iterator_concept;
|
||||
|
||||
template <class V, class Pat, class Concept>
|
||||
concept ConstIteratorConceptIs = std::same_as<ConstIteratorConcept<V, Pat>, Concept>;
|
||||
|
||||
// When `iterator<true>::iterator_concept` is `bidirectional_iterator_tag`
|
||||
static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
|
||||
Pattern<bidirectional_iterator>,
|
||||
std::bidirectional_iterator_tag>);
|
||||
|
||||
// When `iterator<true>::iterator_concept` is `forward_iterator_tag`
|
||||
static_assert(ConstIteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
|
||||
Pattern<bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
|
||||
Pattern<bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorConceptIs<View<forward_iterator, forward_iterator>,
|
||||
Pattern<bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorConceptIs<View<forward_iterator, forward_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
|
||||
// `iterator<true>::iterator_concept` cannot be `input_iterator_tag`
|
||||
} // namespace test_iterator_concept
|
||||
|
||||
namespace test_iterator_category {
|
||||
template <template <class> class InnerIt>
|
||||
using InnerRange = BasicView<std::vector<float>, ViewProperties{}, InnerIt>;
|
||||
|
||||
template <bool Common, template <class> class InnerIt>
|
||||
using MaybeCommonInnerRange = BasicView<std::vector<float>, ViewProperties{.common = Common}, InnerIt>;
|
||||
|
||||
template <template <class> class It, template <class> class InnerIt>
|
||||
using View = BasicView<std::vector<InnerRange<InnerIt>>, ViewProperties{}, It>;
|
||||
|
||||
template <template <class> class It, template <class> class InnerIt>
|
||||
using RvalueView = BasicView<RvalueVector<InnerRange<InnerIt>>, ViewProperties{}, It>;
|
||||
|
||||
template <bool Common, template <class> class It, bool CommonInner, template <class> class InnerIt>
|
||||
using MaybeCommonView =
|
||||
BasicView<std::vector<MaybeCommonInnerRange<CommonInner, InnerIt>>, ViewProperties{.common = Common}, It>;
|
||||
|
||||
template <template <class> class It>
|
||||
using Pattern = BasicView<std::vector<float>, ViewProperties{}, It>;
|
||||
|
||||
template <template <class> class It>
|
||||
using RvaluePattern = BasicView<RvalueVector<float>, ViewProperties{}, It>;
|
||||
|
||||
template <bool Common, template <class> class It>
|
||||
using MaybeCommonPattern = BasicView<std::vector<float>, ViewProperties{.common = Common}, It>;
|
||||
|
||||
template <class V, class Pattern>
|
||||
using IteratorCategory = std::ranges::iterator_t<std::ranges::join_with_view<V, Pattern>>::iterator_category;
|
||||
|
||||
template <class V, class Pattern>
|
||||
concept HasIteratorCategory = requires { typename IteratorCategory<V, Pattern>; };
|
||||
|
||||
template <class V, class Pat, class Category>
|
||||
concept IteratorCategoryIs = std::same_as<IteratorCategory<V, Pat>, Category>;
|
||||
|
||||
// When `iterator<false>::iterator_category` is not defined
|
||||
static_assert(!HasIteratorCategory<View<cpp20_input_iterator, forward_iterator>, Pattern<forward_iterator>>);
|
||||
static_assert(!HasIteratorCategory<View<forward_iterator, cpp20_input_iterator>, Pattern<forward_iterator>>);
|
||||
static_assert(!HasIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<cpp20_input_iterator>>);
|
||||
static_assert(!HasIteratorCategory<RvalueView<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
|
||||
static_assert(HasIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
|
||||
|
||||
// When
|
||||
// is_reference_v<common_reference_t<iter_reference_t<InnerIter>,
|
||||
// iter_reference_t<PatternIter>>>
|
||||
// has different values for `iterator<false>`
|
||||
static_assert(IteratorCategoryIs<View<forward_iterator, forward_iterator>,
|
||||
RvaluePattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
|
||||
// When `iterator<false>::iterator_category` is `bidirectional_iterator_tag`
|
||||
static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::bidirectional_iterator_tag>);
|
||||
static_assert(IteratorCategoryIs<MaybeCommonView<false, bidirectional_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::bidirectional_iterator_tag>);
|
||||
|
||||
// When `iterator<false>::iterator_category` is `forward_iterator_tag`
|
||||
static_assert(IteratorCategoryIs<MaybeCommonView<true, forward_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, forward_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, false, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<false, bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(IteratorCategoryIs<MaybeCommonView<false, forward_iterator, false, forward_iterator>,
|
||||
MaybeCommonPattern<false, forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
|
||||
// When `iterator<false>::iterator_category` is `input_iterator_tag`
|
||||
static_assert(IteratorCategoryIs<View<ForwardIteratorWithInputCategory, forward_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(IteratorCategoryIs<View<forward_iterator, ForwardIteratorWithInputCategory>,
|
||||
Pattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(IteratorCategoryIs<View<forward_iterator, forward_iterator>,
|
||||
Pattern<ForwardIteratorWithInputCategory>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(IteratorCategoryIs<View<ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory>,
|
||||
Pattern<ForwardIteratorWithInputCategory>,
|
||||
std::input_iterator_tag>);
|
||||
|
||||
template <class V, class Pattern>
|
||||
using ConstIteratorCategory = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pattern>>::iterator_category;
|
||||
|
||||
template <class V, class Pattern>
|
||||
concept HasConstIteratorCategory = requires { typename ConstIteratorCategory<V, Pattern>; };
|
||||
|
||||
template <class V, class Pat, class Category>
|
||||
concept ConstIteratorCategoryIs = std::same_as<ConstIteratorCategory<V, Pat>, Category>;
|
||||
|
||||
// `iterator<true>::iterator_category` is not defined in those
|
||||
// cases because `join_with_view<V, Pattern>` cannot const-accessed
|
||||
static_assert(!HasConstIteratorCategory<View<cpp20_input_iterator, forward_iterator>, Pattern<forward_iterator>>);
|
||||
static_assert(!HasConstIteratorCategory<View<forward_iterator, cpp20_input_iterator>, Pattern<forward_iterator>>);
|
||||
static_assert(!HasConstIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<cpp20_input_iterator>>);
|
||||
static_assert(!HasConstIteratorCategory<RvalueView<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
|
||||
static_assert(HasConstIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
|
||||
|
||||
// When
|
||||
// is_reference_v<common_reference_t<iter_reference_t<InnerIter>,
|
||||
// iter_reference_t<PatternIter>>>
|
||||
// has different values for `iterator<true>`
|
||||
static_assert(ConstIteratorCategoryIs<View<forward_iterator, forward_iterator>,
|
||||
RvaluePattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
|
||||
// When `iterator<true>::iterator_category` is `bidirectional_iterator_tag`
|
||||
static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::bidirectional_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<MaybeCommonView<false, bidirectional_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::bidirectional_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<
|
||||
BasicVectorView<
|
||||
BasicVectorView<float, ViewProperties{.common = true}, forward_iterator, bidirectional_iterator>,
|
||||
ViewProperties{.common = true},
|
||||
forward_iterator,
|
||||
bidirectional_iterator>,
|
||||
BasicVectorView<float, ViewProperties{.common = true}, forward_iterator, bidirectional_iterator>,
|
||||
std::bidirectional_iterator_tag>);
|
||||
|
||||
// When `iterator<true>::iterator_category` is `forward_iterator_tag`
|
||||
static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, forward_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, forward_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, false, bidirectional_iterator>,
|
||||
MaybeCommonPattern<true, bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
|
||||
MaybeCommonPattern<false, bidirectional_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<MaybeCommonView<false, forward_iterator, false, forward_iterator>,
|
||||
MaybeCommonPattern<false, forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
static_assert(
|
||||
ConstIteratorCategoryIs<
|
||||
BasicVectorView<BasicVectorView<float, ViewProperties{}, ForwardIteratorWithInputCategory, forward_iterator>,
|
||||
ViewProperties{},
|
||||
ForwardIteratorWithInputCategory,
|
||||
forward_iterator>,
|
||||
BasicVectorView<float, ViewProperties{}, ForwardIteratorWithInputCategory, forward_iterator>,
|
||||
std::forward_iterator_tag>);
|
||||
|
||||
// When `iterator<true>::iterator_category` is `input_iterator_tag`
|
||||
static_assert(ConstIteratorCategoryIs<View<ForwardIteratorWithInputCategory, forward_iterator>,
|
||||
Pattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<View<forward_iterator, ForwardIteratorWithInputCategory>,
|
||||
Pattern<forward_iterator>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<View<forward_iterator, forward_iterator>,
|
||||
Pattern<ForwardIteratorWithInputCategory>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<View<ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory>,
|
||||
Pattern<ForwardIteratorWithInputCategory>,
|
||||
std::input_iterator_tag>);
|
||||
static_assert(ConstIteratorCategoryIs<
|
||||
BasicVectorView<
|
||||
BasicVectorView<float, ViewProperties{}, DefaultCtorInputIter, ForwardIteratorWithInputCategory>,
|
||||
ViewProperties{},
|
||||
DefaultCtorInputIter,
|
||||
ForwardIteratorWithInputCategory>,
|
||||
BasicVectorView<float, ViewProperties{}, ForwardIteratorWithInputCategory>,
|
||||
std::input_iterator_tag>);
|
||||
} // namespace test_iterator_category
|
||||
|
||||
namespace test_value_type {
|
||||
template <class ValueType, class ConstValueType = ValueType>
|
||||
struct View : std::ranges::view_base {
|
||||
struct InnerRange : std::ranges::view_base {
|
||||
ValueType* begin();
|
||||
ValueType* end();
|
||||
ConstValueType* begin() const;
|
||||
ConstValueType* end() const;
|
||||
};
|
||||
|
||||
InnerRange* begin();
|
||||
InnerRange* end();
|
||||
const InnerRange* begin() const;
|
||||
const InnerRange* end() const;
|
||||
};
|
||||
|
||||
template <class ValueType, class ConstValueType = ValueType>
|
||||
using Pattern = View<ValueType, ConstValueType>::InnerRange;
|
||||
|
||||
template <class V, class Pat>
|
||||
using IteratorValueType = std::ranges::iterator_t<std::ranges::join_with_view<V, Pat>>::value_type;
|
||||
|
||||
template <class V, class Pat, class ValueType>
|
||||
concept IteratorValueTypeIs = std::same_as<IteratorValueType<V, Pat>, ValueType>;
|
||||
|
||||
// Test that `iterator<false>::value_type` is equal to
|
||||
// common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>
|
||||
static_assert(IteratorValueTypeIs<View<int>, Pattern<int>, int>);
|
||||
static_assert(IteratorValueTypeIs<View<int>, Pattern<long>, long>);
|
||||
static_assert(IteratorValueTypeIs<View<long>, Pattern<int>, long>);
|
||||
static_assert(IteratorValueTypeIs<View<std::nullptr_t>, Pattern<void*>, void*>);
|
||||
static_assert(IteratorValueTypeIs<View<std::tuple<long, int>>, Pattern<std::tuple<int, long>>, std::tuple<long, long>>);
|
||||
|
||||
template <class V, class Pat>
|
||||
using ConstIteratorValueType = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pat>>::value_type;
|
||||
|
||||
template <class V, class Pat, class ValueType>
|
||||
concept ConstIteratorValueTypeIs = std::same_as<ConstIteratorValueType<V, Pat>, ValueType>;
|
||||
|
||||
// Test that `iterator<true>::value_type` is equal to
|
||||
// common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>
|
||||
static_assert(ConstIteratorValueTypeIs<View<int>, Pattern<int>, int>);
|
||||
static_assert(ConstIteratorValueTypeIs<View<int>, Pattern<long>, long>);
|
||||
static_assert(ConstIteratorValueTypeIs<View<long>, Pattern<int>, long>);
|
||||
static_assert(ConstIteratorValueTypeIs<View<std::nullptr_t>, Pattern<void*>, void*>);
|
||||
static_assert(
|
||||
ConstIteratorValueTypeIs<View<std::tuple<long, int>>, Pattern<std::tuple<int, long>>, std::tuple<long, long>>);
|
||||
|
||||
// Test value types of non-simple const ranges
|
||||
static_assert(ConstIteratorValueTypeIs<View<short, int>, Pattern<short, int>, int>);
|
||||
static_assert(ConstIteratorValueTypeIs<View<short, int>, Pattern<int, long>, long>);
|
||||
static_assert(ConstIteratorValueTypeIs<View<int, long>, Pattern<short, int>, long>);
|
||||
static_assert(ConstIteratorValueTypeIs<View<int, std::nullptr_t>, Pattern<int, void*>, void*>);
|
||||
static_assert(ConstIteratorValueTypeIs<View<std::tuple<long, int>, std::pair<long, int>>,
|
||||
Pattern<std::tuple<int, long>, std::pair<int, long>>,
|
||||
std::pair<long, long>>);
|
||||
} // namespace test_value_type
|
||||
|
||||
namespace test_difference_type {
|
||||
template <class DifferenceType, class ValueType>
|
||||
struct Iter {
|
||||
using value_type = std::remove_const_t<ValueType>;
|
||||
using difference_type = DifferenceType;
|
||||
|
||||
ValueType& operator*() const;
|
||||
Iter& operator++();
|
||||
Iter operator++(int);
|
||||
bool operator==(const Iter&) const;
|
||||
};
|
||||
|
||||
static_assert(std::forward_iterator<Iter<int, void*>>);
|
||||
|
||||
template <class DifferenceType,
|
||||
class InnerDifferenceType,
|
||||
class ConstDifferenceType = DifferenceType,
|
||||
class InnerConstDifferenceType = InnerDifferenceType>
|
||||
struct View : std::ranges::view_base {
|
||||
struct InnerRange : std::ranges::view_base {
|
||||
Iter<InnerDifferenceType, float> begin();
|
||||
Iter<InnerDifferenceType, float> end();
|
||||
Iter<InnerConstDifferenceType, double> begin() const;
|
||||
Iter<InnerConstDifferenceType, double> end() const;
|
||||
};
|
||||
|
||||
Iter<DifferenceType, InnerRange> begin();
|
||||
Iter<DifferenceType, InnerRange> end();
|
||||
Iter<ConstDifferenceType, const InnerRange> begin() const;
|
||||
Iter<ConstDifferenceType, const InnerRange> end() const;
|
||||
};
|
||||
|
||||
template <class DifferenceType, class ConstDifferenceType = DifferenceType>
|
||||
struct Pattern : std::ranges::view_base {
|
||||
Iter<DifferenceType, float> begin();
|
||||
Iter<DifferenceType, float> end();
|
||||
Iter<ConstDifferenceType, double> begin() const;
|
||||
Iter<ConstDifferenceType, double> end() const;
|
||||
};
|
||||
|
||||
template <class V, class Pat>
|
||||
using IteratorDifferenceType = std::ranges::iterator_t<std::ranges::join_with_view<V, Pat>>::difference_type;
|
||||
|
||||
template <class V, class Pat, class DifferenceType>
|
||||
concept IteratorDifferenceTypeIs = std::same_as<IteratorDifferenceType<V, Pat>, DifferenceType>;
|
||||
|
||||
// Test that `iterator<false>::difference_type` is equal to
|
||||
// common_type_t<
|
||||
// iter_difference_t<OuterIter>,
|
||||
// iter_difference_t<InnerIter>,
|
||||
// iter_difference_t<PatternIter>>
|
||||
static_assert(IteratorDifferenceTypeIs<View<int, int>, Pattern<int>, int>);
|
||||
static_assert(IteratorDifferenceTypeIs<View<signed char, signed char>, Pattern<signed char>, signed char>);
|
||||
static_assert(IteratorDifferenceTypeIs<View<short, short>, Pattern<short>, short>);
|
||||
static_assert(IteratorDifferenceTypeIs<View<signed char, short>, Pattern<short>, int>);
|
||||
static_assert(IteratorDifferenceTypeIs<View<signed char, short>, Pattern<int>, int>);
|
||||
static_assert(IteratorDifferenceTypeIs<View<long long, long>, Pattern<int>, long long>);
|
||||
static_assert(IteratorDifferenceTypeIs<View<long, long long>, Pattern<int>, long long>);
|
||||
|
||||
template <class V, class Pat>
|
||||
using ConstIteratorDifferenceType = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pat>>::difference_type;
|
||||
|
||||
template <class V, class Pat, class DifferenceType>
|
||||
concept ConstIteratorDifferenceTypeIs = std::same_as<ConstIteratorDifferenceType<V, Pat>, DifferenceType>;
|
||||
|
||||
// Test that `iterator<true>::difference_type` is equal to
|
||||
// common_type_t<
|
||||
// iter_difference_t<OuterIter>,
|
||||
// iter_difference_t<InnerIter>,
|
||||
// iter_difference_t<PatternIter>>
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<int, int>, Pattern<int>, int>);
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<signed char, signed char>, Pattern<signed char>, signed char>);
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<short, short>, Pattern<short>, short>);
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<signed char, short>, Pattern<short>, int>);
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<signed char, short>, Pattern<int>, int>);
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<long long, long>, Pattern<int>, long long>);
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<long, long long>, Pattern<int>, long long>);
|
||||
|
||||
// Test difference types of non-simple const ranges
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<short, short, int, int>, Pattern<short, int>, int>);
|
||||
static_assert(
|
||||
ConstIteratorDifferenceTypeIs<View<int, short, signed char, signed char>, Pattern<long, signed char>, signed char>);
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<long, long long, signed char, short>, Pattern<long, short>, int>);
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<short, short, long long, long>, Pattern<short, int>, long long>);
|
||||
static_assert(ConstIteratorDifferenceTypeIs<View<signed char, signed char, long, long long>,
|
||||
Pattern<signed char, int>,
|
||||
long long>);
|
||||
} // namespace test_difference_type
|
||||
@@ -0,0 +1,360 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// std::views::join_with_view
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "test_iterators.h"
|
||||
|
||||
template <class View, class T>
|
||||
concept CanBePiped = requires(View&& view, T&& t) {
|
||||
{ std::forward<View>(view) | std::forward<T>(t) };
|
||||
};
|
||||
|
||||
struct Range : std::ranges::view_base {
|
||||
using Iterator = forward_iterator<std::string_view*>;
|
||||
using Sentinel = sentinel_wrapper<Iterator>;
|
||||
constexpr explicit Range(std::string_view* b, std::string_view* e) : begin_(b), end_(e) {}
|
||||
constexpr Iterator begin() const { return Iterator(begin_); }
|
||||
constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
|
||||
|
||||
private:
|
||||
std::string_view* begin_;
|
||||
std::string_view* end_;
|
||||
};
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
using Iterator = forward_iterator<const char*>;
|
||||
using Sentinel = sentinel_wrapper<Iterator>;
|
||||
static constexpr std::string_view pat{", "};
|
||||
|
||||
constexpr Pattern() = default;
|
||||
constexpr Iterator begin() const { return Iterator(pat.data()); }
|
||||
constexpr Sentinel end() const { return Sentinel(Iterator(pat.data() + pat.size())); }
|
||||
};
|
||||
|
||||
struct NonCopyablePattern : Pattern {
|
||||
NonCopyablePattern(const NonCopyablePattern&) = delete;
|
||||
};
|
||||
|
||||
template <typename View>
|
||||
constexpr void compareViews(View v, std::string_view list) {
|
||||
auto b1 = v.begin();
|
||||
auto e1 = v.end();
|
||||
auto b2 = list.begin();
|
||||
auto e2 = list.end();
|
||||
for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
|
||||
assert(*b1 == *b2);
|
||||
}
|
||||
assert(b1 == e1);
|
||||
assert(b2 == e2);
|
||||
}
|
||||
|
||||
constexpr int absoluteValue(int x) { return x < 0 ? -x : x; }
|
||||
|
||||
template <class T>
|
||||
constexpr const T&& asConstRvalue(T&& t) {
|
||||
return static_cast<const T&&>(t);
|
||||
}
|
||||
|
||||
constexpr void test_adaptor_with_pattern(std::span<std::string_view> buff) {
|
||||
// Test `views::join_with(pattern)(v)`
|
||||
{
|
||||
using Result = std::ranges::join_with_view<Range, Pattern>;
|
||||
const Range range(buff.data(), buff.data() + buff.size());
|
||||
Pattern pattern;
|
||||
|
||||
{
|
||||
// 'views::join_with(pattern)' - &&
|
||||
std::same_as<Result> decltype(auto) result = std::views::join_with(pattern)(range);
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(pattern)' - const&&
|
||||
std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(pattern))(range);
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(pattern)' - &
|
||||
auto partial = std::views::join_with(pattern);
|
||||
std::same_as<Result> decltype(auto) result = partial(range);
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(pattern)' - const&
|
||||
auto const partial = std::views::join_with(pattern);
|
||||
std::same_as<Result> decltype(auto) result = partial(range);
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
}
|
||||
|
||||
// Test `v | views::join_with(pattern)`
|
||||
{
|
||||
using Result = std::ranges::join_with_view<Range, Pattern>;
|
||||
const Range range(buff.data(), buff.data() + buff.size());
|
||||
Pattern pattern;
|
||||
|
||||
{
|
||||
// 'views::join_with(pattern)' - &&
|
||||
std::same_as<Result> decltype(auto) result = range | std::views::join_with(pattern);
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(pattern)' - const&&
|
||||
std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(pattern));
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(pattern)' - &
|
||||
auto partial = std::views::join_with(pattern);
|
||||
std::same_as<Result> decltype(auto) result = range | partial;
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(pattern)' - const&
|
||||
auto const partial = std::views::join_with(pattern);
|
||||
std::same_as<Result> decltype(auto) result = range | partial;
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
}
|
||||
|
||||
// Test `views::join_with(v, pattern)` range adaptor object
|
||||
{
|
||||
using Result = std::ranges::join_with_view<Range, Pattern>;
|
||||
const Range range(buff.data(), buff.data() + buff.size());
|
||||
Pattern pattern;
|
||||
|
||||
{
|
||||
// 'views::join_with' - &&
|
||||
auto range_adaptor = std::views::join_with;
|
||||
std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with' - const&&
|
||||
const auto range_adaptor = std::views::join_with;
|
||||
std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with' - &
|
||||
auto range_adaptor = std::views::join_with;
|
||||
std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with' - const&
|
||||
const auto range_adaptor = std::views::join_with;
|
||||
std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);
|
||||
compareViews(result, "abcd, ef, ghij, kl");
|
||||
}
|
||||
}
|
||||
|
||||
// Test `adaptor | views::join_with(pattern)`
|
||||
{
|
||||
auto pred = [](std::string_view s) { return s.size() >= 3; };
|
||||
using Result = std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, Pattern>;
|
||||
const Range range(buff.data(), buff.data() + buff.size());
|
||||
Pattern pattern;
|
||||
|
||||
{
|
||||
std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(pattern);
|
||||
compareViews(result, "abcd, ghij");
|
||||
}
|
||||
{
|
||||
const auto partial = std::views::filter(pred) | std::views::join_with(pattern);
|
||||
std::same_as<Result> decltype(auto) result = range | partial;
|
||||
compareViews(result, "abcd, ghij");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void test_adaptor_with_single_element(std::span<std::string_view> buff) {
|
||||
// Test `views::join_with(element)(v)`
|
||||
{
|
||||
using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
|
||||
const Range range(buff.data(), buff.data() + buff.size());
|
||||
const char element = '.';
|
||||
|
||||
{
|
||||
// 'views::join_with(element)' - &&
|
||||
std::same_as<Result> decltype(auto) result = std::views::join_with(element)(range);
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(element)' - const&&
|
||||
std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(element))(range);
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(element)' - &
|
||||
auto partial = std::views::join_with(element);
|
||||
std::same_as<Result> decltype(auto) result = partial(range);
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(element)' - const&
|
||||
const auto partial = std::views::join_with(element);
|
||||
std::same_as<Result> decltype(auto) result = partial(range);
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
}
|
||||
|
||||
// Test `v | views::join_with(element)`
|
||||
{
|
||||
using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
|
||||
const Range range(buff.data(), buff.data() + buff.size());
|
||||
const char element = '.';
|
||||
|
||||
{
|
||||
// 'views::join_with(element)' - &&
|
||||
std::same_as<Result> decltype(auto) result = range | std::views::join_with(element);
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(element)' - const&&
|
||||
std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(element));
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(element)' - &
|
||||
auto partial = std::views::join_with(element);
|
||||
std::same_as<Result> decltype(auto) result = range | partial;
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with(element)' - const&
|
||||
const auto partial = std::views::join_with(element);
|
||||
std::same_as<Result> decltype(auto) result = range | partial;
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
}
|
||||
|
||||
// Test `views::join_with(v, element)` range adaptor object
|
||||
{
|
||||
using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
|
||||
const Range range(buff.data(), buff.data() + buff.size());
|
||||
const char element = '.';
|
||||
|
||||
{
|
||||
// 'views::join_with' - &&
|
||||
auto range_adaptor = std::views::join_with;
|
||||
std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with' - const&&
|
||||
const auto range_adaptor = std::views::join_with;
|
||||
std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with' - &
|
||||
auto range_adaptor = std::views::join_with;
|
||||
std::same_as<Result> decltype(auto) result = range_adaptor(range, element);
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
{
|
||||
// 'views::join_with' - const&
|
||||
const auto range_adaptor = std::views::join_with;
|
||||
std::same_as<Result> decltype(auto) result = range_adaptor(range, element);
|
||||
compareViews(result, "abcd.ef.ghij.kl");
|
||||
}
|
||||
}
|
||||
|
||||
// Test `adaptor | views::join_with(element)`
|
||||
{
|
||||
auto pred = [](std::string_view s) { return s.size() >= 3; };
|
||||
using Result =
|
||||
std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, std::ranges::single_view<char>>;
|
||||
const Range range(buff.data(), buff.data() + buff.size());
|
||||
const char element = '.';
|
||||
|
||||
{
|
||||
std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(element);
|
||||
compareViews(result, "abcd.ghij");
|
||||
}
|
||||
{
|
||||
const auto partial = std::views::filter(pred) | std::views::join_with(element);
|
||||
std::same_as<Result> decltype(auto) result = range | partial;
|
||||
compareViews(result, "abcd.ghij");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
std::string_view buff[] = {"abcd", "ef", "ghij", "kl"};
|
||||
|
||||
// Test range adaptor object
|
||||
{
|
||||
using RangeAdaptorObject = decltype(std::views::join_with);
|
||||
static_assert(std::is_const_v<RangeAdaptorObject>);
|
||||
|
||||
// The type of a customization point object, ignoring cv-qualifiers, shall model semiregular
|
||||
static_assert(std::semiregular<std::remove_const<RangeAdaptorObject>>);
|
||||
}
|
||||
|
||||
test_adaptor_with_pattern(buff);
|
||||
test_adaptor_with_single_element(buff);
|
||||
|
||||
// Test that one can call std::views::join_with with arbitrary stuff, as long as we
|
||||
// don't try to actually complete the call by passing it a range.
|
||||
//
|
||||
// That makes no sense and we can't do anything with the result, but it's valid.
|
||||
{
|
||||
long array[3] = {1, 2, 3};
|
||||
[[maybe_unused]] auto partial = std::views::join_with(std::move(array));
|
||||
}
|
||||
|
||||
// Test SFINAE friendliness
|
||||
{
|
||||
struct NotAView {};
|
||||
|
||||
static_assert(!CanBePiped<Range, decltype(std::views::join_with)>);
|
||||
static_assert(CanBePiped<Range, decltype(std::views::join_with(Pattern{}))>);
|
||||
static_assert(CanBePiped<Range, decltype(std::views::join_with('.'))>);
|
||||
static_assert(!CanBePiped<NotAView, decltype(std::views::join_with(Pattern{}))>);
|
||||
static_assert(!CanBePiped<NotAView, decltype(std::views::join_with('.'))>);
|
||||
static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with(Pattern{}))>);
|
||||
static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with('.'))>);
|
||||
static_assert(!CanBePiped<Range, decltype(std::views::join_with(NotAView{}))>);
|
||||
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::join_with)>);
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::join_with), Pattern, Range>);
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::join_with), char, Range>);
|
||||
static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, Pattern>);
|
||||
static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, char>);
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, Pattern, Pattern>);
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, char, char>);
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::join_with), NonCopyablePattern>);
|
||||
}
|
||||
|
||||
{
|
||||
static_assert(std::is_same_v<decltype(std::ranges::views::join_with), decltype(std::views::join_with)>);
|
||||
assert(std::addressof(std::ranges::views::join_with) == std::addressof(std::views::join_with));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// [Example 1:
|
||||
// vector<string> vs = {"the", "quick", "brown", "fox"};
|
||||
// for (char c : vs | views::join_with('-')) {
|
||||
// cout << c;
|
||||
// }
|
||||
// // The above prints the-quick-brown-fox
|
||||
// - end example]
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
constexpr bool test() {
|
||||
std::vector<std::string> vs = {"the", "quick", "brown", "fox"};
|
||||
std::string result;
|
||||
for (char c : vs | std::views::join_with('-')) {
|
||||
result += c;
|
||||
}
|
||||
|
||||
return result == "the-quick-brown-fox";
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
assert(test());
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// sentinel() = default;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
constexpr bool test() {
|
||||
using Inner = BasicVectorView<char, ViewProperties{.common = false}, forward_iterator>;
|
||||
using V = BasicVectorView<Inner, ViewProperties{}, forward_iterator>;
|
||||
using Pattern = Inner;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
static_assert(!std::ranges::common_range<JWV>);
|
||||
|
||||
[[maybe_unused]] std::ranges::sentinel_t<JWV> se;
|
||||
[[maybe_unused]] std::ranges::sentinel_t<const JWV> cse;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// constexpr sentinel(sentinel<!Const> s)
|
||||
// requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
constexpr bool test() {
|
||||
{ // Regular conversion from `!Const` to `Const` sentinel
|
||||
using Inner = BasicVectorView<int, ViewProperties{.common = false}, forward_iterator>;
|
||||
std::vector<Inner> vec = {Inner{11, 12}, Inner{13, 14}};
|
||||
|
||||
std::ranges::join_with_view jwv(vec, 0);
|
||||
using JWV = decltype(jwv);
|
||||
static_assert(!std::ranges::common_range<JWV>);
|
||||
|
||||
using Sent = std::ranges::sentinel_t<JWV>;
|
||||
using CSent = std::ranges::sentinel_t<const JWV>;
|
||||
static_assert(!std::same_as<Sent, CSent>);
|
||||
|
||||
Sent se = jwv.end();
|
||||
[[maybe_unused]] CSent cse = se;
|
||||
}
|
||||
|
||||
{ // Test conversion from `Const` to `!Const` (should be invalid)
|
||||
using Inner = BasicVectorView<int, ViewProperties{.common = false}, forward_iterator>;
|
||||
using V = std::vector<Inner>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
|
||||
static_assert(!std::ranges::common_range<JWV>);
|
||||
|
||||
using Sent = std::ranges::sentinel_t<JWV>;
|
||||
using CSent = std::ranges::sentinel_t<const JWV>;
|
||||
static_assert(!std::convertible_to<CSent, Sent>);
|
||||
static_assert(!std::constructible_from<Sent, CSent>);
|
||||
}
|
||||
|
||||
{ // When `convertible_to<sentinel_t<V>, sentinel_t<Base>>` is not modeled
|
||||
using V = ConstOppositeView<std::vector<long>>;
|
||||
using Pattern = std::ranges::single_view<long>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
static_assert(!std::ranges::common_range<JWV>);
|
||||
|
||||
using Sent = std::ranges::sentinel_t<JWV>;
|
||||
using CSent = std::ranges::sentinel_t<const JWV>;
|
||||
static_assert(!std::convertible_to<CSent, Sent>);
|
||||
static_assert(!std::constructible_from<Sent, CSent>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// template<bool OtherConst>
|
||||
// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
|
||||
// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
#include "test_comparisons.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
struct NonCrossConstComparableView : std::ranges::view_base {
|
||||
using NonConstRange = std::vector<int>;
|
||||
NonConstRange* begin();
|
||||
sentinel_wrapper<NonConstRange*> end();
|
||||
|
||||
using ConstRange = BasicVectorView<int, ViewProperties{}, forward_iterator>;
|
||||
ConstRange* begin() const;
|
||||
sentinel_wrapper<ConstRange*> end() const;
|
||||
};
|
||||
|
||||
static_assert(std::ranges::range<NonCrossConstComparableView>);
|
||||
static_assert(std::ranges::range<const NonCrossConstComparableView>);
|
||||
|
||||
constexpr bool test() {
|
||||
using Inner = BasicVectorView<int, ViewProperties{.common = false}, cpp20_input_iterator>;
|
||||
using V = std::vector<Inner>;
|
||||
using Pattern = std::ranges::single_view<int>;
|
||||
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
|
||||
static_assert(!std::ranges::common_range<JWV>);
|
||||
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
using CIter = std::ranges::iterator_t<const JWV>;
|
||||
static_assert(!std::same_as<Iter, CIter>);
|
||||
|
||||
using Sent = std::ranges::sentinel_t<JWV>;
|
||||
using CSent = std::ranges::sentinel_t<const JWV>;
|
||||
static_assert(!std::same_as<Sent, CSent>);
|
||||
|
||||
{ // Compare iterator<Const> with sentinel<Const>
|
||||
{ // Const == true
|
||||
AssertEqualityReturnBool<CIter, CSent>();
|
||||
const JWV jwv(V{Inner{1, 2}, Inner{4}}, 3);
|
||||
assert(testEquality(std::ranges::next(jwv.begin(), 4), jwv.end(), true));
|
||||
assert(testEquality(jwv.begin(), jwv.end(), false));
|
||||
}
|
||||
|
||||
{ // Const == false
|
||||
AssertEqualityReturnBool<Iter, Sent>();
|
||||
JWV jwv(V{Inner{5}, Inner{7, 8}}, 6);
|
||||
assert(testEquality(std::ranges::next(jwv.begin(), 4), jwv.end(), true));
|
||||
assert(testEquality(std::ranges::next(jwv.begin(), 2), jwv.end(), false));
|
||||
}
|
||||
}
|
||||
|
||||
{ // Compare iterator<Const> with sentinel<!Const>
|
||||
{ // Const == true
|
||||
AssertEqualityReturnBool<CIter, Sent>();
|
||||
JWV jwv(V{Inner{9, 10}, Inner{12}}, 11);
|
||||
assert(testEquality(std::ranges::next(std::as_const(jwv).begin(), 4), jwv.end(), true));
|
||||
assert(testEquality(std::ranges::next(std::as_const(jwv).begin(), 2), jwv.end(), false));
|
||||
}
|
||||
|
||||
{ // Const == false
|
||||
AssertEqualityReturnBool<Iter, CSent>();
|
||||
JWV jwv(V{Inner{13}, Inner{15, 16}}, 14);
|
||||
assert(testEquality(std::ranges::next(jwv.begin(), 4), std::as_const(jwv).end(), true));
|
||||
assert(testEquality(std::ranges::next(jwv.begin(), 3), std::as_const(jwv).end(), false));
|
||||
}
|
||||
}
|
||||
|
||||
{ // Check invalid comparisons between iterator<Const> and sentinel<!Const>
|
||||
using JWV2 = std::ranges::join_with_view<NonCrossConstComparableView, Pattern>;
|
||||
static_assert(!std::ranges::common_range<JWV2>);
|
||||
|
||||
static_assert(!weakly_equality_comparable_with<std::ranges::iterator_t<const JWV2>, std::ranges::sentinel_t<JWV2>>);
|
||||
static_assert(!weakly_equality_comparable_with<std::ranges::iterator_t<JWV2>, std::ranges::sentinel_t<const JWV2>>);
|
||||
|
||||
// Those should be valid
|
||||
static_assert(weakly_equality_comparable_with<std::ranges::iterator_t<JWV2>, std::ranges::sentinel_t<JWV2>>);
|
||||
static_assert(
|
||||
weakly_equality_comparable_with<std::ranges::iterator_t<const JWV2>, std::ranges::sentinel_t<const JWV2>>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// constexpr View base() const& requires copy_constructible<View>;
|
||||
// constexpr View base() &&;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using InnerRange = std::vector<int>;
|
||||
|
||||
struct Range : std::ranges::view_base {
|
||||
constexpr explicit Range(InnerRange* b, InnerRange* e) : begin_(b), end_(e) {}
|
||||
constexpr Range(const Range& other) : begin_(other.begin_), end_(other.end_), was_copy_initialized_(true) {}
|
||||
constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), was_move_initialized_(true) {}
|
||||
Range& operator=(const Range&) = default;
|
||||
Range& operator=(Range&&) = default;
|
||||
constexpr InnerRange* begin() const { return begin_; }
|
||||
constexpr InnerRange* end() const { return end_; }
|
||||
|
||||
InnerRange* begin_;
|
||||
InnerRange* end_;
|
||||
bool was_copy_initialized_ = false;
|
||||
bool was_move_initialized_ = false;
|
||||
};
|
||||
|
||||
static_assert(std::ranges::view<Range>);
|
||||
static_assert(std::ranges::input_range<Range>);
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
static constexpr int pat[2] = {0, 0};
|
||||
constexpr const int* begin() const { return pat; }
|
||||
constexpr const int* end() const { return pat + 2; }
|
||||
};
|
||||
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
|
||||
template <class Tp>
|
||||
struct NonCopyableRange : std::ranges::view_base {
|
||||
NonCopyableRange(const NonCopyableRange&) = delete;
|
||||
NonCopyableRange(NonCopyableRange&&) = default;
|
||||
NonCopyableRange& operator=(const NonCopyableRange&) = default;
|
||||
NonCopyableRange& operator=(NonCopyableRange&&) = default;
|
||||
Tp* begin() const;
|
||||
Tp* end() const;
|
||||
};
|
||||
|
||||
static_assert(!std::copy_constructible<NonCopyableRange<InnerRange>>);
|
||||
static_assert(!std::copy_constructible<NonCopyableRange<int>>);
|
||||
|
||||
template <typename T>
|
||||
concept CanCallBaseOn = requires(T&& t) { std::forward<T>(t).base(); };
|
||||
|
||||
constexpr bool test() {
|
||||
InnerRange buff[3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
|
||||
Pattern pattern;
|
||||
|
||||
{ // Check the const& overload
|
||||
Range range(buff, buff + 3);
|
||||
std::ranges::join_with_view<Range, Pattern> view(range, pattern);
|
||||
std::same_as<Range> decltype(auto) result = view.base();
|
||||
assert(result.was_copy_initialized_);
|
||||
assert(result.begin() == buff);
|
||||
assert(result.end() == buff + 3);
|
||||
}
|
||||
|
||||
{ // Check the const& overload on const `view`
|
||||
Range range(buff, buff + 3);
|
||||
const std::ranges::join_with_view<Range, Pattern> view(range, pattern);
|
||||
std::same_as<Range> decltype(auto) result = view.base();
|
||||
assert(result.was_copy_initialized_);
|
||||
assert(result.begin() == buff);
|
||||
assert(result.end() == buff + 3);
|
||||
}
|
||||
|
||||
{ // Check the && overload
|
||||
Range range(buff, buff + 3);
|
||||
std::ranges::join_with_view<Range, Pattern> view(range, pattern);
|
||||
std::same_as<Range> decltype(auto) result = std::move(view).base();
|
||||
assert(result.was_move_initialized_);
|
||||
assert(result.begin() == buff);
|
||||
assert(result.end() == buff + 3);
|
||||
}
|
||||
|
||||
{ // Ensure the const& overload is not considered when the base is not copy-constructible
|
||||
static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
|
||||
static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
|
||||
static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&&>);
|
||||
static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&&>);
|
||||
static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>>);
|
||||
}
|
||||
|
||||
{ // Ensure the const& overload does not depend on Pattern's copy-constructability
|
||||
static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>&>);
|
||||
static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>&>);
|
||||
static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>&&>);
|
||||
static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>&&>);
|
||||
static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>>);
|
||||
}
|
||||
|
||||
{ // Check above two at the same time
|
||||
static_assert(
|
||||
!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
|
||||
static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
|
||||
static_assert(
|
||||
!CanCallBaseOn<const std::ranges::join_with_view< NonCopyableRange<InnerRange>, NonCopyableRange<int>>&&>);
|
||||
static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&&>);
|
||||
static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// constexpr auto begin();
|
||||
// constexpr auto begin() const
|
||||
// requires forward_range<const V> &&
|
||||
// forward_range<const Pattern> &&
|
||||
// is_reference_v<range_reference_t<const V>> &&
|
||||
// input_range<range_reference_t<const V>>;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
template <bool Simple>
|
||||
using MaybeSimpleForwardView = BasicView<std::vector<std::string>, ViewProperties{.simple = Simple}, forward_iterator>;
|
||||
|
||||
template <bool Simple>
|
||||
using MaybeSimpleForwardRvalueView =
|
||||
BasicView<RvalueVector<std::string>, ViewProperties{.simple = Simple}, forward_iterator>;
|
||||
|
||||
template <bool Simple>
|
||||
using MaybeSimplePattern = BasicView<std::string, ViewProperties{.simple = Simple}, forward_iterator>;
|
||||
|
||||
template <class V, class Pattern>
|
||||
concept JoinWithViewHasConstBegin = requires(const std::ranges::join_with_view<V, Pattern> jwv) {
|
||||
{ jwv.begin() } -> std::input_iterator;
|
||||
};
|
||||
|
||||
constexpr void test_begin() {
|
||||
using Str = std::string;
|
||||
using Vec = std::vector<Str>;
|
||||
|
||||
{ // `V` models simple-view
|
||||
// `is_reference_v<InnerRng>` is true
|
||||
// `Pattern` models simple-view
|
||||
// `V` and `Pattern` contain some elements
|
||||
using V = MaybeSimpleForwardView<true>;
|
||||
using Pattern = MaybeSimplePattern<true>;
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"A", "B", "C"}), Pattern(Str{">>"}));
|
||||
auto it = jwv.begin();
|
||||
assert(std::ranges::equal(std::views::counted(it, 7), Str{"A>>B>>C"}));
|
||||
}
|
||||
|
||||
{ // `V` does not model simple-view
|
||||
// `is_reference_v<InnerRng>` is true
|
||||
// `Pattern` models simple-view
|
||||
// `V` and `Pattern` are empty
|
||||
using V = MaybeSimpleForwardView<false>;
|
||||
using Pattern = MaybeSimplePattern<true>;
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V(Vec{}), Pattern(Str{}));
|
||||
auto it = jwv.begin();
|
||||
assert(it == jwv.end());
|
||||
}
|
||||
|
||||
{ // `V` models simple-view
|
||||
// `is_reference_v<InnerRng>` is false
|
||||
// `Pattern` models simple-view
|
||||
// `V` contains two elements, `Pattern` is empty
|
||||
using V = MaybeSimpleForwardRvalueView<true>;
|
||||
using Pattern = MaybeSimplePattern<true>;
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"1", "2"}), Pattern(Str{""}));
|
||||
auto it = jwv.begin();
|
||||
assert(*it == '1');
|
||||
assert(*++it == '2');
|
||||
}
|
||||
|
||||
{ // `V` models simple-view
|
||||
// `is_reference_v<InnerRng>` is true
|
||||
// `Pattern` does not model simple-view
|
||||
// `V` contains one element, `Pattern` is empty
|
||||
using V = MaybeSimpleForwardView<true>;
|
||||
using Pattern = MaybeSimplePattern<false>;
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"07"}), Pattern(Str{}));
|
||||
auto it = jwv.begin();
|
||||
assert(*it++ == '0');
|
||||
assert(*it == '7');
|
||||
}
|
||||
|
||||
{ // `V` does not model simple-view
|
||||
// `is_reference_v<InnerRng>` is false
|
||||
// `Pattern` models simple-view
|
||||
// `V` contains three elements (2nd is empty), `Pattern` is not empty
|
||||
using V = MaybeSimpleForwardRvalueView<false>;
|
||||
using Pattern = MaybeSimplePattern<true>;
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"A", "", "C"}), Pattern(Str{"--"}));
|
||||
auto it = jwv.begin();
|
||||
assert(std::ranges::equal(std::views::counted(it, 6), Str("A----C")));
|
||||
}
|
||||
|
||||
{ // `V` does not model simple-view
|
||||
// `is_reference_v<InnerRng>` is true
|
||||
// `Pattern` does not model simple-view
|
||||
// `V` contains some empty elements, `Pattern` is not empty
|
||||
using V = MaybeSimpleForwardView<false>;
|
||||
using Pattern = MaybeSimplePattern<false>;
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"", "", ""}), Pattern(Str{"-"}));
|
||||
auto it = jwv.begin();
|
||||
assert(*it++ == '-');
|
||||
assert(*it == '-');
|
||||
}
|
||||
|
||||
{ // `V` models simple-view
|
||||
// `is_reference_v<InnerRng>` is false
|
||||
// `Pattern` does not model simple-view
|
||||
// `V` contains two elements, `Pattern` is not empty
|
||||
using V = MaybeSimpleForwardRvalueView<true>;
|
||||
using Pattern = MaybeSimplePattern<false>;
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"X", "Z"}), Pattern(Str{"Y"}));
|
||||
auto it = jwv.begin();
|
||||
assert(*it == 'X');
|
||||
assert(*++it == 'Y');
|
||||
assert(*++it == 'Z');
|
||||
}
|
||||
|
||||
{ // `V` does not model simple-view
|
||||
// `is_reference_v<InnerRng>` is false
|
||||
// `Pattern` does not model simple-view
|
||||
// `V` contains two empty elements, `Pattern` is not empty
|
||||
using V = MaybeSimpleForwardRvalueView<false>;
|
||||
using Pattern = MaybeSimplePattern<false>;
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"", ""}), Pattern(Str{"?"}));
|
||||
auto it = jwv.begin();
|
||||
assert(*it == '?');
|
||||
assert(++it == jwv.end());
|
||||
}
|
||||
|
||||
{ // `V` does not model forward range
|
||||
// `V` contains some empty elements, `Pattern` is empty
|
||||
using V = BasicView<Vec, ViewProperties{.common = false}, cpp20_input_iterator>;
|
||||
using Pattern = MaybeSimplePattern<false>;
|
||||
std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"", "", ""}), Pattern(Str{""}));
|
||||
auto it = jwv.begin();
|
||||
assert(it == jwv.end());
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void test_const_begin() {
|
||||
using Vec = std::vector<std::array<int, 2>>;
|
||||
using Pat = std::array<int, 2>;
|
||||
|
||||
{ // `const V` models forward range
|
||||
// `const Pattern` models forward range
|
||||
// `is_reference_v<range_reference_t<const V>>` is true
|
||||
// `range_reference_t<const V>` models input range
|
||||
using V = BasicView<Vec, ViewProperties{}, forward_iterator>;
|
||||
using Pattern = BasicView<Pat, ViewProperties{}, forward_iterator>;
|
||||
|
||||
const std::ranges::join_with_view<V, Pattern> jwv{V{Vec{std::array{1, 2}, std::array{3, 4}}}, Pattern{Pat{0, 0}}};
|
||||
auto it = jwv.begin();
|
||||
assert(std::ranges::equal(std::views::counted(it, 6), std::array{1, 2, 0, 0, 3, 4}));
|
||||
}
|
||||
|
||||
// `const V` does not model forward range
|
||||
// `const Pattern` models forward range
|
||||
// `is_reference_v<range_reference_t<const V>>` is true
|
||||
// `range_reference_t<const V>` models input range
|
||||
static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{.common = false}, cpp20_input_iterator>,
|
||||
BasicView<Pat, ViewProperties{}, forward_iterator>>);
|
||||
|
||||
// `const V` models forward range
|
||||
// `const Pattern` does not model forward range
|
||||
// `is_reference_v<range_reference_t<const V>>` is true
|
||||
// `range_reference_t<const V>` models input range
|
||||
static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{}, forward_iterator>,
|
||||
BasicView<Pat, ViewProperties{.common = false}, cpp20_input_iterator>>);
|
||||
|
||||
// `const V` models forward range
|
||||
// `const Pattern` models forward range
|
||||
// `is_reference_v<range_reference_t<const V>>` is false
|
||||
// `range_reference_t<const V>` models input range
|
||||
static_assert(
|
||||
!JoinWithViewHasConstBegin<BasicView<RvalueVector<std::vector<int>>, ViewProperties{}, forward_iterator>,
|
||||
BasicView<Pat, ViewProperties{}, forward_iterator>>);
|
||||
|
||||
// `const V` models forward range
|
||||
// `const Pattern` models forward range
|
||||
// `is_reference_v<range_reference_t<const V>>` is true
|
||||
// `range_reference_t<const V>` does not model input range
|
||||
static_assert(!JoinWithViewHasConstBegin<
|
||||
BasicView<std::vector<InputRangeButOutputWhenConst<int>>, ViewProperties{}, forward_iterator>,
|
||||
BasicView<Pat, ViewProperties{}, forward_iterator>>);
|
||||
|
||||
// `concatable<range_reference_t<const V>, const Pattern>` is not satisfied
|
||||
// See also LWG-4074: compatible-joinable-ranges is underconstrained
|
||||
static_assert(!JoinWithViewHasConstBegin<BasicVectorView<int, ViewProperties{}, forward_iterator>,
|
||||
lwg4074::PatternWithProxyConstAccess>);
|
||||
|
||||
// Check situation when iterators returned by `begin()` and `begin() const` are the same
|
||||
using JWV = std::ranges::join_with_view<MaybeSimpleForwardView<true>, MaybeSimplePattern<true>>;
|
||||
static_assert(std::same_as<std::ranges::iterator_t<JWV&>, std::ranges::iterator_t<const JWV&>>);
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
test_begin();
|
||||
test_const_begin();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// template <input_range V, forward_range Pattern>
|
||||
// requires view<V> && input_range<range_reference_t<V>> && view<Pattern> &&
|
||||
// compatible-joinable-ranges<range_reference_t<V>, Pattern>
|
||||
// class join_with_view;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "test_iterators.h"
|
||||
#include "../types.h"
|
||||
|
||||
template <class View, class Pattern>
|
||||
concept CanFormJoinWithView = requires { typename std::ranges::join_with_view<View, Pattern>; };
|
||||
|
||||
// join_with_view is not valid when `V` is not an input_range
|
||||
namespace test_when_view_is_not_an_input_range {
|
||||
struct View : std::ranges::view_base {
|
||||
using It = cpp20_output_iterator<std::vector<int>*>;
|
||||
It begin();
|
||||
sentinel_wrapper<It> end();
|
||||
};
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
int* begin();
|
||||
int* end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::range<View>);
|
||||
static_assert(!std::ranges::input_range<View>);
|
||||
static_assert(std::ranges::view<View>);
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
static_assert(!CanFormJoinWithView<View, Pattern>);
|
||||
} // namespace test_when_view_is_not_an_input_range
|
||||
|
||||
// join_with_view is not valid when `Pattern` is not a forward_range
|
||||
namespace test_when_pattern_is_not_a_forward_range {
|
||||
struct View : std::ranges::view_base {
|
||||
std::vector<float>* begin();
|
||||
std::vector<float>* end();
|
||||
};
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
using It = cpp20_input_iterator<float*>;
|
||||
It begin();
|
||||
sentinel_wrapper<It> end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<View>);
|
||||
static_assert(std::ranges::view<View>);
|
||||
static_assert(!std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
static_assert(!CanFormJoinWithView<View, Pattern>);
|
||||
} // namespace test_when_pattern_is_not_a_forward_range
|
||||
|
||||
// join_with_view is not valid when `V` does not model std::ranges::view
|
||||
namespace test_when_view_does_not_model_view {
|
||||
struct View {
|
||||
std::vector<double>* begin();
|
||||
std::vector<double>* end();
|
||||
};
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
double* begin();
|
||||
double* end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<View>);
|
||||
static_assert(!std::ranges::view<View>);
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
static_assert(!CanFormJoinWithView<View, Pattern>);
|
||||
} // namespace test_when_view_does_not_model_view
|
||||
|
||||
// join_with_view is not valid when `range_reference_t` of `V` is not an input_range
|
||||
namespace test_when_range_reference_t_of_view_is_not_an_input_range {
|
||||
struct InnerRange {
|
||||
using It = cpp20_output_iterator<long*>;
|
||||
It begin();
|
||||
sentinel_wrapper<It> end();
|
||||
};
|
||||
|
||||
struct View : std::ranges::view_base {
|
||||
InnerRange* begin();
|
||||
InnerRange* end();
|
||||
};
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
long* begin();
|
||||
long* end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::range<InnerRange>);
|
||||
static_assert(!std::ranges::input_range<InnerRange>);
|
||||
static_assert(std::ranges::input_range<View>);
|
||||
static_assert(std::ranges::view<View>);
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
static_assert(!CanFormJoinWithView<View, Pattern>);
|
||||
} // namespace test_when_range_reference_t_of_view_is_not_an_input_range
|
||||
|
||||
// join_with_view is not valid when `Pattern` does not model std::ranges::view
|
||||
namespace test_when_pattern_does_not_model_view {
|
||||
struct View : std::ranges::view_base {
|
||||
std::vector<short>* begin();
|
||||
std::vector<short>* end();
|
||||
};
|
||||
|
||||
struct Pattern {
|
||||
short* begin();
|
||||
short* end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<View>);
|
||||
static_assert(std::ranges::view<View>);
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(!std::ranges::view<Pattern>);
|
||||
static_assert(!CanFormJoinWithView<View, Pattern>);
|
||||
} // namespace test_when_pattern_does_not_model_view
|
||||
|
||||
// join_with_view is not valid when `range_reference_t<View>` and pattern
|
||||
// does not model together compatible-joinable-ranges
|
||||
namespace test_when_used_ranges_are_not_concatable {
|
||||
using std::ranges::range_reference_t;
|
||||
using std::ranges::range_rvalue_reference_t;
|
||||
using std::ranges::range_value_t;
|
||||
|
||||
template <class InnerRange>
|
||||
struct View : std::ranges::view_base {
|
||||
InnerRange* begin();
|
||||
InnerRange* end();
|
||||
};
|
||||
|
||||
namespace no_concat_reference_t {
|
||||
struct ValueType {};
|
||||
|
||||
struct InnerRange {
|
||||
struct It {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = ValueType;
|
||||
struct reference {
|
||||
operator value_type();
|
||||
};
|
||||
|
||||
It& operator++();
|
||||
void operator++(int);
|
||||
reference operator*() const;
|
||||
};
|
||||
|
||||
It begin();
|
||||
sentinel_wrapper<It> end();
|
||||
};
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
struct It {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = ValueType;
|
||||
struct reference {
|
||||
operator value_type();
|
||||
};
|
||||
|
||||
It& operator++();
|
||||
It operator++(int);
|
||||
reference operator*() const;
|
||||
bool operator==(const It&) const;
|
||||
friend value_type&& iter_move(const It&);
|
||||
};
|
||||
|
||||
It begin();
|
||||
It end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<InnerRange>);
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
static_assert(!std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
|
||||
static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
|
||||
static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
|
||||
static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
|
||||
} // namespace no_concat_reference_t
|
||||
|
||||
namespace no_concat_value_t {
|
||||
struct InnerRange {
|
||||
struct It {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
struct value_type {};
|
||||
|
||||
struct reference {
|
||||
operator value_type();
|
||||
operator float();
|
||||
};
|
||||
|
||||
It& operator++();
|
||||
void operator++(int);
|
||||
reference operator*() const;
|
||||
};
|
||||
|
||||
It begin();
|
||||
sentinel_wrapper<It> end();
|
||||
};
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
const float* begin();
|
||||
const float* end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<InnerRange>);
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
|
||||
static_assert(!std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
|
||||
static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
|
||||
static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
|
||||
} // namespace no_concat_value_t
|
||||
|
||||
namespace no_concat_rvalue_reference_t {
|
||||
struct InnerRange {
|
||||
using It = cpp20_input_iterator<int*>;
|
||||
It begin();
|
||||
sentinel_wrapper<It> end();
|
||||
};
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
struct It {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
struct value_type {
|
||||
operator int() const;
|
||||
};
|
||||
|
||||
struct rvalue_reference {
|
||||
operator value_type();
|
||||
};
|
||||
|
||||
It& operator++();
|
||||
It operator++(int);
|
||||
value_type& operator*() const;
|
||||
bool operator==(const It&) const;
|
||||
friend rvalue_reference iter_move(const It&);
|
||||
};
|
||||
|
||||
It begin();
|
||||
It end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<InnerRange>);
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
|
||||
static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
|
||||
static_assert(!std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
|
||||
static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
|
||||
} // namespace no_concat_rvalue_reference_t
|
||||
|
||||
namespace not_concat_indirectly_readable { // Required after LWG-4074 ("compatible-joinable-ranges is underconstrained")
|
||||
struct InnerRange {
|
||||
using It = cpp20_input_iterator<int*>;
|
||||
It begin();
|
||||
sentinel_wrapper<It> end();
|
||||
};
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
lwg4074::Iter begin();
|
||||
lwg4074::Iter end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<InnerRange>);
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
|
||||
static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
|
||||
static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
|
||||
LIBCPP_STATIC_ASSERT(!std::ranges::__concat_indirectly_readable<InnerRange, Pattern>);
|
||||
static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
|
||||
} // namespace not_concat_indirectly_readable
|
||||
} // namespace test_when_used_ranges_are_not_concatable
|
||||
@@ -0,0 +1,230 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// template<class R, class P>
|
||||
// join_with_view(R&&, P&&) -> join_with_view<views::all_t<R>, views::all_t<P>>;
|
||||
//
|
||||
// template<input_range R>
|
||||
// join_with_view(R&&, range_value_t<range_reference_t<R>>)
|
||||
// -> join_with_view<views::all_t<R>, single_view<range_value_t<range_reference_t<R>>>>;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <deque>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_iterators.h"
|
||||
|
||||
struct View : std::ranges::view_base {
|
||||
using It = cpp20_input_iterator<std::deque<int>*>;
|
||||
|
||||
View() = default;
|
||||
It begin() const;
|
||||
sentinel_wrapper<It> end() const;
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<View>);
|
||||
static_assert(std::ranges::view<View>);
|
||||
|
||||
struct Pattern : std::ranges::view_base {
|
||||
Pattern() = default;
|
||||
forward_iterator<int*> begin();
|
||||
forward_iterator<int*> end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::view<Pattern>);
|
||||
|
||||
// A range that is not a view
|
||||
struct Range {
|
||||
using It = cpp20_input_iterator<std::deque<int>*>;
|
||||
|
||||
Range() = default;
|
||||
It begin() const;
|
||||
sentinel_wrapper<It> end() const;
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<Range>);
|
||||
static_assert(!std::ranges::view<Range>);
|
||||
|
||||
// A pattern that is not a view
|
||||
struct RangePattern {
|
||||
RangePattern() = default;
|
||||
forward_iterator<int*> begin();
|
||||
forward_iterator<int*> end();
|
||||
};
|
||||
|
||||
static_assert(std::ranges::forward_range<RangePattern>);
|
||||
static_assert(!std::ranges::view<RangePattern>);
|
||||
|
||||
void test_range_and_pattern_deduction_guide() {
|
||||
{ // Both `v` and `pat` model `std::ranges::view`.
|
||||
{
|
||||
View v;
|
||||
Pattern pat;
|
||||
std::ranges::join_with_view view(v, pat);
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
|
||||
}
|
||||
{
|
||||
View v;
|
||||
std::ranges::join_with_view view(v, Pattern{});
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
|
||||
}
|
||||
{
|
||||
Pattern pat;
|
||||
std::ranges::join_with_view view(View{}, pat);
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
|
||||
}
|
||||
{
|
||||
std::ranges::join_with_view view(View{}, Pattern{});
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Only `pat` models `std::ranges::view`.
|
||||
{
|
||||
Range v;
|
||||
Pattern pat;
|
||||
std::ranges::join_with_view view(v, pat);
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::ref_view<Range>, Pattern>>);
|
||||
}
|
||||
{
|
||||
Range v;
|
||||
std::ranges::join_with_view view(v, Pattern{});
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::ref_view<Range>, Pattern>>);
|
||||
}
|
||||
{
|
||||
Pattern pat;
|
||||
std::ranges::join_with_view view(Range{}, pat);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::owning_view<Range>, Pattern>>);
|
||||
}
|
||||
{
|
||||
std::ranges::join_with_view view(Range{}, Pattern{});
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::owning_view<Range>, Pattern>>);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Only `v` models `std::ranges::view`.
|
||||
{
|
||||
View v;
|
||||
RangePattern pat;
|
||||
std::ranges::join_with_view view(v, pat);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::ref_view<RangePattern>>>);
|
||||
}
|
||||
{
|
||||
View v;
|
||||
std::ranges::join_with_view view(v, RangePattern{});
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::owning_view<RangePattern>>>);
|
||||
}
|
||||
{
|
||||
RangePattern pat;
|
||||
std::ranges::join_with_view view(View{}, pat);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::ref_view<RangePattern>>>);
|
||||
}
|
||||
{
|
||||
std::ranges::join_with_view view(View{}, RangePattern{});
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::owning_view<RangePattern>>>);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Both `v` and `pat` don't model `std::ranges::view`.
|
||||
{
|
||||
Range r;
|
||||
RangePattern pat;
|
||||
std::ranges::join_with_view view(r, pat);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(view),
|
||||
std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::ref_view<RangePattern>>>);
|
||||
}
|
||||
{
|
||||
Range r;
|
||||
std::ranges::join_with_view view(r, RangePattern{});
|
||||
static_assert(std::is_same_v<
|
||||
decltype(view),
|
||||
std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::owning_view<RangePattern>>>);
|
||||
}
|
||||
{
|
||||
RangePattern pat;
|
||||
std::ranges::join_with_view view(Range{}, pat);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(view),
|
||||
std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::ref_view<RangePattern>>>);
|
||||
}
|
||||
{
|
||||
std::ranges::join_with_view view(Range{}, RangePattern{});
|
||||
static_assert(
|
||||
std::is_same_v<
|
||||
decltype(view),
|
||||
std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::owning_view<RangePattern>>>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_range_and_element_deduction_guide() {
|
||||
{ // Element is lvalue
|
||||
int elem = 0;
|
||||
|
||||
{
|
||||
View v;
|
||||
std::ranges::join_with_view view(v, elem);
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
|
||||
}
|
||||
{
|
||||
std::ranges::join_with_view view(View{}, elem);
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
|
||||
}
|
||||
{
|
||||
Range r;
|
||||
std::ranges::join_with_view view(r, elem);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view),
|
||||
std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::single_view<int>>>);
|
||||
}
|
||||
{
|
||||
std::ranges::join_with_view view(Range{}, elem);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view),
|
||||
std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::single_view<int>>>);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Element is rvalue
|
||||
{
|
||||
View v;
|
||||
std::ranges::join_with_view view(v, 1);
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
|
||||
}
|
||||
{
|
||||
std::ranges::join_with_view view(View{}, 1);
|
||||
static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
|
||||
}
|
||||
{
|
||||
Range r;
|
||||
std::ranges::join_with_view view(r, 1);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view),
|
||||
std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::single_view<int>>>);
|
||||
}
|
||||
{
|
||||
std::ranges::join_with_view view(Range{}, 1);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(view),
|
||||
std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::single_view<int>>>);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// join_with_view()
|
||||
// requires default_initializable<V> && default_initializable<Pattern> = default;
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
|
||||
static constexpr auto view = std::to_array<std::array<int, 2>>({{1, 2}, {3, 4}, {5, 6}});
|
||||
|
||||
struct TrivialView : std::ranges::view_base {
|
||||
int val_; // intentionally uninitialized
|
||||
|
||||
constexpr auto begin() { return view.data(); }
|
||||
constexpr auto end() { return view.data() + view.size(); }
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<TrivialView> && std::is_trivially_default_constructible_v<TrivialView>);
|
||||
|
||||
struct NonDefaultConstructibleView : TrivialView {
|
||||
NonDefaultConstructibleView(int);
|
||||
};
|
||||
|
||||
struct TrivialPattern : std::ranges::view_base {
|
||||
int val_; // intentionally uninitialized
|
||||
|
||||
constexpr int* begin() { return &val_; }
|
||||
constexpr int* end() { return &val_ + 1; }
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<TrivialPattern> &&
|
||||
std::is_trivially_default_constructible_v<TrivialPattern>);
|
||||
|
||||
struct NonDefaultConstructiblePattern : TrivialPattern {
|
||||
NonDefaultConstructiblePattern(int);
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
{ // Check if `base_` and `pattern_` are value initialised
|
||||
std::ranges::join_with_view<TrivialView, TrivialPattern> v;
|
||||
assert(std::move(v).base().val_ == 0);
|
||||
assert(std::ranges::equal(v, std::array{1, 2, 0, 3, 4, 0, 5, 6}));
|
||||
}
|
||||
|
||||
{ // Default constructor should not be explicit
|
||||
[[maybe_unused]] std::ranges::join_with_view<TrivialView, TrivialPattern> v = {};
|
||||
}
|
||||
|
||||
static_assert(std::default_initializable<std::ranges::join_with_view<TrivialView, TrivialPattern>>);
|
||||
static_assert(!std::default_initializable<std::ranges::join_with_view<TrivialView, NonDefaultConstructiblePattern>>);
|
||||
static_assert(!std::default_initializable<std::ranges::join_with_view<NonDefaultConstructibleView, TrivialPattern>>);
|
||||
static_assert(!std::default_initializable<
|
||||
std::ranges::join_with_view<NonDefaultConstructibleView, NonDefaultConstructiblePattern>>);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// template<input_range R>
|
||||
// requires constructible_from<V, views::all_t<R>> &&
|
||||
// constructible_from<Pattern, single_view<range_value_t<InnerRng>>>
|
||||
// constexpr explicit join_with_view(R&& r, range_value_t<InnerRng> e);
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../types.h"
|
||||
#include "test_iterators.h"
|
||||
#include "test_range.h"
|
||||
|
||||
struct MoveOnlyInt {
|
||||
MoveOnlyInt() = default;
|
||||
MoveOnlyInt(MoveOnlyInt&&) = default;
|
||||
MoveOnlyInt& operator=(MoveOnlyInt&&) = default;
|
||||
|
||||
constexpr MoveOnlyInt(int val) : val_(val) {}
|
||||
constexpr operator int() const { return val_; }
|
||||
|
||||
int val_ = 0;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::common_type<MoveOnlyInt, int> {
|
||||
using type = int;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::common_type<int, MoveOnlyInt> {
|
||||
using type = int;
|
||||
};
|
||||
|
||||
struct OutputView : std::ranges::view_base {
|
||||
using It = cpp20_output_iterator<int*>;
|
||||
It begin() const;
|
||||
sentinel_wrapper<It> end() const;
|
||||
};
|
||||
|
||||
static_assert(std::ranges::output_range<OutputView, int>);
|
||||
static_assert(std::ranges::view<OutputView>);
|
||||
|
||||
struct InputRange {
|
||||
using It = cpp20_input_iterator<int*>;
|
||||
It begin() const;
|
||||
sentinel_wrapper<It> end() const;
|
||||
};
|
||||
|
||||
struct InputView : InputRange, std::ranges::view_base {};
|
||||
|
||||
static_assert(std::ranges::input_range<InputRange>);
|
||||
static_assert(std::ranges::input_range<const InputRange>);
|
||||
static_assert(std::ranges::view<InputView>);
|
||||
static_assert(std::ranges::input_range<InputView>);
|
||||
static_assert(std::ranges::input_range<const InputView>);
|
||||
|
||||
class View : public std::ranges::view_base {
|
||||
using OuterRange = std::array<std::array<MoveOnlyInt, 2>, 3>;
|
||||
|
||||
static constexpr OuterRange range_on_input_view = {{{1, 1}, {1, 1}, {1, 1}}};
|
||||
static constexpr OuterRange range_on_ref_input_range = {{{2, 2}, {2, 2}, {2, 2}}};
|
||||
static constexpr OuterRange range_on_const_ref_input_range = {{{3, 3}, {3, 3}, {3, 3}}};
|
||||
static constexpr OuterRange range_on_owning_input_range = {{{4, 4}, {4, 4}, {4, 4}}};
|
||||
|
||||
const OuterRange* r_;
|
||||
|
||||
public:
|
||||
// Those functions should never be called in this test.
|
||||
View(View&&) { assert(false); }
|
||||
View(OutputView) { assert(false); }
|
||||
View& operator=(View&&) {
|
||||
assert(false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr explicit View(InputView) : r_(&range_on_input_view) {}
|
||||
constexpr explicit View(InputRange) = delete;
|
||||
constexpr explicit View(std::ranges::ref_view<InputRange>) : r_(&range_on_ref_input_range) {}
|
||||
constexpr explicit View(std::ranges::ref_view<const InputRange>) : r_(&range_on_const_ref_input_range) {}
|
||||
constexpr explicit View(std::ranges::owning_view<InputRange>) : r_(&range_on_owning_input_range) {}
|
||||
|
||||
constexpr auto begin() const { return r_->begin(); }
|
||||
constexpr auto end() const { return r_->end(); }
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<View>);
|
||||
static_assert(std::ranges::input_range<const View>);
|
||||
|
||||
class Pattern : public std::ranges::view_base {
|
||||
int val_;
|
||||
|
||||
public:
|
||||
// Those functions should never be called in this test.
|
||||
Pattern(Pattern&&) { assert(false); }
|
||||
template <class T>
|
||||
Pattern(const std::ranges::single_view<T>&) {
|
||||
assert(false);
|
||||
}
|
||||
Pattern& operator=(Pattern&&) {
|
||||
assert(false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr explicit Pattern(std::ranges::single_view<T>&& v) : val_(v[0]) {}
|
||||
|
||||
constexpr const int* begin() const { return &val_; }
|
||||
constexpr const int* end() const { return &val_ + 1; }
|
||||
};
|
||||
|
||||
static_assert(std::ranges::forward_range<Pattern>);
|
||||
static_assert(std::ranges::forward_range<const Pattern>);
|
||||
|
||||
constexpr void test_ctor_with_view_and_element() {
|
||||
// Check construction from `r` and `e`, when `r` models `std::ranges::view`
|
||||
|
||||
{ // `r` and `e` are glvalues
|
||||
InputView r;
|
||||
int e = 0;
|
||||
std::ranges::join_with_view<View, Pattern> jwv(r, e);
|
||||
assert(std::ranges::equal(jwv, std::array{1, 1, 0, 1, 1, 0, 1, 1}));
|
||||
}
|
||||
|
||||
{ // `r` and `e` are const glvalues
|
||||
const InputView r;
|
||||
const int e = 1;
|
||||
std::ranges::join_with_view<View, Pattern> jwv(r, e);
|
||||
assert(std::ranges::equal(jwv, std::array{1, 1, 1, 1, 1, 1, 1, 1}));
|
||||
}
|
||||
|
||||
{ // `r` and `e` are prvalues
|
||||
std::ranges::join_with_view<View, Pattern> jwv(InputView{}, MoveOnlyInt{2});
|
||||
assert(std::ranges::equal(jwv, std::array{1, 1, 2, 1, 1, 2, 1, 1}));
|
||||
}
|
||||
|
||||
{ // `r` and `e` are xvalues
|
||||
InputView r;
|
||||
MoveOnlyInt e = 3;
|
||||
std::ranges::join_with_view<View, Pattern> jwv(std::move(r), std::move(e));
|
||||
assert(std::ranges::equal(jwv, std::array{1, 1, 3, 1, 1, 3, 1, 1}));
|
||||
}
|
||||
|
||||
// Check explicitness
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputView, MoveOnlyInt>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputView, int>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputView&, int&>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const InputView, const int>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const InputView&, const int&>);
|
||||
}
|
||||
|
||||
constexpr void test_ctor_with_non_view_and_element() {
|
||||
// Check construction from `r` and `e`, when `r` does not model `std::ranges::view`
|
||||
|
||||
{ // `r` and `e` are glvalues
|
||||
InputRange r;
|
||||
int e = 0;
|
||||
std::ranges::join_with_view<View, Pattern> jwv(r, e);
|
||||
assert(std::ranges::equal(jwv, std::array{2, 2, 0, 2, 2, 0, 2, 2}));
|
||||
}
|
||||
|
||||
{ // `r` and `e` are const glvalues
|
||||
const InputRange r;
|
||||
const int e = 1;
|
||||
std::ranges::join_with_view<View, Pattern> jwv(r, e);
|
||||
assert(std::ranges::equal(jwv, std::array{3, 3, 1, 3, 3, 1, 3, 3}));
|
||||
}
|
||||
|
||||
{ // `r` and `e` are prvalues
|
||||
std::ranges::join_with_view<View, Pattern> jwv(InputRange{}, MoveOnlyInt{2});
|
||||
assert(std::ranges::equal(jwv, std::array{4, 4, 2, 4, 4, 2, 4, 4}));
|
||||
}
|
||||
|
||||
{ // `r` and `e` are xvalues
|
||||
InputRange r;
|
||||
MoveOnlyInt e = 3;
|
||||
std::ranges::join_with_view<View, Pattern> jwv(std::move(r), std::move(e));
|
||||
assert(std::ranges::equal(jwv, std::array{4, 4, 3, 4, 4, 3, 4, 4}));
|
||||
}
|
||||
|
||||
// Check explicitness
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputRange, MoveOnlyInt>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputRange, int>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputRange&, int&>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const InputRange&, const int&>);
|
||||
}
|
||||
|
||||
constexpr void test_constraints() {
|
||||
{ // `R` is not an input range
|
||||
using R = OutputView;
|
||||
static_assert(!std::ranges::input_range<R>);
|
||||
static_assert(std::constructible_from<View, std::views::all_t<R>>);
|
||||
static_assert(std::constructible_from<Pattern, std::ranges::single_view<int>>);
|
||||
static_assert(!std::constructible_from<std::ranges::join_with_view<View, Pattern>, R, int>);
|
||||
}
|
||||
|
||||
{ // `V` is not constructible from `views::all_t<R>`
|
||||
using R = test_range<cpp20_input_iterator>;
|
||||
static_assert(std::ranges::input_range<R>);
|
||||
static_assert(!std::constructible_from<View, std::views::all_t<R>>);
|
||||
static_assert(std::constructible_from<Pattern, std::ranges::single_view<int>>);
|
||||
static_assert(!std::constructible_from<std::ranges::join_with_view<View, Pattern>, R, int>);
|
||||
}
|
||||
|
||||
{ // `Pattern` is not constructible from `single_view<range_value_t<InnerRng>>`
|
||||
using R = InputView;
|
||||
using Pat = test_view<forward_iterator>;
|
||||
static_assert(std::ranges::input_range<R>);
|
||||
static_assert(std::constructible_from<View, std::views::all_t<R>>);
|
||||
static_assert(!std::constructible_from<Pat, std::ranges::single_view<int>>);
|
||||
static_assert(!std::constructible_from<std::ranges::join_with_view<View, Pat>, R, int>);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
test_ctor_with_view_and_element();
|
||||
test_ctor_with_non_view_and_element();
|
||||
test_constraints();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// constexpr explicit join_with_view(V base, Pattern pattern);
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
class View : public std::ranges::view_base {
|
||||
using OuterRange = std::array<std::array<int, 2>, 3>;
|
||||
|
||||
static constexpr OuterRange default_range = {{{1, 2}, {3, 4}, {5, 6}}};
|
||||
static constexpr OuterRange range_on_move = {{{6, 5}, {4, 3}, {2, 1}}};
|
||||
|
||||
const OuterRange* r_ = &default_range;
|
||||
|
||||
public:
|
||||
View() = default;
|
||||
constexpr View(const View&) : r_(&default_range) {}
|
||||
constexpr View(View&&) : r_(&range_on_move) {}
|
||||
|
||||
constexpr View& operator=(View) {
|
||||
r_ = &default_range;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto begin() { return r_->begin(); }
|
||||
constexpr auto end() { return r_->end(); }
|
||||
};
|
||||
|
||||
class Pattern : public std::ranges::view_base {
|
||||
using PatternRange = std::array<int, 2>;
|
||||
|
||||
static constexpr PatternRange default_range = {0, 0};
|
||||
static constexpr PatternRange range_on_move = {7, 7};
|
||||
|
||||
const PatternRange* val_ = &default_range;
|
||||
|
||||
public:
|
||||
Pattern() = default;
|
||||
constexpr Pattern(const Pattern&) : val_(&default_range) {}
|
||||
constexpr Pattern(Pattern&&) : val_(&range_on_move) {}
|
||||
|
||||
constexpr Pattern& operator=(Pattern) {
|
||||
val_ = &default_range;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto begin() { return val_->begin(); }
|
||||
constexpr auto end() { return val_->end(); }
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
{ // Check construction from `view` and `pattern`
|
||||
{ // `view` and `pattern` are glvalues
|
||||
View v;
|
||||
Pattern p;
|
||||
std::ranges::join_with_view<View, Pattern> jwv(v, p);
|
||||
assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
|
||||
}
|
||||
|
||||
{ // `view` and `pattern` are const glvalues
|
||||
const View v;
|
||||
const Pattern p;
|
||||
std::ranges::join_with_view<View, Pattern> jwv(v, p);
|
||||
assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
|
||||
}
|
||||
|
||||
{ // `view` and `pattern` are prvalues
|
||||
std::ranges::join_with_view<View, Pattern> jwv(View{}, Pattern{});
|
||||
assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
|
||||
}
|
||||
|
||||
{ // `view` and `pattern` are xvalues
|
||||
View v;
|
||||
Pattern p;
|
||||
std::ranges::join_with_view<View, Pattern> jwv(std::move(v), std::move(p));
|
||||
assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
// Check explicitness
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, View, Pattern>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, View&, Pattern&>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const View, const Pattern>);
|
||||
static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const View&, const Pattern&>);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// constexpr auto end();
|
||||
// constexpr auto end() const
|
||||
// requires forward_range<const V> && forward_range<const Pattern> &&
|
||||
// is_reference_v<range_reference_t<const V>> &&
|
||||
// input_range<range_reference_t<const V>>;
|
||||
|
||||
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=10000000
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
template <class V, class Pattern>
|
||||
concept JoinWithViewHasConstEnd = requires(const std::ranges::join_with_view<V, Pattern> jwv) { jwv.end(); };
|
||||
|
||||
template <size_t Bits>
|
||||
requires(Bits < (1 << 7))
|
||||
constexpr void test_end() {
|
||||
constexpr bool v_models_forward_range = static_cast<bool>(Bits & (1 << 0));
|
||||
constexpr bool inner_range_is_reference = static_cast<bool>(Bits & (1 << 1));
|
||||
constexpr bool inner_range_models_forward_range = static_cast<bool>(Bits & (1 << 2));
|
||||
constexpr bool v_models_common_range = static_cast<bool>(Bits & (1 << 3));
|
||||
constexpr bool inner_range_models_common_range = static_cast<bool>(Bits & (1 << 4));
|
||||
constexpr bool v_models_simple_range = static_cast<bool>(Bits & (1 << 5));
|
||||
constexpr bool pattern_models_simple_range = static_cast<bool>(Bits & (1 << 6));
|
||||
|
||||
constexpr ViewProperties inner_range_props{.common = inner_range_models_common_range};
|
||||
using InnerRange =
|
||||
std::conditional_t<inner_range_models_forward_range,
|
||||
BasicView<std::vector<int>, inner_range_props, forward_iterator>,
|
||||
BasicView<std::vector<int>, inner_range_props, DefaultCtorInputIter>>;
|
||||
|
||||
constexpr ViewProperties v_props{.simple = v_models_simple_range, .common = v_models_common_range};
|
||||
using UnderlyingV = std::conditional_t<inner_range_is_reference, std::vector<InnerRange>, RvalueVector<InnerRange>>;
|
||||
using V = std::conditional_t<v_models_forward_range,
|
||||
BasicView<UnderlyingV, v_props, forward_iterator>,
|
||||
BasicView<UnderlyingV, v_props, DefaultCtorInputIter>>;
|
||||
|
||||
using UnderlyingPattern = std::vector<int>;
|
||||
using Pattern = BasicView<UnderlyingPattern, ViewProperties{.simple = pattern_models_simple_range}, forward_iterator>;
|
||||
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Iter = std::ranges::iterator_t<JWV>;
|
||||
|
||||
// Test when `JWV` models common range
|
||||
static_assert(std::same_as<Iter, std::ranges::sentinel_t<JWV>> ==
|
||||
(v_models_forward_range && inner_range_is_reference && inner_range_models_forward_range &&
|
||||
v_models_common_range && inner_range_models_common_range));
|
||||
|
||||
{ // `V` and `Pattern` are empty
|
||||
V v{};
|
||||
Pattern pattern{};
|
||||
JWV jwv(std::move(v), std::move(pattern));
|
||||
Iter it = jwv.begin();
|
||||
std::sentinel_for<Iter> decltype(auto) se = jwv.end();
|
||||
assert(it == se);
|
||||
}
|
||||
|
||||
{ // `V` is empty, `Pattern` contains some elements
|
||||
V v{};
|
||||
Pattern pattern{std::vector<int>{0}};
|
||||
JWV jwv(std::move(v), std::move(pattern));
|
||||
Iter it = jwv.begin();
|
||||
std::sentinel_for<Iter> decltype(auto) se = jwv.end();
|
||||
assert(it == se);
|
||||
}
|
||||
|
||||
{ // `V` is not empty, `Pattern is empty`
|
||||
V v{UnderlyingV{
|
||||
std::vector<InnerRange>{InnerRange(std::vector<int>{1, 2, 3}), InnerRange(std::vector<int>{4, 5, 6})}}};
|
||||
Pattern pattern{};
|
||||
JWV jwv(std::move(v), std::move(pattern));
|
||||
Iter it = jwv.begin();
|
||||
std::sentinel_for<Iter> decltype(auto) se = jwv.end();
|
||||
assert(std::ranges::next(it, 6) == se);
|
||||
}
|
||||
|
||||
{ // `V` and `Pattern` are not empty
|
||||
V v{UnderlyingV{std::vector<InnerRange>{
|
||||
InnerRange(std::vector<int>{6, 5}),
|
||||
InnerRange(std::vector<int>{4, 3}),
|
||||
InnerRange(std::vector<int>{2, 1, 0})}}};
|
||||
Pattern pattern{std::vector<int>{-1, -1}};
|
||||
JWV jwv(std::move(v), std::move(pattern));
|
||||
Iter it = jwv.begin();
|
||||
std::sentinel_for<Iter> decltype(auto) se = jwv.end();
|
||||
assert(std::ranges::next(it, 11) == se);
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t Bits>
|
||||
requires(Bits < (1 << 7))
|
||||
constexpr void test_const_end() {
|
||||
constexpr bool const_v_models_forward_range = static_cast<bool>(Bits & (1 << 0));
|
||||
constexpr bool const_pattern_models_forward_range = static_cast<bool>(Bits & (1 << 1));
|
||||
constexpr bool inner_const_range_is_reference = static_cast<bool>(Bits & (1 << 2));
|
||||
constexpr bool inner_const_range_models_input_range = static_cast<bool>(Bits & (1 << 3));
|
||||
constexpr bool inner_const_range_models_forward_range = static_cast<bool>(Bits & (1 << 4));
|
||||
constexpr bool const_v_models_common_range = static_cast<bool>(Bits & (1 << 5));
|
||||
constexpr bool inner_const_range_models_common_range = static_cast<bool>(Bits & (1 << 6));
|
||||
|
||||
constexpr ViewProperties inner_range_props{.common = inner_const_range_models_common_range};
|
||||
using InnerRange =
|
||||
std::conditional_t<inner_const_range_models_forward_range,
|
||||
BasicView<std::vector<int>, inner_range_props, forward_iterator>,
|
||||
std::conditional_t<inner_const_range_models_input_range,
|
||||
BasicView<std::vector<int>, inner_range_props, DefaultCtorInputIter>,
|
||||
InputRangeButOutputWhenConst<int>>>;
|
||||
|
||||
constexpr ViewProperties v_props{.common = const_v_models_common_range};
|
||||
using UnderlyingV =
|
||||
std::conditional_t<inner_const_range_is_reference, std::vector<InnerRange>, RvalueVector<InnerRange>>;
|
||||
using V = std::conditional_t<const_v_models_forward_range,
|
||||
BasicView<UnderlyingV, v_props, forward_iterator>,
|
||||
BasicView<UnderlyingV, v_props, DefaultCtorInputIter>>;
|
||||
using Pattern =
|
||||
std::conditional_t<const_pattern_models_forward_range,
|
||||
BasicView<std::vector<int>, ViewProperties{}, forward_iterator>,
|
||||
ForwardViewButInputWhenConst<int>>;
|
||||
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
static_assert(JoinWithViewHasConstEnd<V, Pattern> ==
|
||||
(const_v_models_forward_range && const_pattern_models_forward_range && inner_const_range_is_reference &&
|
||||
(inner_const_range_models_input_range || inner_const_range_models_forward_range)));
|
||||
static_assert(JoinWithViewHasConstEnd<V, Pattern> == std::ranges::range<const JWV>);
|
||||
|
||||
if constexpr (std::ranges::range<const JWV>) {
|
||||
using ConstIter = std::ranges::iterator_t<const JWV>;
|
||||
|
||||
// Test when `const JWV` models common range
|
||||
static_assert(std::same_as<ConstIter, std::ranges::sentinel_t<const JWV>> ==
|
||||
(inner_const_range_models_forward_range && const_v_models_common_range &&
|
||||
inner_const_range_models_common_range));
|
||||
|
||||
{ // `const V` and `const Pattern` are empty
|
||||
V v{};
|
||||
Pattern pattern{};
|
||||
const JWV jwv(std::move(v), std::move(pattern));
|
||||
ConstIter it = jwv.begin();
|
||||
std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
|
||||
assert(it == se);
|
||||
}
|
||||
|
||||
{ // `const V` is empty, `const Pattern` contains some elements
|
||||
V v{};
|
||||
Pattern pattern{std::vector<int>{1}};
|
||||
const JWV jwv(std::move(v), std::move(pattern));
|
||||
ConstIter it = jwv.begin();
|
||||
std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
|
||||
assert(it == se);
|
||||
}
|
||||
|
||||
{ // `const V` is not empty, `const Pattern is empty`
|
||||
V v{UnderlyingV{
|
||||
std::vector<InnerRange>{InnerRange(std::vector<int>{1, 2, 3}), InnerRange(std::vector<int>{4, 5, 6})}}};
|
||||
Pattern pattern{};
|
||||
const JWV jwv(std::move(v), std::move(pattern));
|
||||
ConstIter it = jwv.begin();
|
||||
std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
|
||||
assert(std::ranges::next(it, 6) == se);
|
||||
}
|
||||
|
||||
{ // `const V` and `const Pattern` are not empty
|
||||
V v{UnderlyingV{std::vector<InnerRange>{
|
||||
InnerRange(std::vector<int>{1}), InnerRange(std::vector<int>{2, 2}), InnerRange(std::vector<int>{3, 3, 3})}}};
|
||||
Pattern pattern{std::vector<int>{0}};
|
||||
const JWV jwv(std::move(v), std::move(pattern));
|
||||
ConstIter it = jwv.begin();
|
||||
std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
|
||||
assert(std::ranges::next(it, 8) == se);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
[]<std::size_t... Bits>(std::index_sequence<Bits...>) {
|
||||
(test_end<Bits>(), ...);
|
||||
(test_const_end<Bits>(), ...);
|
||||
}(std::make_index_sequence<(1 << 7)>{});
|
||||
|
||||
{ // Check situation when iterators returned by `end()` and `end() const` are of the same type
|
||||
using V = BasicView<std::vector<std::string>, ViewProperties{.simple = true}, forward_iterator>;
|
||||
using Pattern = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Sentinel = std::ranges::sentinel_t<JWV&>;
|
||||
using ConstSentinel = std::ranges::sentinel_t<const JWV&>;
|
||||
static_assert(std::input_iterator<Sentinel>);
|
||||
static_assert(std::input_iterator<ConstSentinel>);
|
||||
static_assert(std::same_as<Sentinel, ConstSentinel>);
|
||||
}
|
||||
|
||||
{ // Check situation when sentinels returned by `end()` and `end() const` are of the same type
|
||||
using V = BasicView<std::vector<std::string>, ViewProperties{.simple = true, .common = false}, forward_iterator>;
|
||||
using Pattern = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
|
||||
using JWV = std::ranges::join_with_view<V, Pattern>;
|
||||
using Sentinel = std::ranges::sentinel_t<JWV&>;
|
||||
using ConstSentinel = std::ranges::sentinel_t<const JWV&>;
|
||||
static_assert(!std::input_iterator<Sentinel>);
|
||||
static_assert(!std::input_iterator<ConstSentinel>);
|
||||
static_assert(std::same_as<Sentinel, ConstSentinel>);
|
||||
}
|
||||
|
||||
// Check LWG-4074: compatible-joinable-ranges is underconstrained
|
||||
static_assert(!JoinWithViewHasConstEnd<BasicVectorView<int, ViewProperties{}, forward_iterator>,
|
||||
lwg4074::PatternWithProxyConstAccess>);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++23
|
||||
|
||||
// <ranges>
|
||||
|
||||
// class join_with_view : public view_interface<join_with_view<V, Pattern>>
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <concepts>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
template <class T>
|
||||
struct View : std::ranges::view_base {
|
||||
std::vector<T>* begin();
|
||||
std::vector<T>* end();
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Pattern : std::ranges::view_base {
|
||||
T* begin();
|
||||
T* end();
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using JoinWithView = std::ranges::join_with_view<View<T>, Pattern<T>>;
|
||||
|
||||
static_assert(std::derived_from<JoinWithView<int>, std::ranges::view_interface<JoinWithView<int>>>);
|
||||
static_assert(std::derived_from<JoinWithView<void*>, std::ranges::view_interface<JoinWithView<void*>>>);
|
||||
static_assert(std::derived_from<JoinWithView<std::string>, std::ranges::view_interface<JoinWithView<std::string>>>);
|
||||
319
libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
Normal file
319
libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
Normal file
@@ -0,0 +1,319 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
|
||||
#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "test_iterators.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_range.h"
|
||||
|
||||
template <class Tp>
|
||||
void pass_value(Tp);
|
||||
|
||||
template <class Tp, class... Args>
|
||||
concept ConstructionIsExplicit = std::constructible_from<Tp, Args...> && !requires(Args&&... args) {
|
||||
pass_value<Tp>({std::forward<Args>(args)...});
|
||||
};
|
||||
|
||||
struct ViewProperties {
|
||||
bool simple = false;
|
||||
bool common = true;
|
||||
};
|
||||
|
||||
template <std::ranges::input_range Data,
|
||||
ViewProperties Prop,
|
||||
template <class...> class It,
|
||||
template <class...> class ConstIt = It>
|
||||
class BasicView : public std::ranges::view_base {
|
||||
Data data_;
|
||||
|
||||
public:
|
||||
constexpr BasicView()
|
||||
requires std::default_initializable<Data>
|
||||
= default;
|
||||
|
||||
template <class R>
|
||||
constexpr explicit BasicView(R&& r)
|
||||
requires requires { std::ranges::to<Data>(std::forward<R>(r)); }
|
||||
/*******/ : data_(std::ranges::to<Data>(std::forward<R>(r))) {}
|
||||
|
||||
constexpr explicit BasicView(std::initializer_list<std::ranges::range_value_t<Data>> il)
|
||||
: data_(std::ranges::to<Data>(il)) {}
|
||||
|
||||
constexpr auto begin()
|
||||
requires(!Prop.simple)
|
||||
{
|
||||
return It(data_.begin());
|
||||
}
|
||||
|
||||
constexpr auto end()
|
||||
requires(!Prop.simple)
|
||||
{
|
||||
if constexpr (Prop.common)
|
||||
return It(data_.end());
|
||||
else
|
||||
return sentinel_wrapper(It(data_.end()));
|
||||
}
|
||||
|
||||
constexpr auto begin() const { return ConstIt(data_.begin()); }
|
||||
|
||||
constexpr auto end() const {
|
||||
if constexpr (Prop.common)
|
||||
return ConstIt(data_.end());
|
||||
else
|
||||
return sentinel_wrapper(ConstIt(data_.end()));
|
||||
}
|
||||
};
|
||||
|
||||
template <class Tp, ViewProperties Prop, template <class...> class It, template <class...> class ConstIt = It>
|
||||
using BasicVectorView = BasicView<std::vector<Tp>, Prop, It, ConstIt>;
|
||||
|
||||
struct AsPrvalue {
|
||||
template <class Tp>
|
||||
constexpr auto operator()(Tp&& t) const {
|
||||
return std::forward<Tp>(t);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Tp>
|
||||
class RvalueVector {
|
||||
using Vec = std::vector<Tp>;
|
||||
std::ranges::transform_view<std::ranges::owning_view<Vec>, AsPrvalue> range_;
|
||||
|
||||
public:
|
||||
constexpr RvalueVector() = default;
|
||||
constexpr explicit RvalueVector(Vec vec) : range_(std::move(vec), AsPrvalue{}) {}
|
||||
constexpr explicit RvalueVector(std::initializer_list<Tp> il) : RvalueVector(Vec(il)) {}
|
||||
|
||||
constexpr auto begin() { return range_.begin(); }
|
||||
constexpr auto end() { return range_.end(); }
|
||||
constexpr auto begin() const { return range_.begin(); }
|
||||
constexpr auto end() const { return range_.end(); }
|
||||
};
|
||||
|
||||
template <class It>
|
||||
class DefaultCtorInputIter {
|
||||
It it_ = It();
|
||||
|
||||
public:
|
||||
using value_type = std::iter_value_t<It>;
|
||||
using difference_type = std::iter_difference_t<It>;
|
||||
|
||||
DefaultCtorInputIter() = default;
|
||||
constexpr explicit DefaultCtorInputIter(It it) : it_(it) {}
|
||||
|
||||
constexpr DefaultCtorInputIter& operator++() {
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr void operator++(int) { ++*this; }
|
||||
constexpr decltype(auto) operator*() const { return *it_; }
|
||||
constexpr bool operator==(const DefaultCtorInputIter&) const = default;
|
||||
};
|
||||
|
||||
template <class It>
|
||||
DefaultCtorInputIter(It) -> DefaultCtorInputIter<It>;
|
||||
|
||||
template <class Tp>
|
||||
class InputRangeButOutputWhenConst {
|
||||
using Vec = std::vector<Tp>;
|
||||
std::ranges::ref_view<Vec> range_;
|
||||
|
||||
public:
|
||||
constexpr explicit InputRangeButOutputWhenConst(Vec& vec) : range_(vec) {}
|
||||
|
||||
constexpr auto begin() { return cpp20_input_iterator(range_.begin()); }
|
||||
constexpr auto end() { return sentinel_wrapper(cpp20_input_iterator(range_.end())); }
|
||||
constexpr auto begin() const { return cpp20_output_iterator(range_.begin()); }
|
||||
constexpr auto end() const { return sentinel_wrapper(cpp20_output_iterator(range_.end())); }
|
||||
};
|
||||
|
||||
template <class Tp>
|
||||
using ForwardViewButInputWhenConst =
|
||||
BasicVectorView<Tp, ViewProperties{.common = false}, forward_iterator, cpp20_input_iterator>;
|
||||
|
||||
template <class It>
|
||||
class ForwardIteratorWithInputCategory {
|
||||
It it_ = It();
|
||||
|
||||
public:
|
||||
using value_type = std::iter_value_t<It>;
|
||||
using difference_type = std::iter_difference_t<It>;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
ForwardIteratorWithInputCategory() = default;
|
||||
explicit ForwardIteratorWithInputCategory(It it);
|
||||
|
||||
std::iter_reference_t<It> operator*() const;
|
||||
ForwardIteratorWithInputCategory& operator++();
|
||||
ForwardIteratorWithInputCategory operator++(int);
|
||||
bool operator==(const ForwardIteratorWithInputCategory&) const;
|
||||
};
|
||||
|
||||
template <class It>
|
||||
explicit ForwardIteratorWithInputCategory(It) -> ForwardIteratorWithInputCategory<It>;
|
||||
|
||||
template <class It>
|
||||
class EqComparableInputIter {
|
||||
It it_;
|
||||
|
||||
public:
|
||||
using value_type = std::iter_value_t<It>;
|
||||
using difference_type = std::iter_difference_t<It>;
|
||||
|
||||
constexpr explicit EqComparableInputIter(It it) : it_(it) {}
|
||||
|
||||
constexpr decltype(auto) operator*() const { return *it_; }
|
||||
constexpr EqComparableInputIter& operator++() {
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
constexpr void operator++(int) { ++it_; }
|
||||
|
||||
friend constexpr It base(const EqComparableInputIter& i) { return i.it_; }
|
||||
|
||||
friend constexpr bool operator==(const EqComparableInputIter& left, const EqComparableInputIter& right) {
|
||||
return left.it_ == right.it_;
|
||||
}
|
||||
};
|
||||
|
||||
template <class It>
|
||||
EqComparableInputIter(It) -> EqComparableInputIter<It>;
|
||||
|
||||
template <class Val>
|
||||
struct ConstOppositeView : std::ranges::view_base {
|
||||
const Val* begin();
|
||||
sentinel_wrapper<const Val*> end();
|
||||
Val* begin() const;
|
||||
sentinel_wrapper<Val*> end() const;
|
||||
};
|
||||
|
||||
namespace lwg4074 { // Helpers for LWG-4074 ("compatible-joinable-ranges is underconstrained")
|
||||
struct CommonReference;
|
||||
|
||||
struct Value {
|
||||
Value(int);
|
||||
};
|
||||
|
||||
struct Reference {
|
||||
operator Value() const;
|
||||
operator CommonReference() const;
|
||||
};
|
||||
|
||||
struct CommonReference {
|
||||
CommonReference(int);
|
||||
};
|
||||
|
||||
struct Iter {
|
||||
using value_type = Value;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
Iter& operator++();
|
||||
Iter operator++(int);
|
||||
Reference operator*() const;
|
||||
bool operator==(const Iter&) const;
|
||||
};
|
||||
|
||||
struct PatternWithProxyConstAccess {
|
||||
int* begin();
|
||||
int* end();
|
||||
Iter begin() const;
|
||||
Iter end() const;
|
||||
};
|
||||
} // namespace lwg4074
|
||||
|
||||
template <template <class> class TQual, template <class> class UQual>
|
||||
struct std::basic_common_reference<lwg4074::Reference, int, TQual, UQual> {
|
||||
using type = lwg4074::CommonReference;
|
||||
};
|
||||
|
||||
template <template <class> class TQual, template <class> class UQual>
|
||||
struct std::basic_common_reference<int, lwg4074::Reference, TQual, UQual> {
|
||||
using type = lwg4074::CommonReference;
|
||||
};
|
||||
|
||||
namespace selftest {
|
||||
using BV1 = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
|
||||
static_assert(std::ranges::forward_range<BV1>);
|
||||
static_assert(!std::ranges::bidirectional_range<BV1>);
|
||||
static_assert(std::ranges::common_range<BV1>);
|
||||
static_assert(simple_view<BV1>);
|
||||
|
||||
using BV2 =
|
||||
BasicView<RvalueVector<std::string>, ViewProperties{.simple = false, .common = false}, cpp20_input_iterator>;
|
||||
static_assert(std::ranges::input_range<BV2>);
|
||||
static_assert(!std::ranges::forward_range<BV2>);
|
||||
static_assert(!std::ranges::common_range<BV2>);
|
||||
static_assert(!std::is_reference_v<std::ranges::range_reference_t<BV2>>);
|
||||
static_assert(!simple_view<BV2>);
|
||||
|
||||
using RV = RvalueVector<int>;
|
||||
static_assert(std::movable<RV>);
|
||||
static_assert(std::ranges::random_access_range<RV>);
|
||||
static_assert(std::ranges::random_access_range<const RV>);
|
||||
static_assert(!std::is_reference_v<std::ranges::range_reference_t<RV>>);
|
||||
static_assert(!std::is_reference_v<std::ranges::range_reference_t<const RV>>);
|
||||
|
||||
using DCII = DefaultCtorInputIter<int*>;
|
||||
static_assert(std::default_initializable<DCII>);
|
||||
static_assert(std::sentinel_for<DCII, DCII>);
|
||||
static_assert(std::input_iterator<DCII>);
|
||||
static_assert(!std::forward_iterator<DCII>);
|
||||
|
||||
using IRBOWC = InputRangeButOutputWhenConst<int>;
|
||||
static_assert(std::ranges::input_range<IRBOWC>);
|
||||
static_assert(std::ranges::output_range<const IRBOWC&, int>);
|
||||
|
||||
using FVBIWC = ForwardViewButInputWhenConst<int>;
|
||||
static_assert(std::default_initializable<FVBIWC>);
|
||||
static_assert(std::ranges::view<FVBIWC>);
|
||||
static_assert(std::ranges::forward_range<FVBIWC>);
|
||||
static_assert(!std::ranges::common_range<FVBIWC>);
|
||||
static_assert(std::ranges::input_range<const FVBIWC&>);
|
||||
static_assert(!std::ranges::forward_range<const FVBIWC&>);
|
||||
static_assert(!std::ranges::common_range<const FVBIWC&>);
|
||||
|
||||
using FIWIC = ForwardIteratorWithInputCategory<long*>;
|
||||
static_assert(std::forward_iterator<FIWIC>);
|
||||
static_assert(std::same_as<FIWIC::iterator_category, std::input_iterator_tag>);
|
||||
static_assert(std::same_as<FIWIC::iterator_category, std::iterator_traits<FIWIC>::iterator_category>);
|
||||
|
||||
using ECII = EqComparableInputIter<int*>;
|
||||
static_assert(std::input_iterator<ECII>);
|
||||
static_assert(!std::forward_iterator<ECII>);
|
||||
static_assert(std::equality_comparable<ECII>);
|
||||
|
||||
using COV = ConstOppositeView<int>;
|
||||
static_assert(std::ranges::view<COV>);
|
||||
static_assert(std::ranges::range<const COV>);
|
||||
static_assert(!std::ranges::common_range<COV>);
|
||||
static_assert(!std::ranges::common_range<const COV>);
|
||||
static_assert(std::convertible_to<std::ranges::iterator_t<const COV>, std::ranges::iterator_t<COV>>);
|
||||
static_assert(!std::convertible_to<std::ranges::iterator_t<COV>, std::ranges::iterator_t<const COV>>);
|
||||
|
||||
static_assert(std::common_with<lwg4074::Value, int>);
|
||||
static_assert(std::common_with<lwg4074::Value, lwg4074::Reference>);
|
||||
static_assert(std::common_reference_with<lwg4074::Reference, int&>);
|
||||
static_assert(std::common_reference_with<lwg4074::Reference, lwg4074::CommonReference>);
|
||||
static_assert(std::forward_iterator<lwg4074::Iter>);
|
||||
static_assert(std::ranges::forward_range<lwg4074::PatternWithProxyConstAccess>);
|
||||
static_assert(std::ranges::forward_range<const lwg4074::PatternWithProxyConstAccess>);
|
||||
} // namespace selftest
|
||||
|
||||
#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
|
||||
@@ -1115,7 +1115,6 @@ feature_test_macros = [
|
||||
"name": "__cpp_lib_ranges_join_with",
|
||||
"values": {"c++23": 202202},
|
||||
"headers": ["ranges"],
|
||||
"unimplemented": True,
|
||||
},
|
||||
{
|
||||
"name": "__cpp_lib_ranges_repeat",
|
||||
|
||||
Reference in New Issue
Block a user