223 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710 11// friend constexpr void iter_swap(const inner-iterator& x, const inner-iterator& y)12// noexcept(noexcept(ranges::iter_swap(x.i_.<current>, y.i_.<current>)))13// requires indirectly_swappable<iterator_t<Base>>;14 15#include <ranges>16 17#include <cassert>18#include <type_traits>19#include <utility>20#include "../types.h"21 22namespace adl {23 24template <bool IsNoexcept = false>25struct MaybeNoexceptIterator {26 using value_type = int;27 using difference_type = std::ptrdiff_t;28 29 value_type* ptr_ = nullptr;30 int* iter_swap_invocations_ = nullptr;31 32 constexpr MaybeNoexceptIterator() = default;33 constexpr explicit MaybeNoexceptIterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {}34 35 value_type& operator*() const { return *ptr_; }36 37 MaybeNoexceptIterator& operator++() { ++ptr_; return *this; }38 MaybeNoexceptIterator operator++(int) {39 MaybeNoexceptIterator prev = *this;40 ++ptr_;41 return prev;42 }43 44 MaybeNoexceptIterator& operator--() { --ptr_; return *this; }45 MaybeNoexceptIterator operator--(int) {46 MaybeNoexceptIterator prev = *this;47 --ptr_;48 return prev;49 }50 51 constexpr friend void iter_swap(MaybeNoexceptIterator a, MaybeNoexceptIterator) noexcept(IsNoexcept) {52 if (a.iter_swap_invocations_) {53 ++(*a.iter_swap_invocations_);54 }55 }56 57 friend bool operator==(const MaybeNoexceptIterator& lhs, const MaybeNoexceptIterator& rhs) {58 return lhs.ptr_ == rhs.ptr_;59 }60};61 62template <bool IsNoexcept = false>63struct View : std::ranges::view_base {64 int* iter_swaps = nullptr;65 66 constexpr View() = default;67 constexpr View(int& iter_swap_invocations) : iter_swaps(&iter_swap_invocations) {68 }69 70 constexpr adl::MaybeNoexceptIterator<IsNoexcept> begin() {71 return adl::MaybeNoexceptIterator<IsNoexcept>(*iter_swaps);72 }73 constexpr adl::MaybeNoexceptIterator<IsNoexcept> end() {74 return adl::MaybeNoexceptIterator<IsNoexcept>(*iter_swaps);75 }76};77 78} // namespace adl79 80constexpr bool test() {81 // Can use `iter_swap` with `inner-iterator`; `View` is a forward range.82 {83 // Non-const iterator.84 {85 SplitViewDiff v("abc def", " ");86 auto segment = *v.begin();87 88 auto i1 = segment.begin();89 auto i2 = i1++;90 static_assert(std::is_void_v<decltype(iter_swap(i1, i2))>);91 assert(*i1 == 'b');92 assert(*i2 == 'a');93 94 iter_swap(i1, i2);95 assert(*i1 == 'a');96 assert(*i2 == 'b');97 // Note that `iter_swap` swaps characters in the actual underlying range.98 assert(*v.base().begin() == 'b');99 }100 101 // Const iterator.102 {103 SplitViewDiff v("abc def", " ");104 auto segment = *v.begin();105 106 auto i1 = segment.begin();107 const auto i2 = i1++;108 static_assert(std::is_void_v<decltype(iter_swap(i1, i2))>);109 static_assert(std::is_void_v<decltype(iter_swap(i2, i2))>);110 assert(*i1 == 'b');111 assert(*i2 == 'a');112 113 iter_swap(i1, i2);114 assert(*i1 == 'a');115 assert(*i2 == 'b');116 assert(*v.base().begin() == 'b');117 }118 }119 120 // Can use `iter_swap` with `inner-iterator`; `View` is an input range.121 {122 123 // Non-const iterator.124 {125 // Iterators belong to the same view.126 {127 SplitViewInput v("abc def", ' ');128 auto segment = *v.begin();129 130 auto i1 = segment.begin();131 auto i2 = i1;132 ++i1;133 static_assert(std::is_void_v<decltype(iter_swap(i1, i2))>);134 assert(*i1 == 'b');135 // For an input view, all inner iterators are essentially thin proxies to the same underlying iterator.136 assert(*i2 == 'b');137 138 iter_swap(i1, i2);139 assert(*i1 == 'b');140 assert(*i2 == 'b');141 }142 143 // Iterators belong to different views.144 {145 SplitViewInput v1("abc def", ' ');146 auto val1 = *v1.begin();147 SplitViewInput v2 = v1;148 auto val2 = *v2.begin();149 150 auto i1 = val1.begin();151 auto i2 = val2.begin();152 ++i1;153 assert(*i1 == 'b');154 assert(*i2 == 'a');155 156 iter_swap(i1, i2);157 assert(*i1 == 'a');158 assert(*i2 == 'b');159 }160 }161 162 // Const iterator.163 {164 SplitViewInput v("abc def", ' ');165 auto segment = *v.begin();166 167 const auto i1 = segment.begin();168 const auto i2 = i1;169 static_assert(std::is_void_v<decltype(iter_swap(i1, i2))>);170 assert(*i1 == 'a');171 assert(*i2 == 'a');172 173 iter_swap(i1, i2);174 assert(*i1 == 'a');175 assert(*i2 == 'a');176 }177 }178 179 // Ensure the `iter_swap` customization point is being used.180 {181 int iter_swap_invocations = 0;182 adl::View<> input(iter_swap_invocations);183 std::ranges::lazy_split_view<adl::View<>, adl::View<>> v(input, adl::View<>());184 185 auto segment = *v.begin();186 auto i = segment.begin();187 iter_swap(i, i);188 assert(iter_swap_invocations == 1);189 }190 191 // Check the `noexcept` specification.192 {193 {194 using ThrowingSplitView = std::ranges::lazy_split_view<adl::View<false>, adl::View<false>>;195 using ThrowingValueType = std::ranges::iterator_t<ThrowingSplitView>::value_type;196 using ThrowingIter = std::ranges::iterator_t<ThrowingValueType>;197 ASSERT_NOT_NOEXCEPT(std::ranges::iter_swap(198 std::declval<adl::MaybeNoexceptIterator<false>>(),199 std::declval<adl::MaybeNoexceptIterator<false>>()));200 ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<ThrowingIter>(), std::declval<ThrowingIter>()));201 }202 203 {204 using NoexceptSplitView = std::ranges::lazy_split_view<adl::View<true>, adl::View<true>>;205 using NoexceptValueType = std::ranges::iterator_t<NoexceptSplitView>::value_type;206 using NoexceptIter = std::ranges::iterator_t<NoexceptValueType>;207 ASSERT_NOEXCEPT(std::ranges::iter_swap(208 std::declval<adl::MaybeNoexceptIterator<true>>(),209 std::declval<adl::MaybeNoexceptIterator<true>>()));210 ASSERT_NOEXCEPT(iter_swap(std::declval<NoexceptIter>(), std::declval<NoexceptIter>()));211 }212 }213 214 return true;215}216 217int main(int, char**) {218 test();219 static_assert(test());220 221 return 0;222}223