| 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 | |
| 27 | template <class I> |
| 28 | concept CanIterSwap = requires(I i) { iter_swap(i); }; |
| 29 | |
| 30 | enum class SwapKind { no_swap, with_same_type, with_different_type }; |
| 31 | enum class IterKind { inner_view, pattern }; |
| 32 | |
| 33 | template <std::forward_iterator Iter, IterKind Kind> |
| 34 | class IterSwapTrackingIterator { |
| 35 | public: |
| 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 | |
| 76 | private: |
| 77 | Iter iter_ = Iter(); |
| 78 | SwapKind* flag_ = nullptr; |
| 79 | }; |
| 80 | |
| 81 | static_assert(std::forward_iterator<IterSwapTrackingIterator<int*, IterKind::inner_view>> && |
| 82 | !std::bidirectional_iterator<IterSwapTrackingIterator<int*, IterKind::inner_view>>); |
| 83 | |
| 84 | constexpr 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 | |
| 181 | int main(int, char**) { |
| 182 | test(); |
| 183 | static_assert(test()); |
| 184 | |
| 185 | return 0; |
| 186 | } |
| 187 | |