e1008cd1d8
Since it was added in C++11, std::copy_n and std::ranges::copy_n should do nothing given a negative size, but for random access iterators we add the size to the iterator, possibly resulting in undefined behaviour. Also, C++20 clarified that std::copy_n requires the Size type to be convertible to an integral type. We previously assumed that it could be directly used in arithmetic expressions, without conversion to an integral type. This also fixes a bug in the random_access_iterator_wrapper helper adds some convenience aliases for using the iterator wrappers. libstdc++-v3/ChangeLog: * include/bits/ranges_algobase.h (__copy_n_fn): Only call ranges::copy for positive values. * include/bits/stl_algo.h (copy_n): Convert Size argument to an integral type and only call __copy_n for positive values. * testsuite/util/testsuite_iterators.h (random_access_iterator_wrapper::operator+=): Fix range check for negative values. (output_container, input_container, forward_container) (bidirectional_container, random_access_container): New alias templates. * testsuite/25_algorithms/copy_n/5.cc: New test.
900 lines
23 KiB
C++
900 lines
23 KiB
C++
// -*- C++ -*-
|
|
// Iterator Wrappers for the C++ library testsuite.
|
|
//
|
|
// Copyright (C) 2004-2020 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this library; see the file COPYING3. If not see
|
|
// <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
// This file provides the following:
|
|
//
|
|
// input_iterator_wrapper, output_iterator_wrapper
|
|
// forward_iterator_wrapper, bidirectional_iterator_wrapper and
|
|
// random_access_wrapper, which attempt to exactly perform the requirements
|
|
// of these types of iterators. These are constructed from the class
|
|
// test_container, which is given two pointers to T and an iterator type.
|
|
|
|
#include <testsuite_hooks.h>
|
|
#include <bits/stl_iterator_base_types.h>
|
|
|
|
#if __cplusplus >= 201103L
|
|
#include <bits/move.h>
|
|
#endif
|
|
|
|
#ifndef _TESTSUITE_ITERATORS
|
|
#define _TESTSUITE_ITERATORS
|
|
|
|
#ifdef DISABLE_ITERATOR_DEBUG
|
|
#define ITERATOR_VERIFY(x)
|
|
#else
|
|
#define ITERATOR_VERIFY(x) VERIFY(x)
|
|
#endif
|
|
|
|
namespace __gnu_test
|
|
{
|
|
/**
|
|
* @brief Simple container for holding two pointers.
|
|
*
|
|
* Note that input_iterator_wrapper changes first to denote
|
|
* how the valid range of == , ++, etc. change as the iterators are used.
|
|
*/
|
|
template<typename T>
|
|
struct BoundsContainer
|
|
{
|
|
T* first;
|
|
T* last;
|
|
|
|
BoundsContainer(T* _first, T* _last) : first(_first), last(_last)
|
|
{ }
|
|
|
|
std::size_t size() const { return last - first; }
|
|
};
|
|
|
|
// Simple container for holding state of a set of output iterators.
|
|
template<typename T>
|
|
struct OutputContainer : public BoundsContainer<T>
|
|
{
|
|
T* incrementedto;
|
|
bool* writtento;
|
|
|
|
OutputContainer(T* _first, T* _last)
|
|
: BoundsContainer<T>(_first, _last), incrementedto(_first),
|
|
writtento(new bool[this->size()]())
|
|
{ }
|
|
|
|
~OutputContainer()
|
|
{ delete[] writtento; }
|
|
};
|
|
|
|
// Produced by output_iterator to allow limited writing to pointer
|
|
template<class T>
|
|
class WritableObject
|
|
{
|
|
T* ptr;
|
|
|
|
public:
|
|
OutputContainer<T>* SharedInfo;
|
|
|
|
WritableObject(T* ptr_in, OutputContainer<T>* SharedInfo_in):
|
|
ptr(ptr_in), SharedInfo(SharedInfo_in)
|
|
{ }
|
|
|
|
#if __cplusplus >= 201103L
|
|
template<class U>
|
|
typename std::enable_if<std::is_assignable<T&, U>::value>::type
|
|
operator=(U&& new_val) const
|
|
{
|
|
ITERATOR_VERIFY(SharedInfo->writtento[ptr - SharedInfo->first] == 0);
|
|
SharedInfo->writtento[ptr - SharedInfo->first] = 1;
|
|
*ptr = std::forward<U>(new_val);
|
|
}
|
|
#else
|
|
template<class U>
|
|
void
|
|
operator=(const U& new_val)
|
|
{
|
|
ITERATOR_VERIFY(SharedInfo->writtento[ptr - SharedInfo->first] == 0);
|
|
SharedInfo->writtento[ptr - SharedInfo->first] = 1;
|
|
*ptr = new_val;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
/**
|
|
* @brief output_iterator wrapper for pointer
|
|
*
|
|
* This class takes a pointer and wraps it to provide exactly
|
|
* the requirements of a output_iterator. It should not be
|
|
* instantiated directly, but generated from a test_container
|
|
*/
|
|
template<class T>
|
|
struct output_iterator_wrapper
|
|
: public std::iterator<std::output_iterator_tag, T, std::ptrdiff_t, T*, T&>
|
|
{
|
|
protected:
|
|
output_iterator_wrapper() : ptr(0), SharedInfo(0)
|
|
{ }
|
|
|
|
public:
|
|
typedef OutputContainer<T> ContainerType;
|
|
T* ptr;
|
|
ContainerType* SharedInfo;
|
|
|
|
output_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in)
|
|
: ptr(_ptr), SharedInfo(SharedInfo_in)
|
|
{
|
|
ITERATOR_VERIFY(ptr >= SharedInfo->first && ptr <= SharedInfo->last);
|
|
}
|
|
|
|
#if __cplusplus >= 201103L
|
|
output_iterator_wrapper(const output_iterator_wrapper&) = default;
|
|
|
|
output_iterator_wrapper&
|
|
operator=(const output_iterator_wrapper&) = default;
|
|
#endif
|
|
|
|
WritableObject<T>
|
|
operator*() const
|
|
{
|
|
ITERATOR_VERIFY(ptr < SharedInfo->last);
|
|
ITERATOR_VERIFY(SharedInfo->writtento[ptr - SharedInfo->first] == false);
|
|
return WritableObject<T>(ptr, SharedInfo);
|
|
}
|
|
|
|
output_iterator_wrapper&
|
|
operator++()
|
|
{
|
|
ITERATOR_VERIFY(SharedInfo && ptr < SharedInfo->last);
|
|
ITERATOR_VERIFY(ptr>=SharedInfo->incrementedto);
|
|
ptr++;
|
|
SharedInfo->incrementedto=ptr;
|
|
return *this;
|
|
}
|
|
|
|
output_iterator_wrapper
|
|
operator++(int)
|
|
{
|
|
output_iterator_wrapper<T> tmp = *this;
|
|
++*this;
|
|
return tmp;
|
|
}
|
|
|
|
#if __cplusplus >= 201103L
|
|
template<typename U>
|
|
void operator,(const U&) const = delete;
|
|
#else
|
|
private:
|
|
template<typename U>
|
|
void operator,(const U&) const;
|
|
#endif
|
|
};
|
|
|
|
#if __cplusplus >= 201103L
|
|
template<typename T, typename U>
|
|
void operator,(const T&, const output_iterator_wrapper<U>&) = delete;
|
|
#endif
|
|
|
|
#if __cplusplus >= 201103L
|
|
using std::remove_cv;
|
|
#else
|
|
template<typename T> struct remove_cv { typedef T type; };
|
|
template<typename T> struct remove_cv<const T> { typedef T type; };
|
|
template<typename T> struct remove_cv<volatile T> { typedef T type; };
|
|
template<typename T> struct remove_cv<const volatile T> { typedef T type; };
|
|
#endif
|
|
|
|
/**
|
|
* @brief input_iterator wrapper for pointer
|
|
*
|
|
* This class takes a pointer and wraps it to provide exactly
|
|
* the requirements of a input_iterator. It should not be
|
|
* instantiated directly, but generated from a test_container
|
|
*/
|
|
template<class T>
|
|
class input_iterator_wrapper
|
|
: public std::iterator<std::input_iterator_tag, typename remove_cv<T>::type,
|
|
std::ptrdiff_t, T*, T&>
|
|
{
|
|
struct post_inc_proxy
|
|
{
|
|
struct deref_proxy
|
|
{
|
|
T* ptr;
|
|
operator const T&() const { return *ptr; }
|
|
} p;
|
|
|
|
deref_proxy operator*() const { return p; }
|
|
};
|
|
|
|
protected:
|
|
input_iterator_wrapper() : ptr(0), SharedInfo(0)
|
|
{ }
|
|
|
|
public:
|
|
typedef BoundsContainer<T> ContainerType;
|
|
T* ptr;
|
|
ContainerType* SharedInfo;
|
|
|
|
input_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in)
|
|
: ptr(_ptr), SharedInfo(SharedInfo_in)
|
|
{ ITERATOR_VERIFY(ptr >= SharedInfo->first && ptr <= SharedInfo->last); }
|
|
|
|
#if __cplusplus >= 201103L
|
|
input_iterator_wrapper(const input_iterator_wrapper&) = default;
|
|
|
|
input_iterator_wrapper&
|
|
operator=(const input_iterator_wrapper&) = default;
|
|
#endif
|
|
|
|
bool
|
|
operator==(const input_iterator_wrapper& in) const
|
|
{
|
|
ITERATOR_VERIFY(SharedInfo && SharedInfo == in.SharedInfo);
|
|
ITERATOR_VERIFY(ptr>=SharedInfo->first && in.ptr>=SharedInfo->first);
|
|
return ptr == in.ptr;
|
|
}
|
|
|
|
bool
|
|
operator!=(const input_iterator_wrapper& in) const
|
|
{
|
|
return !(*this == in);
|
|
}
|
|
|
|
T&
|
|
operator*() const
|
|
{
|
|
ITERATOR_VERIFY(SharedInfo && ptr < SharedInfo->last);
|
|
ITERATOR_VERIFY(ptr >= SharedInfo->first);
|
|
return *ptr;
|
|
}
|
|
|
|
T*
|
|
operator->() const
|
|
{
|
|
return &**this;
|
|
}
|
|
|
|
input_iterator_wrapper&
|
|
operator++()
|
|
{
|
|
ITERATOR_VERIFY(SharedInfo && ptr < SharedInfo->last);
|
|
ITERATOR_VERIFY(ptr>=SharedInfo->first);
|
|
ptr++;
|
|
SharedInfo->first=ptr;
|
|
return *this;
|
|
}
|
|
|
|
post_inc_proxy
|
|
operator++(int)
|
|
{
|
|
post_inc_proxy tmp = { { ptr } };
|
|
++*this;
|
|
return tmp;
|
|
}
|
|
|
|
#if __cplusplus >= 201103L
|
|
template<typename U>
|
|
void operator,(const U&) const = delete;
|
|
#else
|
|
private:
|
|
template<typename U>
|
|
void operator,(const U&) const;
|
|
#endif
|
|
};
|
|
|
|
#if __cplusplus >= 201103L
|
|
template<typename T, typename U>
|
|
void operator,(const T&, const input_iterator_wrapper<U>&) = delete;
|
|
#endif
|
|
|
|
/**
|
|
* @brief forward_iterator wrapper for pointer
|
|
*
|
|
* This class takes a pointer and wraps it to provide exactly
|
|
* the requirements of a forward_iterator. It should not be
|
|
* instantiated directly, but generated from a test_container
|
|
*/
|
|
template<class T>
|
|
struct forward_iterator_wrapper : public input_iterator_wrapper<T>
|
|
{
|
|
typedef BoundsContainer<T> ContainerType;
|
|
typedef std::forward_iterator_tag iterator_category;
|
|
|
|
forward_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in)
|
|
: input_iterator_wrapper<T>(_ptr, SharedInfo_in)
|
|
{ }
|
|
|
|
forward_iterator_wrapper()
|
|
{ }
|
|
|
|
#if __cplusplus >= 201103L
|
|
forward_iterator_wrapper(const forward_iterator_wrapper&) = default;
|
|
|
|
forward_iterator_wrapper&
|
|
operator=(const forward_iterator_wrapper&) = default;
|
|
#endif
|
|
|
|
T&
|
|
operator*() const
|
|
{
|
|
ITERATOR_VERIFY(this->SharedInfo && this->ptr < this->SharedInfo->last);
|
|
return *(this->ptr);
|
|
}
|
|
|
|
T*
|
|
operator->() const
|
|
{ return &**this; }
|
|
|
|
forward_iterator_wrapper&
|
|
operator++()
|
|
{
|
|
ITERATOR_VERIFY(this->SharedInfo && this->ptr < this->SharedInfo->last);
|
|
this->ptr++;
|
|
return *this;
|
|
}
|
|
|
|
forward_iterator_wrapper
|
|
operator++(int)
|
|
{
|
|
forward_iterator_wrapper<T> tmp = *this;
|
|
++*this;
|
|
return tmp;
|
|
}
|
|
|
|
#if __cplusplus >= 201402L
|
|
bool
|
|
operator==(const forward_iterator_wrapper& it) const noexcept
|
|
{
|
|
// Since C++14 value-initialized forward iterators are comparable.
|
|
if (this->SharedInfo == nullptr || it.SharedInfo == nullptr)
|
|
return this->SharedInfo == it.SharedInfo && this->ptr == it.ptr;
|
|
|
|
const input_iterator_wrapper<T>& base_this = *this;
|
|
const input_iterator_wrapper<T>& base_that = it;
|
|
return base_this == base_that;
|
|
}
|
|
|
|
bool
|
|
operator!=(const forward_iterator_wrapper& it) const noexcept
|
|
{
|
|
return !(*this == it);
|
|
}
|
|
#endif
|
|
};
|
|
|
|
/**
|
|
* @brief bidirectional_iterator wrapper for pointer
|
|
*
|
|
* This class takes a pointer and wraps it to provide exactly
|
|
* the requirements of a bidirectional_iterator. It should not be
|
|
* instantiated directly, but generated from a test_container
|
|
*/
|
|
template<class T>
|
|
struct bidirectional_iterator_wrapper : public forward_iterator_wrapper<T>
|
|
{
|
|
typedef BoundsContainer<T> ContainerType;
|
|
typedef std::bidirectional_iterator_tag iterator_category;
|
|
|
|
bidirectional_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in)
|
|
: forward_iterator_wrapper<T>(_ptr, SharedInfo_in)
|
|
{ }
|
|
|
|
bidirectional_iterator_wrapper()
|
|
: forward_iterator_wrapper<T>()
|
|
{ }
|
|
|
|
#if __cplusplus >= 201103L
|
|
bidirectional_iterator_wrapper(
|
|
const bidirectional_iterator_wrapper&) = default;
|
|
|
|
bidirectional_iterator_wrapper&
|
|
operator=(const bidirectional_iterator_wrapper&) = default;
|
|
#endif
|
|
|
|
bidirectional_iterator_wrapper&
|
|
operator++()
|
|
{
|
|
ITERATOR_VERIFY(this->SharedInfo && this->ptr < this->SharedInfo->last);
|
|
this->ptr++;
|
|
return *this;
|
|
}
|
|
|
|
bidirectional_iterator_wrapper
|
|
operator++(int)
|
|
{
|
|
bidirectional_iterator_wrapper<T> tmp = *this;
|
|
++*this;
|
|
return tmp;
|
|
}
|
|
|
|
bidirectional_iterator_wrapper&
|
|
operator--()
|
|
{
|
|
ITERATOR_VERIFY(this->SharedInfo && this->ptr > this->SharedInfo->first);
|
|
this->ptr--;
|
|
return *this;
|
|
}
|
|
|
|
bidirectional_iterator_wrapper
|
|
operator--(int)
|
|
{
|
|
bidirectional_iterator_wrapper<T> tmp = *this;
|
|
--*this;
|
|
return tmp;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief random_access_iterator wrapper for pointer
|
|
*
|
|
* This class takes a pointer and wraps it to provide exactly
|
|
* the requirements of a random_access_iterator. It should not be
|
|
* instantiated directly, but generated from a test_container
|
|
*/
|
|
template<class T>
|
|
struct random_access_iterator_wrapper
|
|
: public bidirectional_iterator_wrapper<T>
|
|
{
|
|
typedef BoundsContainer<T> ContainerType;
|
|
typedef std::random_access_iterator_tag iterator_category;
|
|
|
|
random_access_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in)
|
|
: bidirectional_iterator_wrapper<T>(_ptr, SharedInfo_in)
|
|
{ }
|
|
|
|
random_access_iterator_wrapper()
|
|
: bidirectional_iterator_wrapper<T>()
|
|
{ }
|
|
|
|
#if __cplusplus >= 201103L
|
|
random_access_iterator_wrapper(
|
|
const random_access_iterator_wrapper&) = default;
|
|
|
|
random_access_iterator_wrapper&
|
|
operator=(const random_access_iterator_wrapper&) = default;
|
|
#endif
|
|
|
|
random_access_iterator_wrapper&
|
|
operator++()
|
|
{
|
|
ITERATOR_VERIFY(this->SharedInfo && this->ptr < this->SharedInfo->last);
|
|
this->ptr++;
|
|
return *this;
|
|
}
|
|
|
|
random_access_iterator_wrapper
|
|
operator++(int)
|
|
{
|
|
random_access_iterator_wrapper<T> tmp = *this;
|
|
++*this;
|
|
return tmp;
|
|
}
|
|
|
|
random_access_iterator_wrapper&
|
|
operator--()
|
|
{
|
|
ITERATOR_VERIFY(this->SharedInfo && this->ptr > this->SharedInfo->first);
|
|
this->ptr--;
|
|
return *this;
|
|
}
|
|
|
|
random_access_iterator_wrapper
|
|
operator--(int)
|
|
{
|
|
random_access_iterator_wrapper<T> tmp = *this;
|
|
--*this;
|
|
return tmp;
|
|
}
|
|
|
|
random_access_iterator_wrapper&
|
|
operator+=(std::ptrdiff_t n)
|
|
{
|
|
if(n > 0)
|
|
{
|
|
ITERATOR_VERIFY(n <= this->SharedInfo->last - this->ptr);
|
|
this->ptr += n;
|
|
}
|
|
else
|
|
{
|
|
ITERATOR_VERIFY(-n <= this->ptr - this->SharedInfo->first);
|
|
this->ptr += n;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
random_access_iterator_wrapper&
|
|
operator-=(std::ptrdiff_t n)
|
|
{ return *this += -n; }
|
|
|
|
random_access_iterator_wrapper
|
|
operator-(std::ptrdiff_t n) const
|
|
{
|
|
random_access_iterator_wrapper<T> tmp = *this;
|
|
return tmp -= n;
|
|
}
|
|
|
|
std::ptrdiff_t
|
|
operator-(const random_access_iterator_wrapper<T>& in) const
|
|
{
|
|
ITERATOR_VERIFY(this->SharedInfo == in.SharedInfo);
|
|
return this->ptr - in.ptr;
|
|
}
|
|
|
|
T&
|
|
operator[](std::ptrdiff_t n) const
|
|
{ return *(*this + n); }
|
|
|
|
bool
|
|
operator<(const random_access_iterator_wrapper<T>& in) const
|
|
{
|
|
ITERATOR_VERIFY(this->SharedInfo == in.SharedInfo);
|
|
return this->ptr < in.ptr;
|
|
}
|
|
|
|
bool
|
|
operator>(const random_access_iterator_wrapper<T>& in) const
|
|
{
|
|
return in < *this;
|
|
}
|
|
|
|
bool
|
|
operator>=(const random_access_iterator_wrapper<T>& in) const
|
|
{
|
|
return !(*this < in);
|
|
}
|
|
|
|
bool
|
|
operator<=(const random_access_iterator_wrapper<T>& in) const
|
|
{
|
|
return !(*this > in);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
random_access_iterator_wrapper<T>
|
|
operator+(random_access_iterator_wrapper<T> it, std::ptrdiff_t n)
|
|
{ return it += n; }
|
|
|
|
template<typename T>
|
|
random_access_iterator_wrapper<T>
|
|
operator+(std::ptrdiff_t n, random_access_iterator_wrapper<T> it)
|
|
{ return it += n; }
|
|
|
|
|
|
/**
|
|
* @brief A container-type class for holding iterator wrappers
|
|
* test_container takes two parameters, a class T and an iterator
|
|
* wrapper templated by T (for example forward_iterator_wrapper<T>.
|
|
* It takes two pointers representing a range and presents them as
|
|
* a container of iterators.
|
|
*/
|
|
template <class T, template<class TT> class ItType>
|
|
struct test_container
|
|
{
|
|
typename ItType<T>::ContainerType bounds;
|
|
|
|
test_container(T* _first, T* _last) : bounds(_first, _last)
|
|
{ }
|
|
|
|
#if __cplusplus >= 201103L
|
|
template<std::size_t N>
|
|
explicit
|
|
test_container(T (&arr)[N]) : test_container(arr, arr+N)
|
|
{ }
|
|
#endif
|
|
|
|
ItType<T>
|
|
it(int pos)
|
|
{
|
|
ITERATOR_VERIFY(pos >= 0 && pos <= size());
|
|
return ItType<T>(bounds.first + pos, &bounds);
|
|
}
|
|
|
|
ItType<T>
|
|
it(T* pos)
|
|
{
|
|
ITERATOR_VERIFY(pos >= bounds.first && pos <= bounds.last);
|
|
return ItType<T>(pos, &bounds);
|
|
}
|
|
|
|
const T&
|
|
val(int pos)
|
|
{ return (bounds.first)[pos]; }
|
|
|
|
ItType<T>
|
|
begin()
|
|
{ return it(bounds.first); }
|
|
|
|
ItType<T>
|
|
end()
|
|
{ return it(bounds.last); }
|
|
|
|
std::size_t
|
|
size() const
|
|
{ return bounds.size(); }
|
|
};
|
|
|
|
#if __cplusplus >= 201103L
|
|
template<typename T>
|
|
using output_container
|
|
= test_container<T, output_iterator_wrapper>;
|
|
|
|
template<typename T>
|
|
using input_container
|
|
= test_container<T, input_iterator_wrapper>;
|
|
|
|
template<typename T>
|
|
using forward_container
|
|
= test_container<T, forward_iterator_wrapper>;
|
|
|
|
template<typename T>
|
|
using bidirectional_container
|
|
= test_container<T, bidirectional_iterator_wrapper>;
|
|
|
|
template<typename T>
|
|
using random_access_container
|
|
= test_container<T, random_access_iterator_wrapper>;
|
|
#endif
|
|
|
|
#if __cplusplus > 201703L
|
|
template<typename T>
|
|
struct contiguous_iterator_wrapper
|
|
: random_access_iterator_wrapper<T>
|
|
{
|
|
using random_access_iterator_wrapper<T>::random_access_iterator_wrapper;
|
|
|
|
using iterator_concept = std::contiguous_iterator_tag;
|
|
|
|
contiguous_iterator_wrapper&
|
|
operator++()
|
|
{
|
|
random_access_iterator_wrapper<T>::operator++();
|
|
return *this;
|
|
}
|
|
|
|
contiguous_iterator_wrapper&
|
|
operator--()
|
|
{
|
|
random_access_iterator_wrapper<T>::operator--();
|
|
return *this;
|
|
}
|
|
|
|
contiguous_iterator_wrapper
|
|
operator++(int)
|
|
{
|
|
auto tmp = *this;
|
|
++*this;
|
|
return tmp;
|
|
}
|
|
|
|
contiguous_iterator_wrapper
|
|
operator--(int)
|
|
{
|
|
auto tmp = *this;
|
|
--*this;
|
|
return tmp;
|
|
}
|
|
|
|
contiguous_iterator_wrapper&
|
|
operator+=(std::ptrdiff_t n)
|
|
{
|
|
random_access_iterator_wrapper<T>::operator+=(n);
|
|
return *this;
|
|
}
|
|
|
|
friend contiguous_iterator_wrapper
|
|
operator+(contiguous_iterator_wrapper iter, std::ptrdiff_t n)
|
|
{ return iter += n; }
|
|
|
|
friend contiguous_iterator_wrapper
|
|
operator+(std::ptrdiff_t n, contiguous_iterator_wrapper iter)
|
|
{ return iter += n; }
|
|
|
|
contiguous_iterator_wrapper&
|
|
operator-=(std::ptrdiff_t n)
|
|
{ return *this += -n; }
|
|
|
|
friend contiguous_iterator_wrapper
|
|
operator-(contiguous_iterator_wrapper iter, std::ptrdiff_t n)
|
|
{ return iter -= n; }
|
|
};
|
|
|
|
template<typename T>
|
|
using contiguous_container
|
|
= test_container<T, contiguous_iterator_wrapper>;
|
|
|
|
// A move-only input iterator type.
|
|
template<typename T>
|
|
struct input_iterator_wrapper_nocopy : input_iterator_wrapper<T>
|
|
{
|
|
using input_iterator_wrapper<T>::input_iterator_wrapper;
|
|
|
|
input_iterator_wrapper_nocopy()
|
|
: input_iterator_wrapper<T>(nullptr, nullptr)
|
|
{ }
|
|
|
|
input_iterator_wrapper_nocopy(const input_iterator_wrapper_nocopy&) = delete;
|
|
input_iterator_wrapper_nocopy&
|
|
operator=(const input_iterator_wrapper_nocopy&) = delete;
|
|
|
|
input_iterator_wrapper_nocopy(input_iterator_wrapper_nocopy&&) = default;
|
|
input_iterator_wrapper_nocopy&
|
|
operator=(input_iterator_wrapper_nocopy&&) = default;
|
|
|
|
using input_iterator_wrapper<T>::operator++;
|
|
|
|
input_iterator_wrapper_nocopy&
|
|
operator++()
|
|
{
|
|
input_iterator_wrapper<T>::operator++();
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
// A type meeting the minimum std::range requirements
|
|
template<typename T, template<typename> class Iter>
|
|
class test_range
|
|
{
|
|
// Exposes the protected default constructor of Iter<T> if needed. This
|
|
// is needed only when Iter is input_iterator_wrapper or
|
|
// output_iterator_wrapper, because legacy forward iterators and beyond
|
|
// are already default constructible.
|
|
struct iterator : Iter<T>
|
|
{
|
|
using Iter<T>::Iter;
|
|
|
|
using Iter<T>::operator++;
|
|
|
|
iterator& operator++() { Iter<T>::operator++(); return *this; }
|
|
};
|
|
|
|
template<typename I>
|
|
struct sentinel
|
|
{
|
|
T* end;
|
|
|
|
friend bool operator==(const sentinel& s, const I& i) noexcept
|
|
{ return s.end == i.ptr; }
|
|
|
|
friend auto operator-(const sentinel& s, const I& i) noexcept
|
|
requires std::random_access_iterator<I>
|
|
{ return s.end - i.ptr; }
|
|
|
|
friend auto operator-(const I& i, const sentinel& s) noexcept
|
|
requires std::random_access_iterator<I>
|
|
{ return i.ptr - s.end; }
|
|
};
|
|
|
|
protected:
|
|
auto
|
|
get_iterator(T* p)
|
|
{
|
|
if constexpr (std::default_initializable<Iter<T>>)
|
|
return Iter<T>(p, &bounds);
|
|
else
|
|
return iterator(p, &bounds);
|
|
}
|
|
|
|
public:
|
|
test_range(T* first, T* last) : bounds(first, last)
|
|
{ }
|
|
|
|
template<std::size_t N>
|
|
explicit
|
|
test_range(T (&arr)[N]) : test_range(arr, arr+N)
|
|
{ }
|
|
|
|
auto begin() & { return get_iterator(bounds.first); }
|
|
|
|
auto end() &
|
|
{
|
|
using I = decltype(get_iterator(bounds.last));
|
|
return sentinel<I>{bounds.last};
|
|
}
|
|
|
|
typename Iter<T>::ContainerType bounds;
|
|
};
|
|
|
|
template<typename T>
|
|
using test_contiguous_range
|
|
= test_range<T, contiguous_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_random_access_range
|
|
= test_range<T, random_access_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_bidirectional_range
|
|
= test_range<T, bidirectional_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_forward_range
|
|
= test_range<T, forward_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_input_range
|
|
= test_range<T, input_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_output_range
|
|
= test_range<T, output_iterator_wrapper>;
|
|
|
|
// A type meeting the minimum std::sized_range requirements
|
|
template<typename T, template<typename> class Iter>
|
|
struct test_sized_range : test_range<T, Iter>
|
|
{
|
|
using test_range<T, Iter>::test_range;
|
|
|
|
std::size_t size() const noexcept
|
|
{ return this->bounds.size(); }
|
|
};
|
|
|
|
template<typename T>
|
|
using test_contiguous_sized_range
|
|
= test_sized_range<T, contiguous_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_random_access_sized_range
|
|
= test_sized_range<T, random_access_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_bidirectional_sized_range
|
|
= test_sized_range<T, bidirectional_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_forward_sized_range
|
|
= test_sized_range<T, forward_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_input_sized_range
|
|
= test_sized_range<T, input_iterator_wrapper>;
|
|
template<typename T>
|
|
using test_output_sized_range
|
|
= test_sized_range<T, output_iterator_wrapper>;
|
|
|
|
// A type meeting the minimum std::sized_range requirements, and whose end()
|
|
// returns a sized sentinel.
|
|
template<typename T, template<typename> class Iter>
|
|
struct test_sized_range_sized_sent : test_sized_range<T, Iter>
|
|
{
|
|
using test_sized_range<T, Iter>::test_sized_range;
|
|
|
|
template<typename I>
|
|
struct sentinel
|
|
{
|
|
T* end;
|
|
|
|
friend bool operator==(const sentinel& s, const I& i) noexcept
|
|
{ return s.end == i.ptr; }
|
|
|
|
friend std::iter_difference_t<I>
|
|
operator-(const sentinel& s, const I& i) noexcept
|
|
{ return s.end - i.ptr; }
|
|
|
|
friend std::iter_difference_t<I>
|
|
operator-(const I& i, const sentinel& s) noexcept
|
|
{ return i.ptr - s.end; }
|
|
};
|
|
|
|
auto end() &
|
|
{
|
|
using I = decltype(this->get_iterator(this->bounds.last));
|
|
return sentinel<I>{this->bounds.last};
|
|
}
|
|
};
|
|
|
|
// test_range and test_sized_range do not own their elements, so they model
|
|
// std::ranges::borrowed_range. This file does not define specializations of
|
|
// std::ranges::enable_borrowed_range, so that individual tests can decide
|
|
// whether or not to do so.
|
|
// This is also true for test_container, although only when it has forward
|
|
// iterators (because output_iterator_wrapper and input_iterator_wrapper are
|
|
// not default constructible so do not model std::input_or_output_iterator).
|
|
#endif // C++20
|
|
} // namespace __gnu_test
|
|
#endif // _TESTSUITE_ITERATORS
|