1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9// REQUIRES: std-at-least-c++23
10
11// <ranges>
12
13// friend constexpr void iter_swap(const iterator& x, const iterator& y);
14
15#include <ranges>
16
17#include <algorithm>
18#include <array>
19#include <cassert>
20#include <span>
21#include <string>
22#include <string_view>
23#include <type_traits>
24#include <utility>
25#include <vector>
26
27template <class I>
28concept CanIterSwap = requires(I i) { iter_swap(i); };
29
30enum class SwapKind { no_swap, with_same_type, with_different_type };
31enum class IterKind { inner_view, pattern };
32
33template <std::forward_iterator Iter, IterKind Kind>
34class IterSwapTrackingIterator {
35public:
36 using value_type = std::iter_value_t<Iter>;
37 using difference_type = std::iter_difference_t<Iter>;
38
39 constexpr Iter get_iter() const { return iter_; }
40
41 constexpr SwapKind* get_flag() const { return flag_; }
42
43 IterSwapTrackingIterator() = default;
44 constexpr explicit IterSwapTrackingIterator(Iter iter, SwapKind* flag = nullptr)
45 : iter_(std::move(iter)), flag_(flag) {}
46
47 constexpr IterSwapTrackingIterator& operator++() {
48 ++iter_;
49 return *this;
50 }
51
52 constexpr IterSwapTrackingIterator operator++(int) {
53 auto tmp = *this;
54 ++*this;
55 return tmp;
56 }
57
58 constexpr decltype(auto) operator*() const { return *iter_; }
59
60 constexpr bool operator==(const IterSwapTrackingIterator& other) const { return iter_ == other.iter_; }
61
62 friend constexpr decltype(auto) iter_swap(const IterSwapTrackingIterator& lhs, const IterSwapTrackingIterator& rhs) {
63 assert(lhs.flag_ != nullptr && rhs.flag_ != nullptr);
64 *lhs.flag_ = *rhs.flag_ = SwapKind::with_same_type;
65 return std::ranges::iter_swap(lhs.iter_, rhs.iter_);
66 }
67
68 template <std::indirectly_swappable<Iter> OtherIter, IterKind OtherKind>
69 friend constexpr decltype(auto)
70 iter_swap(const IterSwapTrackingIterator& lhs, const IterSwapTrackingIterator<OtherIter, OtherKind>& rhs) {
71 assert(lhs.flag_ != nullptr && rhs.get_flag() != nullptr);
72 *lhs.flag_ = *rhs.get_flag() = SwapKind::with_different_type;
73 return std::ranges::iter_swap(lhs.iter_, rhs.get_iter());
74 }
75
76private:
77 Iter iter_ = Iter();
78 SwapKind* flag_ = nullptr;
79};
80
81static_assert(std::forward_iterator<IterSwapTrackingIterator<int*, IterKind::inner_view>> &&
82 !std::bidirectional_iterator<IterSwapTrackingIterator<int*, IterKind::inner_view>>);
83
84constexpr bool test() {
85 { // Test common usage
86 using V = std::vector<std::string>;
87 using Pattern = std::string;
88 using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
89 using namespace std::string_view_literals;
90
91 JWV jwv(V{"std", "ranges", "views", "join_with_view"}, Pattern{":: "});
92 assert(std::ranges::equal(jwv, "std:: ranges:: views:: join_with_view"sv));
93
94 auto it = jwv.begin();
95 iter_swap(it, std::ranges::next(it, 2)); // Swap elements of the same inner range.
96 assert(std::ranges::equal(jwv, "dts:: ranges:: views:: join_with_view"sv));
97
98 std::ranges::advance(it, 3);
99 iter_swap(std::as_const(it), std::ranges::next(it, 2)); // Swap elements of the pattern.
100 assert(std::ranges::equal(jwv, "dts ::ranges ::views ::join_with_view"sv));
101
102 std::ranges::advance(it, 3);
103 const auto it2 = jwv.begin();
104 iter_swap(std::as_const(it), it2); // Swap elements of different inner ranges.
105 assert(std::ranges::equal(jwv, "rts ::danges ::views ::join_with_view"sv));
106
107 std::ranges::advance(it, 6);
108 iter_swap(std::as_const(it), it2); // Swap element from inner range with element from the pattern.
109 assert(std::ranges::equal(jwv, " tsr::dangesr::viewsr::join_with_view"sv));
110
111 static_assert(std::is_void_v<decltype(iter_swap(it, it))>);
112 static_assert(std::is_void_v<decltype(iter_swap(it2, it2))>);
113 static_assert(!CanIterSwap<std::ranges::iterator_t<const JWV>>);
114 static_assert(!CanIterSwap<const std::ranges::iterator_t<const JWV>>);
115 }
116
117 { // Make sure `iter_swap` calls underlying's iterator `iter_swap` (not `ranges::swap(*i1, *i2)`).
118 using Inner = std::vector<int>;
119 using InnerTrackingIter = IterSwapTrackingIterator<Inner::iterator, IterKind::inner_view>;
120 using TrackingInner = std::ranges::subrange<InnerTrackingIter>;
121 using Pattern = std::array<int, 2>;
122 using PatternTrackingIter = IterSwapTrackingIterator<Pattern::iterator, IterKind::pattern>;
123 using TrackingPattern = std::ranges::subrange<PatternTrackingIter>;
124 using JWV = std::ranges::join_with_view<std::span<TrackingInner>, TrackingPattern>;
125
126 std::array<Inner, 3> v{._M_elems: {{1, 2, 3}, {4, 5}}};
127 Pattern pat{-1, -2};
128
129 SwapKind v_swap_kind = SwapKind::no_swap;
130 std::array<TrackingInner, 2> tracking_v{
131 TrackingInner(InnerTrackingIter(v[0].begin(), &v_swap_kind), InnerTrackingIter(v[0].end())),
132 TrackingInner(InnerTrackingIter(v[1].begin(), &v_swap_kind), InnerTrackingIter(v[1].end()))};
133
134 SwapKind pat_swap_kind = SwapKind::no_swap;
135 TrackingPattern tracking_pat(PatternTrackingIter(pat.begin(), &pat_swap_kind), PatternTrackingIter(pat.end()));
136
137 JWV jwv(tracking_v, tracking_pat);
138 auto it1 = jwv.begin();
139 auto it2 = std::ranges::next(it1);
140
141 // Test calling `iter_swap` when both `it1` and `it2` point to elements of `v`.
142 assert(v_swap_kind == SwapKind::no_swap);
143 iter_swap(it1, it2);
144 assert(*it1 == 2 && *it2 == 1);
145 assert(v_swap_kind == SwapKind::with_same_type && pat_swap_kind == SwapKind::no_swap);
146
147 // Test calling `iter_swap` when `it1` points to element of `v` and `it2` points to element of `pat`.
148 std::ranges::advance(it2, 2);
149 v_swap_kind = SwapKind::no_swap;
150 assert(pat_swap_kind == SwapKind::no_swap);
151 iter_swap(it1, it2);
152 assert(*it1 == -1 && *it2 == 2);
153 assert(v_swap_kind == SwapKind::with_different_type && pat_swap_kind == SwapKind::with_different_type);
154
155 // Test calling `iter_swap` when `it1` and `it2` point to elements of `pat`.
156 std::ranges::advance(it1, 4);
157 v_swap_kind = pat_swap_kind = SwapKind::no_swap;
158 iter_swap(it1, it2);
159 assert(*it1 == 2 && *it2 == -2);
160 assert(v_swap_kind == SwapKind::no_swap && pat_swap_kind == SwapKind::with_same_type);
161
162 // Test calling `iter_swap` when `it1` points to element of `pat` and `it2` points to element of `v`.
163 std::ranges::advance(it2, 3);
164 v_swap_kind = pat_swap_kind = SwapKind::no_swap;
165 iter_swap(it1, it2);
166 assert(*it1 == 5 && *it2 == 2);
167 assert(v_swap_kind == SwapKind::with_different_type && pat_swap_kind == SwapKind::with_different_type);
168 }
169
170 { // InnerIter and PatternIter don't model indirectly swappable
171 using JWV = std::ranges::join_with_view<std::span<std::string>, std::string_view>;
172 static_assert(!CanIterSwap<std::ranges::iterator_t<JWV>>);
173 static_assert(!CanIterSwap<const std::ranges::iterator_t<JWV>>);
174 static_assert(!CanIterSwap<std::ranges::iterator_t<const JWV>>);
175 static_assert(!CanIterSwap<const std::ranges::iterator_t<const JWV>>);
176 }
177
178 return true;
179}
180
181int main(int, char**) {
182 test();
183 static_assert(test());
184
185 return 0;
186}
187

source code of libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp