//===----------------------------------------------------------------------===// // // 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 SUPPORT_TEST_ITERATORS_H #define SUPPORT_TEST_ITERATORS_H #include #include #include #include #include #include #include #include "test_macros.h" #include "type_algorithms.h" // This iterator meets C++20's Cpp17OutputIterator requirements, as described // in Table 90 ([output.iterators]). template class cpp17_output_iterator { It it_; template friend class cpp17_output_iterator; public: typedef std::output_iterator_tag iterator_category; typedef void value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef It pointer; typedef typename std::iterator_traits::reference reference; TEST_CONSTEXPR explicit cpp17_output_iterator(It it) : it_(std::move(it)) {} template TEST_CONSTEXPR cpp17_output_iterator(const cpp17_output_iterator& u) : it_(u.it_) {} template ::value>::type> TEST_CONSTEXPR_CXX14 cpp17_output_iterator(cpp17_output_iterator&& u) : it_(u.it_) { u.it_ = U(); } TEST_CONSTEXPR reference operator*() const {return *it_;} TEST_CONSTEXPR_CXX14 cpp17_output_iterator& operator++() {++it_; return *this;} TEST_CONSTEXPR_CXX14 cpp17_output_iterator operator++(int) {return cpp17_output_iterator(it_++);} friend TEST_CONSTEXPR It base(const cpp17_output_iterator& i) { return i.it_; } template void operator,(T const &) = delete; }; #if TEST_STD_VER > 14 template cpp17_output_iterator(It) -> cpp17_output_iterator; #endif #if TEST_STD_VER > 17 static_assert(std::output_iterator, int>); #endif // This iterator meets C++20's Cpp17InputIterator requirements, as described // in Table 89 ([input.iterators]). template class cpp17_input_iterator { typedef std::iterator_traits Traits; It it_; template friend class cpp17_input_iterator; public: typedef std::input_iterator_tag iterator_category; typedef typename Traits::value_type value_type; typedef typename Traits::difference_type difference_type; typedef It pointer; typedef typename Traits::reference reference; TEST_CONSTEXPR explicit cpp17_input_iterator(It it) : it_(it) {} template TEST_CONSTEXPR cpp17_input_iterator(const cpp17_input_iterator& u) : it_(u.it_) {} template ::value>::type> TEST_CONSTEXPR_CXX14 cpp17_input_iterator(cpp17_input_iterator&& u) : it_(u.it_) { u.it_ = U(); } TEST_CONSTEXPR reference operator*() const {return *it_;} TEST_CONSTEXPR_CXX14 cpp17_input_iterator& operator++() {++it_; return *this;} TEST_CONSTEXPR_CXX14 cpp17_input_iterator operator++(int) {return cpp17_input_iterator(it_++);} friend TEST_CONSTEXPR bool operator==(const cpp17_input_iterator& x, const cpp17_input_iterator& y) {return x.it_ == y.it_;} friend TEST_CONSTEXPR bool operator!=(const cpp17_input_iterator& x, const cpp17_input_iterator& y) {return x.it_ != y.it_;} friend TEST_CONSTEXPR It base(const cpp17_input_iterator& i) { return i.it_; } template void operator,(T const &) = delete; }; #if TEST_STD_VER > 14 template cpp17_input_iterator(It) -> cpp17_input_iterator; #endif #if TEST_STD_VER > 17 static_assert(std::input_iterator>); #endif template class forward_iterator { It it_; template friend class forward_iterator; public: typedef std::forward_iterator_tag iterator_category; typedef typename std::iterator_traits::value_type value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef It pointer; typedef typename std::iterator_traits::reference reference; TEST_CONSTEXPR forward_iterator() : it_() {} TEST_CONSTEXPR explicit forward_iterator(It it) : it_(it) {} template TEST_CONSTEXPR forward_iterator(const forward_iterator& u) : it_(u.it_) {} template ::value>::type> TEST_CONSTEXPR_CXX14 forward_iterator(forward_iterator&& other) : it_(other.it_) { other.it_ = U(); } TEST_CONSTEXPR reference operator*() const {return *it_;} TEST_CONSTEXPR_CXX14 forward_iterator& operator++() {++it_; return *this;} TEST_CONSTEXPR_CXX14 forward_iterator operator++(int) {return forward_iterator(it_++);} friend TEST_CONSTEXPR bool operator==(const forward_iterator& x, const forward_iterator& y) {return x.it_ == y.it_;} friend TEST_CONSTEXPR bool operator!=(const forward_iterator& x, const forward_iterator& y) {return x.it_ != y.it_;} friend TEST_CONSTEXPR It base(const forward_iterator& i) { return i.it_; } template void operator,(T const &) = delete; }; #if TEST_STD_VER > 14 template forward_iterator(It) -> forward_iterator; #endif template class bidirectional_iterator { It it_; template friend class bidirectional_iterator; public: typedef std::bidirectional_iterator_tag iterator_category; typedef typename std::iterator_traits::value_type value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef It pointer; typedef typename std::iterator_traits::reference reference; TEST_CONSTEXPR bidirectional_iterator() : it_() {} TEST_CONSTEXPR explicit bidirectional_iterator(It it) : it_(it) {} template TEST_CONSTEXPR bidirectional_iterator(const bidirectional_iterator& u) : it_(u.it_) {} template ::value>::type> TEST_CONSTEXPR_CXX14 bidirectional_iterator(bidirectional_iterator&& u) : it_(u.it_) { u.it_ = U(); } TEST_CONSTEXPR reference operator*() const {return *it_;} TEST_CONSTEXPR_CXX14 bidirectional_iterator& operator++() {++it_; return *this;} TEST_CONSTEXPR_CXX14 bidirectional_iterator& operator--() {--it_; return *this;} TEST_CONSTEXPR_CXX14 bidirectional_iterator operator++(int) {return bidirectional_iterator(it_++);} TEST_CONSTEXPR_CXX14 bidirectional_iterator operator--(int) {return bidirectional_iterator(it_--);} friend TEST_CONSTEXPR bool operator==(const bidirectional_iterator& x, const bidirectional_iterator& y) {return x.it_ == y.it_;} friend TEST_CONSTEXPR bool operator!=(const bidirectional_iterator& x, const bidirectional_iterator& y) {return x.it_ != y.it_;} friend TEST_CONSTEXPR It base(const bidirectional_iterator& i) { return i.it_; } template void operator,(T const &) = delete; }; #if TEST_STD_VER > 14 template bidirectional_iterator(It) -> bidirectional_iterator; #endif template class random_access_iterator { It it_; template friend class random_access_iterator; public: typedef std::random_access_iterator_tag iterator_category; typedef typename std::iterator_traits::value_type value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef It pointer; typedef typename std::iterator_traits::reference reference; TEST_CONSTEXPR random_access_iterator() : it_() {} TEST_CONSTEXPR explicit random_access_iterator(It it) : it_(it) {} template TEST_CONSTEXPR random_access_iterator(const random_access_iterator& u) : it_(u.it_) {} template ::value>::type> TEST_CONSTEXPR_CXX14 random_access_iterator(random_access_iterator&& u) : it_(u.it_) { u.it_ = U(); } TEST_CONSTEXPR_CXX14 reference operator*() const {return *it_;} TEST_CONSTEXPR_CXX14 reference operator[](difference_type n) const {return it_[n];} TEST_CONSTEXPR_CXX14 random_access_iterator& operator++() {++it_; return *this;} TEST_CONSTEXPR_CXX14 random_access_iterator& operator--() {--it_; return *this;} TEST_CONSTEXPR_CXX14 random_access_iterator operator++(int) {return random_access_iterator(it_++);} TEST_CONSTEXPR_CXX14 random_access_iterator operator--(int) {return random_access_iterator(it_--);} TEST_CONSTEXPR_CXX14 random_access_iterator& operator+=(difference_type n) {it_ += n; return *this;} TEST_CONSTEXPR_CXX14 random_access_iterator& operator-=(difference_type n) {it_ -= n; return *this;} friend TEST_CONSTEXPR_CXX14 random_access_iterator operator+(random_access_iterator x, difference_type n) {x += n; return x;} friend TEST_CONSTEXPR_CXX14 random_access_iterator operator+(difference_type n, random_access_iterator x) {x += n; return x;} friend TEST_CONSTEXPR_CXX14 random_access_iterator operator-(random_access_iterator x, difference_type n) {x -= n; return x;} friend TEST_CONSTEXPR difference_type operator-(random_access_iterator x, random_access_iterator y) {return x.it_ - y.it_;} friend TEST_CONSTEXPR bool operator==(const random_access_iterator& x, const random_access_iterator& y) {return x.it_ == y.it_;} friend TEST_CONSTEXPR bool operator!=(const random_access_iterator& x, const random_access_iterator& y) {return x.it_ != y.it_;} friend TEST_CONSTEXPR bool operator< (const random_access_iterator& x, const random_access_iterator& y) {return x.it_ < y.it_;} friend TEST_CONSTEXPR bool operator<=(const random_access_iterator& x, const random_access_iterator& y) {return x.it_ <= y.it_;} friend TEST_CONSTEXPR bool operator> (const random_access_iterator& x, const random_access_iterator& y) {return x.it_ > y.it_;} friend TEST_CONSTEXPR bool operator>=(const random_access_iterator& x, const random_access_iterator& y) {return x.it_ >= y.it_;} friend TEST_CONSTEXPR It base(const random_access_iterator& i) { return i.it_; } template void operator,(T const &) = delete; }; #if TEST_STD_VER > 14 template random_access_iterator(It) -> random_access_iterator; #endif #if TEST_STD_VER > 17 template class cpp20_random_access_iterator { It it_; template friend class cpp20_random_access_iterator; public: using iterator_category = std::input_iterator_tag; using iterator_concept = std::random_access_iterator_tag; using value_type = typename std::iterator_traits::value_type; using difference_type = typename std::iterator_traits::difference_type; constexpr cpp20_random_access_iterator() : it_() {} constexpr explicit cpp20_random_access_iterator(It it) : it_(it) {} template constexpr cpp20_random_access_iterator(const cpp20_random_access_iterator& u) : it_(u.it_) {} template constexpr cpp20_random_access_iterator(cpp20_random_access_iterator&& u) : it_(u.it_) { u.it_ = U(); } constexpr decltype(auto) operator*() const { return *it_; } constexpr decltype(auto) operator[](difference_type n) const { return it_[n]; } constexpr cpp20_random_access_iterator& operator++() { ++it_; return *this; } constexpr cpp20_random_access_iterator& operator--() { --it_; return *this; } constexpr cpp20_random_access_iterator operator++(int) { return cpp20_random_access_iterator(it_++); } constexpr cpp20_random_access_iterator operator--(int) { return cpp20_random_access_iterator(it_--); } constexpr cpp20_random_access_iterator& operator+=(difference_type n) { it_ += n; return *this; } constexpr cpp20_random_access_iterator& operator-=(difference_type n) { it_ -= n; return *this; } friend constexpr cpp20_random_access_iterator operator+(cpp20_random_access_iterator x, difference_type n) { x += n; return x; } friend constexpr cpp20_random_access_iterator operator+(difference_type n, cpp20_random_access_iterator x) { x += n; return x; } friend constexpr cpp20_random_access_iterator operator-(cpp20_random_access_iterator x, difference_type n) { x -= n; return x; } friend constexpr difference_type operator-(cpp20_random_access_iterator x, cpp20_random_access_iterator y) { return x.it_ - y.it_; } friend constexpr bool operator==(const cpp20_random_access_iterator& x, const cpp20_random_access_iterator& y) { return x.it_ == y.it_; } friend constexpr bool operator!=(const cpp20_random_access_iterator& x, const cpp20_random_access_iterator& y) { return x.it_ != y.it_; } friend constexpr bool operator<(const cpp20_random_access_iterator& x, const cpp20_random_access_iterator& y) { return x.it_ < y.it_; } friend constexpr bool operator<=(const cpp20_random_access_iterator& x, const cpp20_random_access_iterator& y) { return x.it_ <= y.it_; } friend constexpr bool operator>(const cpp20_random_access_iterator& x, const cpp20_random_access_iterator& y) { return x.it_ > y.it_; } friend constexpr bool operator>=(const cpp20_random_access_iterator& x, const cpp20_random_access_iterator& y) { return x.it_ >= y.it_; } friend constexpr It base(const cpp20_random_access_iterator& i) { return i.it_; } template void operator,(T const&) = delete; }; template cpp20_random_access_iterator(It) -> cpp20_random_access_iterator; static_assert(std::random_access_iterator>); template class contiguous_iterator { static_assert(std::is_pointer_v, "Things probably break in this case"); It it_; template friend class contiguous_iterator; public: typedef std::contiguous_iterator_tag iterator_category; typedef typename std::iterator_traits::value_type value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef It pointer; typedef typename std::iterator_traits::reference reference; typedef typename std::remove_pointer::type element_type; TEST_CONSTEXPR_CXX14 It base() const {return it_;} TEST_CONSTEXPR_CXX14 contiguous_iterator() : it_() {} TEST_CONSTEXPR_CXX14 explicit contiguous_iterator(It it) : it_(it) {} template TEST_CONSTEXPR_CXX14 contiguous_iterator(const contiguous_iterator& u) : it_(u.it_) {} template ::value>::type> constexpr contiguous_iterator(contiguous_iterator&& u) : it_(u.it_) { u.it_ = U(); } TEST_CONSTEXPR reference operator*() const {return *it_;} TEST_CONSTEXPR pointer operator->() const {return it_;} TEST_CONSTEXPR reference operator[](difference_type n) const {return it_[n];} TEST_CONSTEXPR_CXX14 contiguous_iterator& operator++() {++it_; return *this;} TEST_CONSTEXPR_CXX14 contiguous_iterator& operator--() {--it_; return *this;} TEST_CONSTEXPR_CXX14 contiguous_iterator operator++(int) {return contiguous_iterator(it_++);} TEST_CONSTEXPR_CXX14 contiguous_iterator operator--(int) {return contiguous_iterator(it_--);} TEST_CONSTEXPR_CXX14 contiguous_iterator& operator+=(difference_type n) {it_ += n; return *this;} TEST_CONSTEXPR_CXX14 contiguous_iterator& operator-=(difference_type n) {it_ -= n; return *this;} friend TEST_CONSTEXPR_CXX14 contiguous_iterator operator+(contiguous_iterator x, difference_type n) {x += n; return x;} friend TEST_CONSTEXPR_CXX14 contiguous_iterator operator+(difference_type n, contiguous_iterator x) {x += n; return x;} friend TEST_CONSTEXPR_CXX14 contiguous_iterator operator-(contiguous_iterator x, difference_type n) {x -= n; return x;} friend TEST_CONSTEXPR difference_type operator-(contiguous_iterator x, contiguous_iterator y) {return x.it_ - y.it_;} friend TEST_CONSTEXPR bool operator==(const contiguous_iterator& x, const contiguous_iterator& y) {return x.it_ == y.it_;} friend TEST_CONSTEXPR bool operator!=(const contiguous_iterator& x, const contiguous_iterator& y) {return x.it_ != y.it_;} friend TEST_CONSTEXPR bool operator< (const contiguous_iterator& x, const contiguous_iterator& y) {return x.it_ < y.it_;} friend TEST_CONSTEXPR bool operator<=(const contiguous_iterator& x, const contiguous_iterator& y) {return x.it_ <= y.it_;} friend TEST_CONSTEXPR bool operator> (const contiguous_iterator& x, const contiguous_iterator& y) {return x.it_ > y.it_;} friend TEST_CONSTEXPR bool operator>=(const contiguous_iterator& x, const contiguous_iterator& y) {return x.it_ >= y.it_;} friend TEST_CONSTEXPR It base(const contiguous_iterator& i) { return i.it_; } template void operator,(T const &) = delete; }; template contiguous_iterator(It) -> contiguous_iterator; template class three_way_contiguous_iterator { static_assert(std::is_pointer_v, "Things probably break in this case"); It it_; template friend class three_way_contiguous_iterator; public: typedef std::contiguous_iterator_tag iterator_category; typedef typename std::iterator_traits::value_type value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef It pointer; typedef typename std::iterator_traits::reference reference; typedef typename std::remove_pointer::type element_type; constexpr It base() const {return it_;} constexpr three_way_contiguous_iterator() : it_() {} constexpr explicit three_way_contiguous_iterator(It it) : it_(it) {} template constexpr three_way_contiguous_iterator(const three_way_contiguous_iterator& u) : it_(u.it_) {} template ::value>::type> constexpr three_way_contiguous_iterator(three_way_contiguous_iterator&& u) : it_(u.it_) { u.it_ = U(); } constexpr reference operator*() const {return *it_;} constexpr pointer operator->() const {return it_;} constexpr reference operator[](difference_type n) const {return it_[n];} constexpr three_way_contiguous_iterator& operator++() {++it_; return *this;} constexpr three_way_contiguous_iterator& operator--() {--it_; return *this;} constexpr three_way_contiguous_iterator operator++(int) {return three_way_contiguous_iterator(it_++);} constexpr three_way_contiguous_iterator operator--(int) {return three_way_contiguous_iterator(it_--);} constexpr three_way_contiguous_iterator& operator+=(difference_type n) {it_ += n; return *this;} constexpr three_way_contiguous_iterator& operator-=(difference_type n) {it_ -= n; return *this;} friend constexpr three_way_contiguous_iterator operator+(three_way_contiguous_iterator x, difference_type n) {x += n; return x;} friend constexpr three_way_contiguous_iterator operator+(difference_type n, three_way_contiguous_iterator x) {x += n; return x;} friend constexpr three_way_contiguous_iterator operator-(three_way_contiguous_iterator x, difference_type n) {x -= n; return x;} friend constexpr difference_type operator-(three_way_contiguous_iterator x, three_way_contiguous_iterator y) {return x.it_ - y.it_;} friend constexpr auto operator<=>(const three_way_contiguous_iterator& x, const three_way_contiguous_iterator& y) {return x.it_ <=> y.it_;} friend constexpr bool operator==(const three_way_contiguous_iterator& x, const three_way_contiguous_iterator& y) {return x.it_ == y.it_;} template void operator,(T const &) = delete; }; template three_way_contiguous_iterator(It) -> three_way_contiguous_iterator; #endif // TEST_STD_VER > 17 template // ADL base() for everything else (including pointers) TEST_CONSTEXPR Iter base(Iter i) { return i; } template struct ThrowingIterator { typedef std::bidirectional_iterator_tag iterator_category; typedef std::ptrdiff_t difference_type; typedef const T value_type; typedef const T * pointer; typedef const T & reference; enum ThrowingAction { TAIncrement, TADecrement, TADereference, TAAssignment, TAComparison }; TEST_CONSTEXPR ThrowingIterator() : begin_(nullptr), end_(nullptr), current_(nullptr), action_(TADereference), index_(0) {} TEST_CONSTEXPR explicit ThrowingIterator(const T* first, const T* last, int index = 0, ThrowingAction action = TADereference) : begin_(first), end_(last), current_(first), action_(action), index_(index) {} TEST_CONSTEXPR ThrowingIterator(const ThrowingIterator &rhs) : begin_(rhs.begin_), end_(rhs.end_), current_(rhs.current_), action_(rhs.action_), index_(rhs.index_) {} TEST_CONSTEXPR_CXX14 ThrowingIterator& operator=(const ThrowingIterator& rhs) { if (action_ == TAAssignment && --index_ < 0) { #ifndef TEST_HAS_NO_EXCEPTIONS throw std::runtime_error("throw from iterator assignment"); #else assert(false); #endif } begin_ = rhs.begin_; end_ = rhs.end_; current_ = rhs.current_; action_ = rhs.action_; index_ = rhs.index_; return *this; } TEST_CONSTEXPR_CXX14 reference operator*() const { if (action_ == TADereference && --index_ < 0) { #ifndef TEST_HAS_NO_EXCEPTIONS throw std::runtime_error("throw from iterator dereference"); #else assert(false); #endif } return *current_; } TEST_CONSTEXPR_CXX14 ThrowingIterator& operator++() { if (action_ == TAIncrement && --index_ < 0) { #ifndef TEST_HAS_NO_EXCEPTIONS throw std::runtime_error("throw from iterator increment"); #else assert(false); #endif } ++current_; return *this; } TEST_CONSTEXPR_CXX14 ThrowingIterator operator++(int) { ThrowingIterator temp = *this; ++(*this); return temp; } TEST_CONSTEXPR_CXX14 ThrowingIterator& operator--() { if (action_ == TADecrement && --index_ < 0) { #ifndef TEST_HAS_NO_EXCEPTIONS throw std::runtime_error("throw from iterator decrement"); #else assert(false); #endif } --current_; return *this; } TEST_CONSTEXPR_CXX14 ThrowingIterator operator--(int) { ThrowingIterator temp = *this; --(*this); return temp; } TEST_CONSTEXPR_CXX14 friend bool operator==(const ThrowingIterator& a, const ThrowingIterator& b) { if (a.action_ == TAComparison && --a.index_ < 0) { #ifndef TEST_HAS_NO_EXCEPTIONS throw std::runtime_error("throw from iterator comparison"); #else assert(false); #endif } bool atEndL = a.current_ == a.end_; bool atEndR = b.current_ == b.end_; if (atEndL != atEndR) return false; // one is at the end (or empty), the other is not. if (atEndL) return true; // both are at the end (or empty) return a.current_ == b.current_; } TEST_CONSTEXPR friend bool operator!=(const ThrowingIterator& a, const ThrowingIterator& b) { return !(a == b); } template void operator,(T2 const &) = delete; private: const T* begin_; const T* end_; const T* current_; ThrowingAction action_; mutable int index_; }; template struct NonThrowingIterator { typedef std::bidirectional_iterator_tag iterator_category; typedef std::ptrdiff_t difference_type; typedef const T value_type; typedef const T * pointer; typedef const T & reference; NonThrowingIterator() : begin_(nullptr), end_(nullptr), current_(nullptr) {} explicit NonThrowingIterator(const T *first, const T *last) : begin_(first), end_(last), current_(first) {} NonThrowingIterator(const NonThrowingIterator& rhs) : begin_(rhs.begin_), end_(rhs.end_), current_(rhs.current_) {} NonThrowingIterator& operator=(const NonThrowingIterator& rhs) TEST_NOEXCEPT { begin_ = rhs.begin_; end_ = rhs.end_; current_ = rhs.current_; return *this; } reference operator*() const TEST_NOEXCEPT { return *current_; } NonThrowingIterator& operator++() TEST_NOEXCEPT { ++current_; return *this; } NonThrowingIterator operator++(int) TEST_NOEXCEPT { NonThrowingIterator temp = *this; ++(*this); return temp; } NonThrowingIterator & operator--() TEST_NOEXCEPT { --current_; return *this; } NonThrowingIterator operator--(int) TEST_NOEXCEPT { NonThrowingIterator temp = *this; --(*this); return temp; } friend bool operator==(const NonThrowingIterator& a, const NonThrowingIterator& b) TEST_NOEXCEPT { bool atEndL = a.current_ == a.end_; bool atEndR = b.current_ == b.end_; if (atEndL != atEndR) return false; // one is at the end (or empty), the other is not. if (atEndL) return true; // both are at the end (or empty) return a.current_ == b.current_; } friend bool operator!=(const NonThrowingIterator& a, const NonThrowingIterator& b) TEST_NOEXCEPT { return !(a == b); } template void operator,(T2 const &) = delete; private: const T *begin_; const T *end_; const T *current_; }; #if TEST_STD_VER > 17 template class cpp20_input_iterator { It it_; public: using value_type = std::iter_value_t; using difference_type = std::iter_difference_t; using iterator_concept = std::input_iterator_tag; constexpr explicit cpp20_input_iterator(It it) : it_(it) {} cpp20_input_iterator(cpp20_input_iterator&&) = default; cpp20_input_iterator& operator=(cpp20_input_iterator&&) = default; constexpr decltype(auto) operator*() const { return *it_; } constexpr cpp20_input_iterator& operator++() { ++it_; return *this; } constexpr void operator++(int) { ++it_; } friend constexpr It base(const cpp20_input_iterator& i) { return i.it_; } template void operator,(T const &) = delete; }; template cpp20_input_iterator(It) -> cpp20_input_iterator; static_assert(std::input_iterator>); template struct iter_value_or_void { using type = void; }; template struct iter_value_or_void { using type = std::iter_value_t; }; template class cpp20_output_iterator { It it_; public: using difference_type = std::iter_difference_t; constexpr explicit cpp20_output_iterator(It it) : it_(it) {} cpp20_output_iterator(cpp20_output_iterator&&) = default; cpp20_output_iterator& operator=(cpp20_output_iterator&&) = default; constexpr decltype(auto) operator*() const { return *it_; } constexpr cpp20_output_iterator& operator++() { ++it_; return *this; } constexpr cpp20_output_iterator operator++(int) { return cpp20_output_iterator(it_++); } friend constexpr It base(const cpp20_output_iterator& i) { return i.it_; } template void operator,(T const&) = delete; }; template cpp20_output_iterator(It) -> cpp20_output_iterator; static_assert(std::output_iterator, int>); # if TEST_STD_VER >= 20 // An `input_iterator` that can be used in a `std::ranges::common_range` template struct common_input_iterator { Base it_; using value_type = std::iter_value_t; using difference_type = std::intptr_t; using iterator_concept = std::input_iterator_tag; constexpr common_input_iterator() = default; constexpr explicit common_input_iterator(Base it) : it_(it) {} constexpr common_input_iterator& operator++() { ++it_; return *this; } constexpr void operator++(int) { ++it_; } constexpr decltype(auto) operator*() const { return *it_; } friend constexpr bool operator==(common_input_iterator const&, common_input_iterator const&) = default; }; # endif // TEST_STD_VER >= 20 // Iterator adaptor that counts the number of times the iterator has had a successor/predecessor // operation called. Has two recorders: // * `stride_count`, which records the total number of calls to an op++, op--, op+=, or op-=. // * `stride_displacement`, which records the displacement of the calls. This means that both // op++/op+= will increase the displacement counter by 1, and op--/op-= will decrease the // displacement counter by 1. template class stride_counting_iterator { public: using value_type = typename iter_value_or_void::type; using difference_type = std::iter_difference_t; using iterator_concept = std::conditional_t, std::contiguous_iterator_tag, std::conditional_t, std::random_access_iterator_tag, std::conditional_t, std::bidirectional_iterator_tag, std::conditional_t, std::forward_iterator_tag, std::conditional_t, std::input_iterator_tag, /* else */ std::output_iterator_tag >>>>>; stride_counting_iterator() requires std::default_initializable = default; constexpr explicit stride_counting_iterator(It const& it) : base_(base(it)) { } friend constexpr It base(stride_counting_iterator const& it) { return It(it.base_); } constexpr difference_type stride_count() const { return stride_count_; } constexpr difference_type stride_displacement() const { return stride_displacement_; } constexpr decltype(auto) operator*() const { return *It(base_); } constexpr decltype(auto) operator[](difference_type n) const { return It(base_)[n]; } constexpr stride_counting_iterator& operator++() { It tmp(base_); base_ = base(++tmp); ++stride_count_; ++stride_displacement_; return *this; } constexpr void operator++(int) { ++*this; } constexpr stride_counting_iterator operator++(int) requires std::forward_iterator { auto temp = *this; ++*this; return temp; } constexpr stride_counting_iterator& operator--() requires std::bidirectional_iterator { It tmp(base_); base_ = base(--tmp); ++stride_count_; --stride_displacement_; return *this; } constexpr stride_counting_iterator operator--(int) requires std::bidirectional_iterator { auto temp = *this; --*this; return temp; } constexpr stride_counting_iterator& operator+=(difference_type const n) requires std::random_access_iterator { It tmp(base_); base_ = base(tmp += n); ++stride_count_; ++stride_displacement_; return *this; } constexpr stride_counting_iterator& operator-=(difference_type const n) requires std::random_access_iterator { It tmp(base_); base_ = base(tmp -= n); ++stride_count_; --stride_displacement_; return *this; } friend constexpr stride_counting_iterator operator+(stride_counting_iterator it, difference_type n) requires std::random_access_iterator { return it += n; } friend constexpr stride_counting_iterator operator+(difference_type n, stride_counting_iterator it) requires std::random_access_iterator { return it += n; } friend constexpr stride_counting_iterator operator-(stride_counting_iterator it, difference_type n) requires std::random_access_iterator { return it -= n; } friend constexpr difference_type operator-(stride_counting_iterator const& x, stride_counting_iterator const& y) requires std::sized_sentinel_for { return base(x) - base(y); } constexpr bool operator==(stride_counting_iterator const& other) const requires std::sentinel_for { return It(base_) == It(other.base_); } friend constexpr bool operator<(stride_counting_iterator const& x, stride_counting_iterator const& y) requires std::random_access_iterator { return It(x.base_) < It(y.base_); } friend constexpr bool operator>(stride_counting_iterator const& x, stride_counting_iterator const& y) requires std::random_access_iterator { return It(x.base_) > It(y.base_); } friend constexpr bool operator<=(stride_counting_iterator const& x, stride_counting_iterator const& y) requires std::random_access_iterator { return It(x.base_) <= It(y.base_); } friend constexpr bool operator>=(stride_counting_iterator const& x, stride_counting_iterator const& y) requires std::random_access_iterator { return It(x.base_) >= It(y.base_); } template void operator,(T const &) = delete; private: decltype(base(std::declval())) base_; difference_type stride_count_ = 0; difference_type stride_displacement_ = 0; }; template stride_counting_iterator(It) -> stride_counting_iterator; #endif // TEST_STD_VER > 17 #if TEST_STD_VER > 17 template class sentinel_wrapper { public: explicit sentinel_wrapper() = default; constexpr explicit sentinel_wrapper(const It& it) : base_(base(it)) {} constexpr bool operator==(const It& other) const { return base_ == base(other); } friend constexpr It base(const sentinel_wrapper& s) { return It(s.base_); } private: decltype(base(std::declval())) base_; }; template sentinel_wrapper(It) -> sentinel_wrapper; template class sized_sentinel { public: explicit sized_sentinel() = default; constexpr explicit sized_sentinel(const It& it) : base_(base(it)) {} constexpr bool operator==(const It& other) const { return base_ == base(other); } friend constexpr auto operator-(const sized_sentinel& s, const It& i) { return s.base_ - base(i); } friend constexpr auto operator-(const It& i, const sized_sentinel& s) { return base(i) - s.base_; } friend constexpr It base(const sized_sentinel& s) { return It(s.base_); } private: decltype(base(std::declval())) base_; }; template sized_sentinel(It) -> sized_sentinel; namespace adl { class Iterator { public: using value_type = int; using reference = int&; using difference_type = std::ptrdiff_t; private: value_type* ptr_ = nullptr; int* iter_moves_ = nullptr; int* iter_swaps_ = nullptr; constexpr Iterator(int* p, int* iter_moves, int* iter_swaps) : ptr_(p) , iter_moves_(iter_moves) , iter_swaps_(iter_swaps) {} public: constexpr Iterator() = default; static constexpr Iterator TrackMoves(int* p, int& iter_moves) { return Iterator(p, &iter_moves, /*iter_swaps=*/nullptr); } static constexpr Iterator TrackSwaps(int& iter_swaps) { return Iterator(/*p=*/nullptr, /*iter_moves=*/nullptr, &iter_swaps); } static constexpr Iterator TrackSwaps(int* p, int& iter_swaps) { return Iterator(p, /*iter_moves=*/nullptr, &iter_swaps); } constexpr int iter_moves() const { assert(iter_moves_); return *iter_moves_; } constexpr int iter_swaps() const { assert(iter_swaps_); return *iter_swaps_; } constexpr value_type& operator*() const { return *ptr_; } constexpr reference operator[](difference_type n) const { return ptr_[n]; } friend constexpr Iterator operator+(Iterator i, difference_type n) { return Iterator(i.ptr_ + n, i.iter_moves_, i.iter_swaps_); } friend constexpr Iterator operator+(difference_type n, Iterator i) { return i + n; } constexpr Iterator operator-(difference_type n) const { return Iterator(ptr_ - n, iter_moves_, iter_swaps_); } constexpr difference_type operator-(Iterator rhs) const { return ptr_ - rhs.ptr_; } constexpr Iterator& operator+=(difference_type n) { ptr_ += n; return *this; } constexpr Iterator& operator-=(difference_type n) { ptr_ -= n; return *this; } constexpr Iterator& operator++() { ++ptr_; return *this; } constexpr Iterator operator++(int) { Iterator prev = *this; ++ptr_; return prev; } constexpr Iterator& operator--() { --ptr_; return *this; } constexpr Iterator operator--(int) { Iterator prev = *this; --ptr_; return prev; } constexpr friend void iter_swap(Iterator a, Iterator b) { std::swap(a.ptr_, b.ptr_); if (a.iter_swaps_) { ++(*a.iter_swaps_); } } constexpr friend value_type&& iter_move(Iterator iter) { if (iter.iter_moves_) { ++(*iter.iter_moves_); } return std::move(*iter); } constexpr friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } constexpr friend auto operator<=>(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ <=> rhs.ptr_; } }; } // namespace adl template class rvalue_iterator { public: using iterator_category = std::input_iterator_tag; using iterator_concept = std::random_access_iterator_tag; using difference_type = std::ptrdiff_t; using reference = T&&; using value_type = T; rvalue_iterator() = default; constexpr rvalue_iterator(T* it) : it_(it) {} constexpr reference operator*() const { return std::move(*it_); } constexpr rvalue_iterator& operator++() { ++it_; return *this; } constexpr rvalue_iterator operator++(int) { auto tmp = *this; ++it_; return tmp; } constexpr rvalue_iterator& operator--() { --it_; return *this; } constexpr rvalue_iterator operator--(int) { auto tmp = *this; --it_; return tmp; } constexpr rvalue_iterator operator+(difference_type n) const { auto tmp = *this; tmp.it += n; return tmp; } constexpr friend rvalue_iterator operator+(difference_type n, rvalue_iterator iter) { iter += n; return iter; } constexpr rvalue_iterator operator-(difference_type n) const { auto tmp = *this; tmp.it -= n; return tmp; } constexpr difference_type operator-(const rvalue_iterator& other) const { return it_ - other.it_; } constexpr rvalue_iterator& operator+=(difference_type n) { it_ += n; return *this; } constexpr rvalue_iterator& operator-=(difference_type n) { it_ -= n; return *this; } constexpr reference operator[](difference_type n) const { return std::move(it_[n]); } auto operator<=>(const rvalue_iterator&) const noexcept = default; private: T* it_; }; template rvalue_iterator(T*) -> rvalue_iterator; static_assert(std::random_access_iterator>); // Proxy // ====================================================================== // Proxy that can wrap a value or a reference. It simulates C++23's tuple // but simplified to just hold one argument. // Note that unlike tuple, this class deliberately doesn't have special handling // of swap to cause a compilation error if it's used in an algorithm that relies // on plain swap instead of ranges::iter_swap. // This class is useful for testing that if algorithms support proxy iterator // properly, i.e. calling ranges::iter_swap and ranges::iter_move instead of // plain swap and std::move. template struct Proxy; template inline constexpr bool IsProxy = false; template inline constexpr bool IsProxy> = true; template struct Proxy { T data; constexpr T& getData() & { return data; } constexpr const T& getData() const& { return data; } constexpr T&& getData() && { return static_cast(data); } constexpr const T&& getData() const&& { return static_cast(data); } template requires std::constructible_from constexpr Proxy(U&& u) : data{std::forward(u)} {} // This constructor covers conversion from cvref of Proxy, including non-const/const versions of copy/move constructor template requires(IsProxy> && std::constructible_from().getData())>) constexpr Proxy(Other&& other) : data{std::forward(other).getData()} {} template requires(IsProxy> && std::assignable_from().getData())>) constexpr Proxy& operator=(Other&& other) { data = std::forward(other).getData(); return *this; } // const assignment required to make ProxyIterator model std::indirectly_writable template requires(IsProxy> && std::assignable_from().getData())>) constexpr const Proxy& operator=(Other&& other) const { data = std::forward(other).getData(); return *this; } // If `T` is a reference type, the implicitly-generated assignment operator will be deleted (and would take precedence // over the templated `operator=` above because it's a better match). constexpr Proxy& operator=(const Proxy& rhs) { data = rhs.data; return *this; } // no specialised swap function that takes const Proxy& and no specialised const member swap // Calling swap(Proxy{}, Proxy{}) would fail (pass prvalues) // Compare operators are defined for the convenience of the tests friend constexpr bool operator==(const Proxy&, const Proxy&) requires (std::equality_comparable && !std::is_reference_v) = default; // Helps compare e.g. `Proxy` and `Proxy`. Note that the default equality comparison operator is deleted // when `T` is a reference type. template friend constexpr bool operator==(const Proxy& lhs, const Proxy& rhs) requires std::equality_comparable_with, std::decay_t> { return lhs.data == rhs.data; } friend constexpr auto operator<=>(const Proxy&, const Proxy&) requires (std::three_way_comparable && !std::is_reference_v) = default; // Helps compare e.g. `Proxy` and `Proxy`. Note that the default 3-way comparison operator is deleted when // `T` is a reference type. template friend constexpr auto operator<=>(const Proxy& lhs, const Proxy& rhs) requires std::three_way_comparable_with, std::decay_t> { return lhs.data <=> rhs.data; } }; // This is to make ProxyIterator model `std::indirectly_readable` template class TQual, template class UQual> requires requires { typename std::common_reference_t, UQual>; } struct std::basic_common_reference, Proxy, TQual, UQual> { using type = Proxy, UQual>>; }; template requires requires { typename std::common_type_t; } struct std::common_type, Proxy> { using type = Proxy>; }; // ProxyIterator // ====================================================================== // It wraps `Base` iterator and when dereferenced it returns a Proxy // It simulates C++23's zip_view::iterator but simplified to just wrap // one base iterator. // Note it forwards value_type, iter_move, iter_swap. e.g if the base // iterator is int*, // operator* -> Proxy // iter_value_t -> Proxy // iter_move -> Proxy template struct ProxyIteratorBase {}; template requires std::derived_from< typename std::iterator_traits::iterator_category, std::input_iterator_tag> struct ProxyIteratorBase { using iterator_category = std::input_iterator_tag; }; template consteval auto get_iterator_concept() { if constexpr (std::random_access_iterator) { return std::random_access_iterator_tag{}; } else if constexpr (std::bidirectional_iterator) { return std::bidirectional_iterator_tag{}; } else if constexpr (std::forward_iterator) { return std::forward_iterator_tag{}; } else { return std::input_iterator_tag{}; } } template struct ProxyIterator : ProxyIteratorBase { Base base_; using iterator_concept = decltype(get_iterator_concept()); using value_type = Proxy>; using difference_type = std::iter_difference_t; ProxyIterator() requires std::default_initializable = default; constexpr ProxyIterator(Base base) : base_{std::move(base)} {} template requires std::constructible_from constexpr ProxyIterator(T&& t) : base_{std::forward(t)} {} friend constexpr decltype(auto) base(const ProxyIterator& p) { return base(p.base_); } // Specialization of iter_move // If operator* returns Proxy, iter_move will return Proxy // Note std::move(*it) returns Proxy&&, which is not what we want as // it will likely result in a copy rather than a move friend constexpr Proxy> iter_move(const ProxyIterator& p) noexcept { return {std::ranges::iter_move(p.base_)}; } // Specialization of iter_swap // Note std::swap(*x, *y) would fail to compile as operator* returns prvalues // and std::swap takes non-const lvalue references friend constexpr void iter_swap(const ProxyIterator& x, const ProxyIterator& y) noexcept { std::ranges::iter_swap(x.base_, y.base_); } // to satisfy input_iterator constexpr Proxy> operator*() const { return {*base_}; } constexpr ProxyIterator& operator++() { ++base_; return *this; } constexpr void operator++(int) { ++*this; } friend constexpr bool operator==(const ProxyIterator& x, const ProxyIterator& y) requires std::equality_comparable { return x.base_ == y.base_; } // to satisfy forward_iterator constexpr ProxyIterator operator++(int) requires std::forward_iterator { auto tmp = *this; ++*this; return tmp; } // to satisfy bidirectional_iterator constexpr ProxyIterator& operator--() requires std::bidirectional_iterator { --base_; return *this; } constexpr ProxyIterator operator--(int) requires std::bidirectional_iterator { auto tmp = *this; --*this; return tmp; } // to satisfy random_access_iterator constexpr ProxyIterator& operator+=(difference_type n) requires std::random_access_iterator { base_ += n; return *this; } constexpr ProxyIterator& operator-=(difference_type n) requires std::random_access_iterator { base_ -= n; return *this; } constexpr Proxy> operator[](difference_type n) const requires std::random_access_iterator { return {base_[n]}; } friend constexpr bool operator<(const ProxyIterator& x, const ProxyIterator& y) requires std::random_access_iterator { return x.base_ < y.base_; } friend constexpr bool operator>(const ProxyIterator& x, const ProxyIterator& y) requires std::random_access_iterator { return x.base_ > y.base_; } friend constexpr bool operator<=(const ProxyIterator& x, const ProxyIterator& y) requires std::random_access_iterator { return x.base_ <= y.base_; } friend constexpr bool operator>=(const ProxyIterator& x, const ProxyIterator& y) requires std::random_access_iterator { return x.base_ >= y.base_; } friend constexpr auto operator<=>(const ProxyIterator& x, const ProxyIterator& y) requires(std::random_access_iterator && std::three_way_comparable) { return x.base_ <=> y.base_; } friend constexpr ProxyIterator operator+(const ProxyIterator& x, difference_type n) requires std::random_access_iterator { return ProxyIterator{x.base_ + n}; } friend constexpr ProxyIterator operator+(difference_type n, const ProxyIterator& x) requires std::random_access_iterator { return ProxyIterator{n + x.base_}; } friend constexpr ProxyIterator operator-(const ProxyIterator& x, difference_type n) requires std::random_access_iterator { return ProxyIterator{x.base_ - n}; } friend constexpr difference_type operator-(const ProxyIterator& x, const ProxyIterator& y) requires std::random_access_iterator { return x.base_ - y.base_; } }; template ProxyIterator(Base) -> ProxyIterator; static_assert(std::indirectly_readable>); static_assert(std::indirectly_writable, Proxy>); static_assert(std::indirectly_writable, Proxy>); template using Cpp20InputProxyIterator = ProxyIterator>; template using ForwardProxyIterator = ProxyIterator>; template using BidirectionalProxyIterator = ProxyIterator>; template using RandomAccessProxyIterator = ProxyIterator>; template using ContiguousProxyIterator = ProxyIterator>; template struct ProxySentinel { BaseSent base_; ProxySentinel() = default; constexpr ProxySentinel(BaseSent base) : base_{std::move(base)} {} template requires std::equality_comparable_with friend constexpr bool operator==(const ProxyIterator& p, const ProxySentinel& sent) { return p.base_ == sent.base_; } }; template ProxySentinel(BaseSent) -> ProxySentinel; template requires std::ranges::view struct ProxyRange { Base base_; constexpr auto begin() { return ProxyIterator{std::ranges::begin(base_)}; } constexpr auto end() { return ProxySentinel{std::ranges::end(base_)}; } constexpr auto begin() const requires std::ranges::input_range { return ProxyIterator{std::ranges::begin(base_)}; } constexpr auto end() const requires std::ranges::input_range { return ProxySentinel{std::ranges::end(base_)}; } }; template requires std::ranges::viewable_range ProxyRange(R&&) -> ProxyRange>; #endif // TEST_STD_VER > 17 #if TEST_STD_VER >= 17 namespace util { template class iterator_wrapper { Iter iter_; using iter_traits = std::iterator_traits; public: using iterator_category = typename iter_traits::iterator_category; using value_type = typename iter_traits::value_type; using difference_type = typename iter_traits::difference_type; using pointer = typename iter_traits::pointer; using reference = typename iter_traits::reference; constexpr iterator_wrapper() : iter_() {} constexpr explicit iterator_wrapper(Iter iter) : iter_(iter) {} decltype(*iter_) operator*() { return *iter_; } decltype(*iter_) operator*() const { return *iter_; } decltype(iter_[0]) operator[](difference_type v) const { return iter_[v]; } Derived& operator++() { ++iter_; return static_cast(*this); } Derived operator++(int) { auto tmp = static_cast(*this); ++(*this); return tmp; } Derived& operator--() { --iter_; return static_cast(*this); } Derived operator--(int) { auto tmp = static_cast(*this); --(*this); return tmp; } iterator_wrapper& operator+=(difference_type i) { iter_ += i; return *this; } friend decltype(iter_ - iter_) operator-(const iterator_wrapper& lhs, const iterator_wrapper& rhs) { return lhs.iter_ - rhs.iter_; } friend Derived operator-(Derived iter, difference_type i) { iter.iter_ -= i; return iter; } friend Derived operator+(Derived iter, difference_type i) { iter.iter_ += i; return iter; } friend bool operator==(const iterator_wrapper& lhs, const iterator_wrapper& rhs) { return lhs.iter_ == rhs.iter_; } friend bool operator!=(const iterator_wrapper& lhs, const iterator_wrapper& rhs) { return lhs.iter_ != rhs.iter_; } }; class iterator_error : std::runtime_error { public: iterator_error(const char* what) : std::runtime_error(what) {} }; #ifndef TEST_HAS_NO_EXCEPTIONS template class throw_on_move_iterator : public iterator_wrapper, Iter> { using base = iterator_wrapper, Iter>; int moves_until_throw_ = 0; public: using difference_type = typename base::difference_type; using value_type = typename base::value_type; using iterator_category = typename base::iterator_category; throw_on_move_iterator() = default; throw_on_move_iterator(Iter iter, int moves_until_throw) : base(std::move(iter)), moves_until_throw_(moves_until_throw) {} throw_on_move_iterator(const throw_on_move_iterator& other) : base(other) {} throw_on_move_iterator& operator=(const throw_on_move_iterator& other) { static_cast(*this) = other; return *this; } throw_on_move_iterator(throw_on_move_iterator&& other) : base(std::move(other)), moves_until_throw_(other.moves_until_throw_ - 1) { if (moves_until_throw_ == -1) throw iterator_error("throw_on_move_iterator"); } throw_on_move_iterator& operator=(throw_on_move_iterator&& other) { moves_until_throw_ = other.moves_until_throw_ - 1; if (moves_until_throw_ == -1) throw iterator_error("throw_on_move_iterator"); return *this; } }; template throw_on_move_iterator(Iter) -> throw_on_move_iterator; #endif // TEST_HAS_NO_EXCEPTIONS } // namespace util #endif // TEST_STD_VER >= 17 namespace types { template using random_access_iterator_list = type_list= 20 contiguous_iterator, #endif random_access_iterator >; template using bidirectional_iterator_list = concatenate_t, type_list > >; template using forward_iterator_list = concatenate_t, type_list > >; template using cpp17_input_iterator_list = concatenate_t, type_list > >; #if TEST_STD_VER >= 20 template using cpp20_input_iterator_list = concatenate_t, type_list, cpp17_input_iterator>>; #endif } // namespace types #endif // SUPPORT_TEST_ITERATORS_H