| //===----------------------------------------------------------------------===// |
| // |
| // 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 <cassert> |
| #include <concepts> |
| #include <iterator> |
| #include <ranges> |
| #include <stdexcept> |
| #include <type_traits> |
| #include <utility> |
| |
| #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 It> |
| class cpp17_output_iterator |
| { |
| It it_; |
| |
| template <class U> friend class cpp17_output_iterator; |
| public: |
| typedef std::output_iterator_tag iterator_category; |
| typedef void value_type; |
| typedef typename std::iterator_traits<It>::difference_type difference_type; |
| typedef It pointer; |
| typedef typename std::iterator_traits<It>::reference reference; |
| |
| TEST_CONSTEXPR explicit cpp17_output_iterator(It it) : it_(std::move(it)) {} |
| |
| template <class U> |
| TEST_CONSTEXPR cpp17_output_iterator(const cpp17_output_iterator<U>& u) : it_(u.it_) {} |
| |
| template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type> |
| TEST_CONSTEXPR_CXX14 cpp17_output_iterator(cpp17_output_iterator<U>&& 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 <class T> |
| void operator,(T const &) = delete; |
| }; |
| #if TEST_STD_VER > 14 |
| template <class It> |
| cpp17_output_iterator(It) -> cpp17_output_iterator<It>; |
| #endif |
| |
| #if TEST_STD_VER > 17 |
| static_assert(std::output_iterator<cpp17_output_iterator<int*>, int>); |
| #endif |
| |
| // This iterator meets C++20's Cpp17InputIterator requirements, as described |
| // in Table 89 ([input.iterators]). |
| template <class It, class ItTraits = It> |
| class cpp17_input_iterator |
| { |
| typedef std::iterator_traits<ItTraits> Traits; |
| It it_; |
| |
| template <class U, class T> 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 <class U, class T> |
| TEST_CONSTEXPR cpp17_input_iterator(const cpp17_input_iterator<U, T>& u) : it_(u.it_) {} |
| |
| template <class U, class T, class = typename std::enable_if<std::is_default_constructible<U>::value>::type> |
| TEST_CONSTEXPR_CXX14 cpp17_input_iterator(cpp17_input_iterator<U, T>&& 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 <class T> |
| void operator,(T const &) = delete; |
| }; |
| #if TEST_STD_VER > 14 |
| template <class It> |
| cpp17_input_iterator(It) -> cpp17_input_iterator<It>; |
| #endif |
| |
| #if TEST_STD_VER > 17 |
| static_assert(std::input_iterator<cpp17_input_iterator<int*>>); |
| #endif |
| |
| template <class It> |
| class forward_iterator |
| { |
| It it_; |
| |
| template <class U> friend class forward_iterator; |
| public: |
| typedef std::forward_iterator_tag iterator_category; |
| typedef typename std::iterator_traits<It>::value_type value_type; |
| typedef typename std::iterator_traits<It>::difference_type difference_type; |
| typedef It pointer; |
| typedef typename std::iterator_traits<It>::reference reference; |
| |
| TEST_CONSTEXPR forward_iterator() : it_() {} |
| TEST_CONSTEXPR explicit forward_iterator(It it) : it_(it) {} |
| |
| template <class U> |
| TEST_CONSTEXPR forward_iterator(const forward_iterator<U>& u) : it_(u.it_) {} |
| |
| template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type> |
| TEST_CONSTEXPR_CXX14 forward_iterator(forward_iterator<U>&& 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 <class T> |
| void operator,(T const &) = delete; |
| }; |
| #if TEST_STD_VER > 14 |
| template <class It> |
| forward_iterator(It) -> forward_iterator<It>; |
| #endif |
| |
| template <class It> |
| class bidirectional_iterator |
| { |
| It it_; |
| |
| template <class U> friend class bidirectional_iterator; |
| public: |
| typedef std::bidirectional_iterator_tag iterator_category; |
| typedef typename std::iterator_traits<It>::value_type value_type; |
| typedef typename std::iterator_traits<It>::difference_type difference_type; |
| typedef It pointer; |
| typedef typename std::iterator_traits<It>::reference reference; |
| |
| TEST_CONSTEXPR bidirectional_iterator() : it_() {} |
| TEST_CONSTEXPR explicit bidirectional_iterator(It it) : it_(it) {} |
| |
| template <class U> |
| TEST_CONSTEXPR bidirectional_iterator(const bidirectional_iterator<U>& u) : it_(u.it_) {} |
| |
| template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type> |
| TEST_CONSTEXPR_CXX14 bidirectional_iterator(bidirectional_iterator<U>&& 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 <class T> |
| void operator,(T const &) = delete; |
| }; |
| #if TEST_STD_VER > 14 |
| template <class It> |
| bidirectional_iterator(It) -> bidirectional_iterator<It>; |
| #endif |
| |
| template <class It> |
| class random_access_iterator |
| { |
| It it_; |
| |
| template <class U> friend class random_access_iterator; |
| public: |
| typedef std::random_access_iterator_tag iterator_category; |
| typedef typename std::iterator_traits<It>::value_type value_type; |
| typedef typename std::iterator_traits<It>::difference_type difference_type; |
| typedef It pointer; |
| typedef typename std::iterator_traits<It>::reference reference; |
| |
| TEST_CONSTEXPR random_access_iterator() : it_() {} |
| TEST_CONSTEXPR explicit random_access_iterator(It it) : it_(it) {} |
| |
| template <class U> |
| TEST_CONSTEXPR random_access_iterator(const random_access_iterator<U>& u) : it_(u.it_) {} |
| |
| template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type> |
| TEST_CONSTEXPR_CXX14 random_access_iterator(random_access_iterator<U>&& 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 <class T> |
| void operator,(T const &) = delete; |
| }; |
| #if TEST_STD_VER > 14 |
| template <class It> |
| random_access_iterator(It) -> random_access_iterator<It>; |
| #endif |
| |
| #if TEST_STD_VER > 17 |
| |
| template <std::random_access_iterator It> |
| class cpp20_random_access_iterator { |
| It it_; |
| |
| template <std::random_access_iterator> |
| 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<It>::value_type; |
| using difference_type = typename std::iterator_traits<It>::difference_type; |
| |
| constexpr cpp20_random_access_iterator() : it_() {} |
| constexpr explicit cpp20_random_access_iterator(It it) : it_(it) {} |
| |
| template <class U> |
| constexpr cpp20_random_access_iterator(const cpp20_random_access_iterator<U>& u) : it_(u.it_) {} |
| |
| template <class U> |
| constexpr cpp20_random_access_iterator(cpp20_random_access_iterator<U>&& 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 <class T> |
| void operator,(T const&) = delete; |
| }; |
| template <class It> |
| cpp20_random_access_iterator(It) -> cpp20_random_access_iterator<It>; |
| |
| static_assert(std::random_access_iterator<cpp20_random_access_iterator<int*>>); |
| |
| template <class It> |
| class contiguous_iterator |
| { |
| static_assert(std::is_pointer_v<It>, "Things probably break in this case"); |
| |
| It it_; |
| |
| template <class U> friend class contiguous_iterator; |
| public: |
| typedef std::contiguous_iterator_tag iterator_category; |
| typedef typename std::iterator_traits<It>::value_type value_type; |
| typedef typename std::iterator_traits<It>::difference_type difference_type; |
| typedef It pointer; |
| typedef typename std::iterator_traits<It>::reference reference; |
| typedef typename std::remove_pointer<It>::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 <class U> |
| TEST_CONSTEXPR_CXX14 contiguous_iterator(const contiguous_iterator<U>& u) : it_(u.it_) {} |
| |
| template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type> |
| constexpr contiguous_iterator(contiguous_iterator<U>&& 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 <class T> |
| void operator,(T const &) = delete; |
| }; |
| template <class It> |
| contiguous_iterator(It) -> contiguous_iterator<It>; |
| |
| template <class It> |
| class three_way_contiguous_iterator |
| { |
| static_assert(std::is_pointer_v<It>, "Things probably break in this case"); |
| |
| It it_; |
| |
| template <class U> friend class three_way_contiguous_iterator; |
| public: |
| typedef std::contiguous_iterator_tag iterator_category; |
| typedef typename std::iterator_traits<It>::value_type value_type; |
| typedef typename std::iterator_traits<It>::difference_type difference_type; |
| typedef It pointer; |
| typedef typename std::iterator_traits<It>::reference reference; |
| typedef typename std::remove_pointer<It>::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 <class U> |
| constexpr three_way_contiguous_iterator(const three_way_contiguous_iterator<U>& u) : it_(u.it_) {} |
| |
| template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type> |
| constexpr three_way_contiguous_iterator(three_way_contiguous_iterator<U>&& 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 <class T> |
| void operator,(T const &) = delete; |
| }; |
| template <class It> |
| three_way_contiguous_iterator(It) -> three_way_contiguous_iterator<It>; |
| #endif // TEST_STD_VER > 17 |
| |
| template <class Iter> // ADL base() for everything else (including pointers) |
| TEST_CONSTEXPR Iter base(Iter i) { return i; } |
| |
| template <typename T> |
| 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 <class T2> |
| void operator,(T2 const &) = delete; |
| |
| private: |
| const T* begin_; |
| const T* end_; |
| const T* current_; |
| ThrowingAction action_; |
| mutable int index_; |
| }; |
| |
| template <typename T> |
| 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 <class T2> |
| void operator,(T2 const &) = delete; |
| |
| private: |
| const T *begin_; |
| const T *end_; |
| const T *current_; |
| }; |
| |
| #if TEST_STD_VER > 17 |
| |
| template <class It> |
| class cpp20_input_iterator |
| { |
| It it_; |
| |
| public: |
| using value_type = std::iter_value_t<It>; |
| using difference_type = std::iter_difference_t<It>; |
| 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 <class T> |
| void operator,(T const &) = delete; |
| }; |
| template <class It> |
| cpp20_input_iterator(It) -> cpp20_input_iterator<It>; |
| |
| static_assert(std::input_iterator<cpp20_input_iterator<int*>>); |
| |
| template<std::input_or_output_iterator> |
| struct iter_value_or_void { using type = void; }; |
| |
| template<std::input_iterator I> |
| struct iter_value_or_void<I> { |
| using type = std::iter_value_t<I>; |
| }; |
| |
| template <class It> |
| class cpp20_output_iterator { |
| It it_; |
| |
| public: |
| using difference_type = std::iter_difference_t<It>; |
| |
| 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 <class T> |
| void operator,(T const&) = delete; |
| }; |
| template <class It> |
| cpp20_output_iterator(It) -> cpp20_output_iterator<It>; |
| |
| static_assert(std::output_iterator<cpp20_output_iterator<int*>, int>); |
| |
| # if TEST_STD_VER >= 20 |
| |
| // An `input_iterator` that can be used in a `std::ranges::common_range` |
| template <class Base> |
| struct common_input_iterator { |
| Base it_; |
| |
| using value_type = std::iter_value_t<Base>; |
| 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 It> |
| class stride_counting_iterator { |
| public: |
| using value_type = typename iter_value_or_void<It>::type; |
| using difference_type = std::iter_difference_t<It>; |
| using iterator_concept = |
| std::conditional_t<std::contiguous_iterator<It>, std::contiguous_iterator_tag, |
| std::conditional_t<std::random_access_iterator<It>, std::random_access_iterator_tag, |
| std::conditional_t<std::bidirectional_iterator<It>, std::bidirectional_iterator_tag, |
| std::conditional_t<std::forward_iterator<It>, std::forward_iterator_tag, |
| std::conditional_t<std::input_iterator<It>, std::input_iterator_tag, |
| /* else */ std::output_iterator_tag |
| >>>>>; |
| |
| stride_counting_iterator() requires std::default_initializable<It> = 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<It> |
| { |
| auto temp = *this; |
| ++*this; |
| return temp; |
| } |
| |
| constexpr stride_counting_iterator& operator--() |
| requires std::bidirectional_iterator<It> |
| { |
| It tmp(base_); |
| base_ = base(--tmp); |
| ++stride_count_; |
| --stride_displacement_; |
| return *this; |
| } |
| |
| constexpr stride_counting_iterator operator--(int) |
| requires std::bidirectional_iterator<It> |
| { |
| auto temp = *this; |
| --*this; |
| return temp; |
| } |
| |
| constexpr stride_counting_iterator& operator+=(difference_type const n) |
| requires std::random_access_iterator<It> |
| { |
| 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> |
| { |
| 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<It> |
| { |
| return it += n; |
| } |
| |
| friend constexpr stride_counting_iterator operator+(difference_type n, stride_counting_iterator it) |
| requires std::random_access_iterator<It> |
| { |
| return it += n; |
| } |
| |
| friend constexpr stride_counting_iterator operator-(stride_counting_iterator it, difference_type n) |
| requires std::random_access_iterator<It> |
| { |
| return it -= n; |
| } |
| |
| friend constexpr difference_type operator-(stride_counting_iterator const& x, stride_counting_iterator const& y) |
| requires std::sized_sentinel_for<It, It> |
| { |
| return base(x) - base(y); |
| } |
| |
| constexpr bool operator==(stride_counting_iterator const& other) const |
| requires std::sentinel_for<It, It> |
| { |
| 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<It> |
| { |
| 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<It> |
| { |
| 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<It> |
| { |
| 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<It> |
| { |
| return It(x.base_) >= It(y.base_); |
| } |
| |
| template <class T> |
| void operator,(T const &) = delete; |
| |
| private: |
| decltype(base(std::declval<It>())) base_; |
| difference_type stride_count_ = 0; |
| difference_type stride_displacement_ = 0; |
| }; |
| template <class It> |
| stride_counting_iterator(It) -> stride_counting_iterator<It>; |
| |
| #endif // TEST_STD_VER > 17 |
| |
| #if TEST_STD_VER > 17 |
| template <class It> |
| 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<It>())) base_; |
| }; |
| template <class It> |
| sentinel_wrapper(It) -> sentinel_wrapper<It>; |
| |
| template <class It> |
| 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<It>())) base_; |
| }; |
| template <class It> |
| sized_sentinel(It) -> sized_sentinel<It>; |
| |
| 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 T> |
| 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 <class T> |
| rvalue_iterator(T*) -> rvalue_iterator<T>; |
| |
| static_assert(std::random_access_iterator<rvalue_iterator<int*>>); |
| |
| // 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 <class T> |
| struct Proxy; |
| |
| template <class T> |
| inline constexpr bool IsProxy = false; |
| |
| template <class T> |
| inline constexpr bool IsProxy<Proxy<T>> = true; |
| |
| template <class T> |
| struct Proxy { |
| T data; |
| |
| constexpr T& getData() & { return data; } |
| |
| constexpr const T& getData() const& { return data; } |
| |
| constexpr T&& getData() && { return static_cast<T&&>(data); } |
| |
| constexpr const T&& getData() const&& { return static_cast<const T&&>(data); } |
| |
| template <class U> |
| requires std::constructible_from<T, U&&> |
| constexpr Proxy(U&& u) : data{std::forward<U>(u)} {} |
| |
| // This constructor covers conversion from cvref of Proxy<U>, including non-const/const versions of copy/move constructor |
| template <class Other> |
| requires(IsProxy<std::decay_t<Other>> && std::constructible_from<T, decltype(std::declval<Other>().getData())>) |
| constexpr Proxy(Other&& other) : data{std::forward<Other>(other).getData()} {} |
| |
| template <class Other> |
| requires(IsProxy<std::decay_t<Other>> && std::assignable_from<T&, decltype(std::declval<Other>().getData())>) |
| constexpr Proxy& operator=(Other&& other) { |
| data = std::forward<Other>(other).getData(); |
| return *this; |
| } |
| |
| // const assignment required to make ProxyIterator model std::indirectly_writable |
| template <class Other> |
| requires(IsProxy<std::decay_t<Other>> && std::assignable_from<const T&, decltype(std::declval<Other>().getData())>) |
| constexpr const Proxy& operator=(Other&& other) const { |
| data = std::forward<Other>(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<T>{}, Proxy<T>{}) 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<T> && !std::is_reference_v<T>) |
| = default; |
| |
| // Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default equality comparison operator is deleted |
| // when `T` is a reference type. |
| template <class U> |
| friend constexpr bool operator==(const Proxy& lhs, const Proxy<U>& rhs) |
| requires std::equality_comparable_with<std::decay_t<T>, std::decay_t<U>> { |
| return lhs.data == rhs.data; |
| } |
| |
| friend constexpr auto operator<=>(const Proxy&, const Proxy&) |
| requires (std::three_way_comparable<T> && !std::is_reference_v<T>) |
| = default; |
| |
| // Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default 3-way comparison operator is deleted when |
| // `T` is a reference type. |
| template <class U> |
| friend constexpr auto operator<=>(const Proxy& lhs, const Proxy<U>& rhs) |
| requires std::three_way_comparable_with<std::decay_t<T>, std::decay_t<U>> { |
| return lhs.data <=> rhs.data; |
| } |
| }; |
| |
| // This is to make ProxyIterator model `std::indirectly_readable` |
| template <class T, class U, template <class> class TQual, template <class> class UQual> |
| requires requires { typename std::common_reference_t<TQual<T>, UQual<U>>; } |
| struct std::basic_common_reference<Proxy<T>, Proxy<U>, TQual, UQual> { |
| using type = Proxy<std::common_reference_t<TQual<T>, UQual<U>>>; |
| }; |
| |
| template <class T, class U> |
| requires requires { typename std::common_type_t<T, U>; } |
| struct std::common_type<Proxy<T>, Proxy<U>> { |
| using type = Proxy<std::common_type_t<T, U>>; |
| }; |
| |
| // ProxyIterator |
| // ====================================================================== |
| // It wraps `Base` iterator and when dereferenced it returns a Proxy<ref> |
| // 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<int&> |
| // iter_value_t -> Proxy<int> |
| // iter_move -> Proxy<int&&> |
| template <class Base> |
| struct ProxyIteratorBase {}; |
| |
| template <class Base> |
| requires std::derived_from< |
| typename std::iterator_traits<Base>::iterator_category, |
| std::input_iterator_tag> |
| struct ProxyIteratorBase<Base> { |
| using iterator_category = std::input_iterator_tag; |
| }; |
| |
| template <std::input_iterator Base> |
| consteval auto get_iterator_concept() { |
| if constexpr (std::random_access_iterator<Base>) { |
| return std::random_access_iterator_tag{}; |
| } else if constexpr (std::bidirectional_iterator<Base>) { |
| return std::bidirectional_iterator_tag{}; |
| } else if constexpr (std::forward_iterator<Base>) { |
| return std::forward_iterator_tag{}; |
| } else { |
| return std::input_iterator_tag{}; |
| } |
| } |
| |
| template <std::input_iterator Base> |
| struct ProxyIterator : ProxyIteratorBase<Base> { |
| Base base_; |
| |
| using iterator_concept = decltype(get_iterator_concept<Base>()); |
| using value_type = Proxy<std::iter_value_t<Base>>; |
| using difference_type = std::iter_difference_t<Base>; |
| |
| ProxyIterator() |
| requires std::default_initializable<Base> |
| = default; |
| |
| constexpr ProxyIterator(Base base) : base_{std::move(base)} {} |
| |
| template <class T> |
| requires std::constructible_from<Base, T&&> |
| constexpr ProxyIterator(T&& t) : base_{std::forward<T>(t)} {} |
| |
| friend constexpr decltype(auto) base(const ProxyIterator& p) { return base(p.base_); } |
| |
| // Specialization of iter_move |
| // If operator* returns Proxy<Foo&>, iter_move will return Proxy<Foo&&> |
| // Note std::move(*it) returns Proxy<Foo&>&&, which is not what we want as |
| // it will likely result in a copy rather than a move |
| friend constexpr Proxy<std::iter_rvalue_reference_t<Base>> 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<std::iter_reference_t<Base>> 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<Base> { |
| return x.base_ == y.base_; |
| } |
| |
| // to satisfy forward_iterator |
| constexpr ProxyIterator operator++(int) |
| requires std::forward_iterator<Base> { |
| auto tmp = *this; |
| ++*this; |
| return tmp; |
| } |
| |
| // to satisfy bidirectional_iterator |
| constexpr ProxyIterator& operator--() |
| requires std::bidirectional_iterator<Base> { |
| --base_; |
| return *this; |
| } |
| |
| constexpr ProxyIterator operator--(int) |
| requires std::bidirectional_iterator<Base> { |
| auto tmp = *this; |
| --*this; |
| return tmp; |
| } |
| |
| // to satisfy random_access_iterator |
| constexpr ProxyIterator& operator+=(difference_type n) |
| requires std::random_access_iterator<Base> { |
| base_ += n; |
| return *this; |
| } |
| |
| constexpr ProxyIterator& operator-=(difference_type n) |
| requires std::random_access_iterator<Base> { |
| base_ -= n; |
| return *this; |
| } |
| |
| constexpr Proxy<std::iter_reference_t<Base>> operator[](difference_type n) const |
| requires std::random_access_iterator<Base> { |
| return {base_[n]}; |
| } |
| |
| friend constexpr bool operator<(const ProxyIterator& x, const ProxyIterator& y) |
| requires std::random_access_iterator<Base> { |
| return x.base_ < y.base_; |
| } |
| |
| friend constexpr bool operator>(const ProxyIterator& x, const ProxyIterator& y) |
| requires std::random_access_iterator<Base> { |
| return x.base_ > y.base_; |
| } |
| |
| friend constexpr bool operator<=(const ProxyIterator& x, const ProxyIterator& y) |
| requires std::random_access_iterator<Base> { |
| return x.base_ <= y.base_; |
| } |
| |
| friend constexpr bool operator>=(const ProxyIterator& x, const ProxyIterator& y) |
| requires std::random_access_iterator<Base> { |
| return x.base_ >= y.base_; |
| } |
| |
| friend constexpr auto operator<=>(const ProxyIterator& x, const ProxyIterator& y) |
| requires(std::random_access_iterator<Base> && std::three_way_comparable<Base>) { |
| return x.base_ <=> y.base_; |
| } |
| |
| friend constexpr ProxyIterator operator+(const ProxyIterator& x, difference_type n) |
| requires std::random_access_iterator<Base> { |
| return ProxyIterator{x.base_ + n}; |
| } |
| |
| friend constexpr ProxyIterator operator+(difference_type n, const ProxyIterator& x) |
| requires std::random_access_iterator<Base> { |
| return ProxyIterator{n + x.base_}; |
| } |
| |
| friend constexpr ProxyIterator operator-(const ProxyIterator& x, difference_type n) |
| requires std::random_access_iterator<Base> { |
| return ProxyIterator{x.base_ - n}; |
| } |
| |
| friend constexpr difference_type operator-(const ProxyIterator& x, const ProxyIterator& y) |
| requires std::random_access_iterator<Base> { |
| return x.base_ - y.base_; |
| } |
| }; |
| template <class Base> |
| ProxyIterator(Base) -> ProxyIterator<Base>; |
| |
| static_assert(std::indirectly_readable<ProxyIterator<int*>>); |
| static_assert(std::indirectly_writable<ProxyIterator<int*>, Proxy<int>>); |
| static_assert(std::indirectly_writable<ProxyIterator<int*>, Proxy<int&>>); |
| |
| template <class Iter> |
| using Cpp20InputProxyIterator = ProxyIterator<cpp20_input_iterator<Iter>>; |
| |
| template <class Iter> |
| using ForwardProxyIterator = ProxyIterator<forward_iterator<Iter>>; |
| |
| template <class Iter> |
| using BidirectionalProxyIterator = ProxyIterator<bidirectional_iterator<Iter>>; |
| |
| template <class Iter> |
| using RandomAccessProxyIterator = ProxyIterator<random_access_iterator<Iter>>; |
| |
| template <class Iter> |
| using ContiguousProxyIterator = ProxyIterator<contiguous_iterator<Iter>>; |
| |
| template <class BaseSent> |
| struct ProxySentinel { |
| BaseSent base_; |
| |
| ProxySentinel() = default; |
| constexpr ProxySentinel(BaseSent base) : base_{std::move(base)} {} |
| |
| template <class Base> |
| requires std::equality_comparable_with<Base, BaseSent> |
| friend constexpr bool operator==(const ProxyIterator<Base>& p, const ProxySentinel& sent) { |
| return p.base_ == sent.base_; |
| } |
| }; |
| template <class BaseSent> |
| ProxySentinel(BaseSent) -> ProxySentinel<BaseSent>; |
| |
| template <std::ranges::input_range Base> |
| requires std::ranges::view<Base> |
| 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<const Base> { |
| return ProxyIterator{std::ranges::begin(base_)}; |
| } |
| |
| constexpr auto end() const |
| requires std::ranges::input_range<const Base> { |
| return ProxySentinel{std::ranges::end(base_)}; |
| } |
| }; |
| |
| template <std::ranges::input_range R> |
| requires std::ranges::viewable_range<R&&> |
| ProxyRange(R&&) -> ProxyRange<std::views::all_t<R&&>>; |
| |
| #endif // TEST_STD_VER > 17 |
| |
| namespace types { |
| template <class Ptr> |
| using random_access_iterator_list = |
| type_list<Ptr, |
| #if TEST_STD_VER >= 20 |
| contiguous_iterator<Ptr>, |
| #endif |
| random_access_iterator<Ptr> >; |
| |
| template <class Ptr> |
| using bidirectional_iterator_list = |
| concatenate_t<random_access_iterator_list<Ptr>, type_list<bidirectional_iterator<Ptr> > >; |
| |
| template <class Ptr> |
| using forward_iterator_list = concatenate_t<bidirectional_iterator_list<Ptr>, type_list<forward_iterator<Ptr> > >; |
| |
| template <class Ptr> |
| using cpp17_input_iterator_list = concatenate_t<forward_iterator_list<Ptr>, type_list<cpp17_input_iterator<Ptr> > >; |
| |
| #if TEST_STD_VER >= 20 |
| template <class Ptr> |
| using cpp20_input_iterator_list = |
| concatenate_t<forward_iterator_list<Ptr>, type_list<cpp20_input_iterator<Ptr>, cpp17_input_iterator<Ptr>>>; |
| #endif |
| } // namespace types |
| |
| |
| #endif // SUPPORT_TEST_ITERATORS_H |