[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:
Jakub Mazurkiewicz
2025-06-21 11:54:50 +02:00
committed by GitHub
parent d6a486c221
commit 1bb2328fd3
49 changed files with 5843 additions and 53 deletions

View File

@@ -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``
---------------------------------------------------------- -----------------

View File

@@ -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
-----------------------------

View File

@@ -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",""
1 Paper # Paper Name Meeting Status First released version Notes
47 `P2273R3 <https://wg21.link/P2273R3>`__ Making ``std::unique_ptr`` constexpr 2022-02 (Virtual) |Complete| 16
48 `P2387R3 <https://wg21.link/P2387R3>`__ Pipe support for user-defined range adaptors 2022-02 (Virtual) |Complete| 19
49 `P2440R1 <https://wg21.link/P2440R1>`__ ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` 2022-02 (Virtual) |Partial| Only ``ranges::iota`` is implemented.
50 `P2441R2 <https://wg21.link/P2441R2>`__ ``views::join_with`` 2022-02 (Virtual) |In Progress| |Complete| 21
51 `P2442R1 <https://wg21.link/P2442R1>`__ Windowing range adaptors: ``views::chunk`` and ``views::slide`` 2022-02 (Virtual)
52 `P2443R1 <https://wg21.link/P2443R1>`__ ``views::chunk_by`` 2022-02 (Virtual) |Complete| 18
53
103 `P2708R1 <https://wg21.link/P2708R1>`__ No Further Fundamentals TSes 2022-11 (Kona) |Nothing To Do|
104
105 `P0290R4 <https://wg21.link/P0290R4>`__ ``apply()`` for ``synchronized_value<T>`` 2023-02 (Issaquah)
106 `P2770R0 <https://wg21.link/P2770R0>`__ Stashing stashing ``iterators`` for proper flattening 2023-02 (Issaquah) |Partial| |Complete| 21 ``join_with_view`` hasn't been done yet since this type isn't implemented yet
107 `P2164R9 <https://wg21.link/P2164R9>`__ ``views::enumerate`` 2023-02 (Issaquah)
108 `P2711R1 <https://wg21.link/P2711R1>`__ Making multi-param constructors of ``views`` ``explicit`` 2023-02 (Issaquah) |In Progress| |Complete| 21 ``join_with_view`` hasn't been done yet since this type isn't implemented yet
109 `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.
110 `P2713R1 <https://wg21.link/P2713R1>`__ Escaping improvements in ``std::format`` 2023-02 (Issaquah) |Complete| 19
111 `P2675R1 <https://wg21.link/P2675R1>`__ ``format``'s width estimation is too approximate and not forward compatible 2023-02 (Issaquah) |Complete| 17

View File

@@ -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)","","",""
1 Issue # Issue Name Meeting Status First released version Notes
66 `LWG4060 <https://wg21.link/LWG4060>`__ ``submdspan`` preconditions do not forbid creating invalid pointer 2024-06 (St. Louis)
67 `LWG4061 <https://wg21.link/LWG4061>`__ Should ``std::basic_format_context`` be default-constructible/copyable/movable? 2024-06 (St. Louis) |Complete| 19
68 `LWG4071 <https://wg21.link/LWG4071>`__ ``reference_wrapper`` comparisons are not SFINAE-friendly 2024-06 (St. Louis) |Complete| 19
69 `LWG4074 <https://wg21.link/LWG4074>`__ ``compatible-joinable-ranges`` is underconstrained 2024-06 (St. Louis) |Complete| 21
70 `LWG4076 <https://wg21.link/LWG4076>`__ ``concat_view`` should be freestanding 2024-06 (St. Louis)
71 `LWG4079 <https://wg21.link/LWG4079>`__ Missing Preconditions in ``concat_view::iterator``\`s conversion constructor 2024-06 (St. Louis)
72 `LWG4082 <https://wg21.link/LWG4082>`__ ``views::concat(r)`` is well-formed when ``r`` is an ``output_range`` 2024-06 (St. Louis)

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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_)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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);

View File

@@ -278,17 +278,11 @@
# 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
# 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
# ifndef __cpp_lib_ranges_repeat
@@ -406,17 +400,11 @@
# 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
# 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
# ifndef __cpp_lib_ranges_repeat

View File

@@ -5633,17 +5633,11 @@
# 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
# 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
# ifndef __cpp_lib_ranges_repeat
@@ -7549,17 +7543,11 @@
# 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
# 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
# ifndef __cpp_lib_ranges_repeat

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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>>>);
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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>>>);

View 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

View File

@@ -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",