[libc++] Refactor the tests for ranges::{advance,next,prev}

This makes all the tests consistent and improves code coverage. This also
uncovers a bug with negative indices in advance() (which also impacts
prev()) -- I'll fix that in a subsequent patch.

I chose to only count operations in the tests for ranges::advance because
doing so in prev() and next() too was reaching diminishing returns, and
didn't meaningfully improve our test coverage.
This commit is contained in:
Louis Dionne 2022-01-13 12:16:30 -05:00
parent 52b8fe9b6e
commit b476039e8b
11 changed files with 386 additions and 445 deletions

View File

@ -13,67 +13,58 @@
#include <iterator>
#include <array>
#include <cassert>
#include "test_iterators.h"
#include "test_macros.h"
using range_t = std::array<int, 10>;
template <bool Count, typename It>
constexpr void check(int* first, std::iter_difference_t<It> n, int* expected) {
using Difference = std::iter_difference_t<It>;
Difference const M = (expected - first); // expected travel distance (which may be negative)
auto abs = [](auto x) { return x < 0 ? -x : x; };
template <std::input_or_output_iterator It>
constexpr void check_move_forward(std::ptrdiff_t const n) {
auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto first = stride_counting_iterator(It(range.begin()));
std::ranges::advance(first, n);
assert(first.base().base() == range.begin() + n);
if constexpr (std::random_access_iterator<It>) {
assert(first.stride_count() == 0 || first.stride_count() == 1);
assert(first.stride_displacement() == 1);
} else {
assert(first.stride_count() == n);
assert(first.stride_displacement() == n);
{
It it(first);
std::ranges::advance(it, n);
assert(base(it) == expected);
ASSERT_SAME_TYPE(decltype(std::ranges::advance(it, n)), void);
}
}
template <std::bidirectional_iterator It>
constexpr void check_move_backward(std::ptrdiff_t const n) {
auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto first = stride_counting_iterator(It(range.begin() + n));
std::ranges::advance(first, -n);
assert(first.base().base() == range.begin());
// Count operations
if constexpr (Count) {
auto it = stride_counting_iterator(It(first));
std::ranges::advance(it, n);
if constexpr (std::random_access_iterator<It>) {
assert(first.stride_count() == 0 || first.stride_count() == 1);
assert(first.stride_displacement() == 1);
assert(it.stride_count() <= 1);
} else {
assert(first.stride_count() == n);
assert(first.stride_displacement() == -n);
assert(it.stride_count() == abs(M));
}
}
}
constexpr bool test() {
check_move_forward<cpp17_input_iterator<range_t::const_iterator> >(1);
check_move_forward<cpp20_input_iterator<range_t::const_iterator> >(2);
check_move_forward<forward_iterator<range_t::const_iterator> >(3);
check_move_forward<bidirectional_iterator<range_t::const_iterator> >(4);
check_move_forward<random_access_iterator<range_t::const_iterator> >(5);
check_move_forward<contiguous_iterator<range_t::const_iterator> >(6);
check_move_forward<output_iterator<range_t::iterator> >(7);
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
check_move_backward<bidirectional_iterator<range_t::const_iterator> >(4);
check_move_backward<random_access_iterator<range_t::const_iterator> >(5);
check_move_backward<contiguous_iterator<range_t::const_iterator> >(6);
// Check advancing forward
for (int n = 0; n != 10; ++n) {
check<false, cpp17_input_iterator<int*>>( range, n, range+n);
check<false, cpp20_input_iterator<int*>>( range, n, range+n);
check<true, forward_iterator<int*>>( range, n, range+n);
check<true, bidirectional_iterator<int*>>(range, n, range+n);
check<true, random_access_iterator<int*>>(range, n, range+n);
check<true, contiguous_iterator<int*>>( range, n, range+n);
check<true, int*>( range, n, range+n);
check<true, output_iterator<int*> >( range, n, range+n);
}
// Zero should be checked for each case and each overload
check_move_forward<cpp17_input_iterator<range_t::const_iterator> >(0);
check_move_forward<cpp20_input_iterator<range_t::const_iterator> >(0);
check_move_forward<forward_iterator<range_t::const_iterator> >(0);
check_move_forward<bidirectional_iterator<range_t::const_iterator> >(0);
check_move_forward<random_access_iterator<range_t::const_iterator> >(0);
check_move_forward<output_iterator<range_t::iterator> >(0);
check_move_backward<bidirectional_iterator<range_t::const_iterator> >(0);
check_move_backward<random_access_iterator<range_t::const_iterator> >(0);
// Check advancing backward
for (int n = 0; n != 10; ++n) {
check<true, bidirectional_iterator<int*>>(range+9, -n, range+9 - n);
check<true, random_access_iterator<int*>>(range+9, -n, range+9 - n);
check<true, contiguous_iterator<int*>>( range+9, -n, range+9 - n);
check<true, int*>( range+9, -n, range+9 - n);
}
return true;
}

View File

@ -13,68 +13,90 @@
#include <iterator>
#include <array>
#include <cassert>
#include <climits>
#include <concepts>
#include <cstddef>
#include "test_iterators.h"
#include "../types.h"
using range_t = std::array<int, 10>;
struct expected_t {
range_t::const_iterator coordinate;
std::ptrdiff_t result;
};
template <std::input_or_output_iterator It>
constexpr void check_forward_sized_sentinel(std::ptrdiff_t n, expected_t expected, range_t& range) {
template <bool Count, typename It>
constexpr void check_forward(int* first, int* last, std::iter_difference_t<It> n, int* expected) {
using Difference = std::iter_difference_t<It>;
auto current = stride_counting_iterator(It(range.begin()));
Difference const result = std::ranges::advance(current, n, distance_apriori_sentinel(range.size()));
assert(current.base().base() == expected.coordinate);
assert(result == expected.result);
Difference const M = (expected - first); // expected travel distance
if constexpr (std::random_access_iterator<It>) {
assert(current.stride_count() == 0 || current.stride_count() == 1);
assert(current.stride_displacement() == current.stride_count());
} else {
assert(current.stride_count() == (n - result));
assert(current.stride_displacement() == (n - result));
{
It it(first);
auto sent = sentinel_wrapper(It(last));
std::same_as<Difference> auto result = std::ranges::advance(it, n, sent);
assert(result == n - M);
assert(base(it) == expected);
}
// Count operations
if constexpr (Count) {
auto it = stride_counting_iterator(It(first));
auto sent = sentinel_wrapper(stride_counting_iterator(It(last)));
(void)std::ranges::advance(it, n, sent);
// We don't have a sized sentinel, so we have to increment one-by-one
// regardless of the iterator category.
assert(it.stride_count() == M);
assert(it.stride_displacement() == M);
}
}
template <std::random_access_iterator It>
constexpr void check_backward_sized_sentinel(std::ptrdiff_t n, expected_t expected, range_t& range) {
template <typename It>
constexpr void check_forward_sized_sentinel(int* first, int* last, std::iter_difference_t<It> n, int* expected) {
using Difference = std::iter_difference_t<It>;
auto current = stride_counting_iterator(It(range.end()));
Difference const result = std::ranges::advance(current, -n, stride_counting_iterator(It(range.begin())));
assert(current.base().base() == expected.coordinate);
assert(result == expected.result);
Difference const size = (last - first);
Difference const M = (expected - first); // expected travel distance
assert(current.stride_count() == 0 || current.stride_count() == 1);
assert(current.stride_displacement() == current.stride_count());
{
It it(first);
auto sent = distance_apriori_sentinel(size);
std::same_as<Difference> auto result = std::ranges::advance(it, n, sent);
assert(result == n - M);
assert(base(it) == expected);
}
// Count operations
{
auto it = stride_counting_iterator(It(first));
auto sent = distance_apriori_sentinel(size);
(void)std::ranges::advance(it, n, sent);
if constexpr (std::random_access_iterator<It>) {
assert(it.stride_count() <= 1);
assert(it.stride_displacement() <= 1);
} else {
assert(it.stride_count() == M);
assert(it.stride_displacement() == M);
}
}
}
template <std::input_or_output_iterator It>
constexpr void check_forward(std::ptrdiff_t n, expected_t expected, range_t& range) {
template <typename It>
constexpr void check_backward(int* first, int* last, std::iter_difference_t<It> n, int* expected) {
static_assert(std::random_access_iterator<It>, "This test doesn't support non random access iterators");
using Difference = std::iter_difference_t<It>;
auto current = stride_counting_iterator(It(range.begin()));
Difference const result = std::ranges::advance(current, n, sentinel_wrapper(It(range.end())));
assert(current.base().base() == expected.coordinate);
assert(result == expected.result);
assert(current.stride_count() == n - result);
}
Difference const M = (expected - last); // expected travel distance (which is negative)
template <std::bidirectional_iterator It>
constexpr void check_backward(std::ptrdiff_t n, expected_t expected, range_t& range) {
using Difference = std::iter_difference_t<It>;
auto current = stride_counting_iterator(It(range.end()));
Difference const result = std::ranges::advance(current, -n, stride_counting_iterator(It(range.begin())));
assert(current.base().base() == expected.coordinate);
assert(result == expected.result);
assert(current.stride_count() == n + result);
assert(current.stride_count() == -current.stride_displacement());
{
It it(last);
It sent(first);
std::same_as<Difference> auto result = std::ranges::advance(it, n, sent);
assert(result == n - M);
assert(base(it) == expected);
}
// Count operations
{
auto it = stride_counting_iterator(It(last));
auto sent = stride_counting_iterator(It(first));
(void)std::ranges::advance(it, n, sent);
assert(it.stride_count() <= 1);
assert(it.stride_displacement() <= 1);
}
}
struct iota_iterator {
@ -95,40 +117,42 @@ static_assert(std::bidirectional_iterator<iota_iterator>);
static_assert(std::sized_sentinel_for<iota_iterator, iota_iterator>);
constexpr bool test() {
auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
check_forward_sized_sentinel<cpp17_input_iterator<range_t::const_iterator> >(1, {range.begin() + 1, 0}, range);
// cpp20_input_iterator not copyable, so is omitted
check_forward_sized_sentinel<forward_iterator<range_t::const_iterator> >(3, {range.begin() + 3, 0}, range);
check_forward_sized_sentinel<bidirectional_iterator<range_t::const_iterator> >(4, {range.begin() + 4, 0}, range);
check_forward_sized_sentinel<random_access_iterator<range_t::const_iterator> >(5, {range.begin() + 5, 0}, range);
check_forward_sized_sentinel<contiguous_iterator<range_t::const_iterator> >(6, {range.begin() + 6, 0}, range);
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
// bidirectional_iterator omitted because the `n < 0` case requires `same_as<I, S>`
check_backward_sized_sentinel<random_access_iterator<range_t::const_iterator> >(5, {range.begin() + 5, 0}, range);
check_backward_sized_sentinel<contiguous_iterator<range_t::const_iterator> >(6, {range.begin() + 4, 0}, range);
for (int size = 0; size != 10; ++size) {
for (int n = 0; n != 20; ++n) {
// distance == range.size()
check_forward_sized_sentinel<forward_iterator<range_t::const_iterator> >(10, {range.end(), 0}, range);
check_backward_sized_sentinel<random_access_iterator<range_t::const_iterator> >(10, {range.begin(), 0}, range);
{
int* expected = n > size ? range + size : range + n;
check_forward<false, cpp17_input_iterator<int*>>( range, range+size, n, expected);
check_forward<false, cpp20_input_iterator<int*>>( range, range+size, n, expected);
check_forward<true, forward_iterator<int*>>( range, range+size, n, expected);
check_forward<true, bidirectional_iterator<int*>>(range, range+size, n, expected);
check_forward<true, random_access_iterator<int*>>(range, range+size, n, expected);
check_forward<true, contiguous_iterator<int*>>( range, range+size, n, expected);
check_forward<true, int*>( range, range+size, n, expected);
// distance > range.size()
check_forward_sized_sentinel<forward_iterator<range_t::const_iterator> >(1000, {range.end(), 990}, range);
check_backward_sized_sentinel<random_access_iterator<range_t::const_iterator> >(1000, {range.begin(), -990}, range);
check_forward_sized_sentinel<cpp17_input_iterator<int*>>( range, range+size, n, expected);
check_forward_sized_sentinel<cpp20_input_iterator<int*>>( range, range+size, n, expected);
check_forward_sized_sentinel<forward_iterator<int*>>( range, range+size, n, expected);
check_forward_sized_sentinel<bidirectional_iterator<int*>>(range, range+size, n, expected);
check_forward_sized_sentinel<random_access_iterator<int*>>(range, range+size, n, expected);
check_forward_sized_sentinel<contiguous_iterator<int*>>( range, range+size, n, expected);
check_forward_sized_sentinel<int*>( range, range+size, n, expected);
}
check_forward<cpp17_input_iterator<range_t::const_iterator> >(1, {range.begin() + 1, 0}, range);
check_forward<forward_iterator<range_t::const_iterator> >(3, {range.begin() + 3, 0}, range);
check_forward<bidirectional_iterator<range_t::const_iterator> >(4, {range.begin() + 4, 0}, range);
check_forward<random_access_iterator<range_t::const_iterator> >(5, {range.begin() + 5, 0}, range);
check_forward<contiguous_iterator<range_t::const_iterator> >(6, {range.begin() + 6, 0}, range);
check_backward<bidirectional_iterator<range_t::const_iterator> >(8, {range.begin() + 2, 0}, range);
// distance == range.size()
check_forward<forward_iterator<range_t::const_iterator> >(10, {range.end(), 0}, range);
check_backward<bidirectional_iterator<range_t::const_iterator> >(10, {range.begin(), 0}, range);
// distance > range.size()
check_forward<forward_iterator<range_t::const_iterator> >(1000, {range.end(), 990}, range);
check_backward<bidirectional_iterator<range_t::const_iterator> >(1000, {range.begin(), -990}, range);
{
// Note that we can only test ranges::advance with a negative n for iterators that
// are sized sentinels for themselves, because ranges::advance is UB otherwise.
// In particular, that excludes bidirectional_iterators since those are not sized sentinels.
// TODO: Enable these tests once we fix the bug in ranges::advance
// int* expected = n > size ? range : range + size - n;
// check_backward<random_access_iterator<int*>>(range, range+size, -n, expected);
// check_backward<contiguous_iterator<int*>>( range, range+size, -n, expected);
// check_backward<int*>( range, range+size, -n, expected);
}
}
}
// regression-test that INT_MIN doesn't cause any undefined behavior
{

View File

@ -13,112 +13,108 @@
#include <iterator>
#include <array>
#include <cassert>
#include <cstddef>
#include "test_iterators.h"
#include "../types.h"
#include "test_iterators.h"
#include "test_macros.h"
using range_t = std::array<int, 10>;
template <class It, class Sent = It>
constexpr void check_assignable_case() {
auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (std::ptrdiff_t n = 0; n != 9; ++n) {
template <bool Count, class It>
constexpr void check_assignable(int* first, int* last, int* expected) {
{
It first(range.begin());
Sent last(It(range.begin() + n));
std::ranges::advance(first, last);
assert(base(first) == range.begin() + n);
It it(first);
auto sent = assignable_sentinel(It(last));
std::ranges::advance(it, sent);
ASSERT_SAME_TYPE(decltype(std::ranges::advance(it, sent)), void);
assert(base(it) == expected);
}
// Count operations
if constexpr (std::is_same_v<It, Sent>) {
stride_counting_iterator<It> first(It(range.begin()));
stride_counting_iterator<It> last(It(range.begin() + n));
std::ranges::advance(first, last);
assert(first.base().base() == range.begin() + n);
assert(first.stride_count() == 0); // because we got here by assigning from last, not by incrementing
}
if constexpr (Count) {
auto it = stride_counting_iterator(It(first));
auto sent = assignable_sentinel(stride_counting_iterator(It(last)));
std::ranges::advance(it, sent);
assert(base(it.base()) == expected);
assert(it.stride_count() == 0); // because we got here by assigning from last, not by incrementing
}
}
template <class It>
constexpr void check_sized_sentinel_case() {
auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
template <bool Count, class It>
constexpr void check_sized_sentinel(int* first, int* last, int* expected) {
auto size = (last - first);
for (std::ptrdiff_t n = 0; n != 9; ++n) {
{
It first(range.begin());
distance_apriori_sentinel last(n);
std::ranges::advance(first, last);
assert(base(first) == range.begin() + n);
It it(first);
auto sent = distance_apriori_sentinel(size);
std::ranges::advance(it, sent);
ASSERT_SAME_TYPE(decltype(std::ranges::advance(it, sent)), void);
assert(base(it) == expected);
}
// Count operations
{
stride_counting_iterator<It> first(It(range.begin()));
distance_apriori_sentinel last(n);
std::ranges::advance(first, last);
assert(first.base().base() == range.begin() + n);
if constexpr (Count) {
auto it = stride_counting_iterator(It(first));
auto sent = distance_apriori_sentinel(size);
std::ranges::advance(it, sent);
if constexpr (std::random_access_iterator<It>) {
assert(first.stride_count() == 1);
assert(first.stride_displacement() == 1);
assert(it.stride_count() == 1);
} else {
assert(first.stride_count() == n);
assert(first.stride_displacement() == n);
}
assert(it.stride_count() == size);
}
}
}
template <class It>
constexpr void check_sentinel_case() {
auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
template <bool Count, class It>
constexpr void check_sentinel(int* first, int* last, int* expected) {
auto size = (last - first);
for (std::ptrdiff_t n = 0; n != 9; ++n) {
{
It first(range.begin());
sentinel_wrapper<It> last(It(range.begin() + n));
std::ranges::advance(first, last);
assert(base(first) == range.begin() + n);
It it(first);
auto sent = sentinel_wrapper(It(last));
std::ranges::advance(it, sent);
ASSERT_SAME_TYPE(decltype(std::ranges::advance(it, sent)), void);
assert(base(it) == expected);
}
// Count operations
{
stride_counting_iterator<It> first(It(range.begin()));
sentinel_wrapper<It> last(It(range.begin() + n));
std::ranges::advance(first, last);
assert(first.base() == last);
assert(first.stride_count() == n);
}
if constexpr (Count) {
auto it = stride_counting_iterator(It(first));
auto sent = sentinel_wrapper(stride_counting_iterator(It(last)));
std::ranges::advance(it, sent);
assert(it.stride_count() == size);
}
}
constexpr bool test() {
using It = range_t::const_iterator;
check_assignable_case<cpp17_input_iterator<It>, sentinel_wrapper<cpp17_input_iterator<It>>>();
check_assignable_case<forward_iterator<It>>();
check_assignable_case<bidirectional_iterator<It>>();
check_assignable_case<random_access_iterator<It>>();
check_assignable_case<contiguous_iterator<It>>();
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
check_sized_sentinel_case<cpp17_input_iterator<It>>();
check_sized_sentinel_case<cpp20_input_iterator<It>>();
check_sized_sentinel_case<forward_iterator<It>>();
check_sized_sentinel_case<bidirectional_iterator<It>>();
check_sized_sentinel_case<random_access_iterator<It>>();
check_sized_sentinel_case<contiguous_iterator<It>>();
for (int n = 0; n != 10; ++n) {
check_assignable<false, cpp17_input_iterator<int*>>( range, range+n, range+n);
check_assignable<false, cpp20_input_iterator<int*>>( range, range+n, range+n);
check_assignable<true, forward_iterator<int*>>( range, range+n, range+n);
check_assignable<true, bidirectional_iterator<int*>>(range, range+n, range+n);
check_assignable<true, random_access_iterator<int*>>(range, range+n, range+n);
check_assignable<true, contiguous_iterator<int*>>( range, range+n, range+n);
check_assignable<true, int*>( range, range+n, range+n);
check_sized_sentinel<false, cpp17_input_iterator<int*>>( range, range+n, range+n);
check_sized_sentinel<false, cpp20_input_iterator<int*>>( range, range+n, range+n);
check_sized_sentinel<true, forward_iterator<int*>>( range, range+n, range+n);
check_sized_sentinel<true, bidirectional_iterator<int*>>(range, range+n, range+n);
check_sized_sentinel<true, random_access_iterator<int*>>(range, range+n, range+n);
check_sized_sentinel<true, contiguous_iterator<int*>>( range, range+n, range+n);
check_sized_sentinel<true, int*>( range, range+n, range+n);
check_sentinel<false, cpp17_input_iterator<int*>>( range, range+n, range+n);
check_sentinel<false, cpp20_input_iterator<int*>>( range, range+n, range+n);
check_sentinel<true, forward_iterator<int*>>( range, range+n, range+n);
check_sentinel<true, bidirectional_iterator<int*>>(range, range+n, range+n);
check_sentinel<true, random_access_iterator<int*>>(range, range+n, range+n);
check_sentinel<true, contiguous_iterator<int*>>( range, range+n, range+n);
check_sentinel<true, int*>( range, range+n, range+n);
}
check_sentinel_case<cpp17_input_iterator<It>>();
// cpp20_input_iterator not copyable, so is omitted
check_sentinel_case<forward_iterator<It>>();
check_sentinel_case<bidirectional_iterator<It>>();
check_sentinel_case<random_access_iterator<It>>();
check_sentinel_case<contiguous_iterator<It>>();
return true;
}

View File

@ -12,29 +12,34 @@
// ranges::next(it)
#include <iterator>
#include <cassert>
#include <concepts>
#include <utility>
#include "test_iterators.h"
template <class It>
constexpr void check() {
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
assert(&*std::ranges::next(It(&range[0])) == &range[1]);
assert(&*std::ranges::next(It(&range[1])) == &range[2]);
assert(&*std::ranges::next(It(&range[2])) == &range[3]);
assert(&*std::ranges::next(It(&range[3])) == &range[4]);
assert(&*std::ranges::next(It(&range[4])) == &range[5]);
assert(&*std::ranges::next(It(&range[5])) == &range[6]);
constexpr void check(int* first, int* expected) {
It it(first);
std::same_as<It> auto result = std::ranges::next(std::move(it));
assert(base(result) == expected);
}
constexpr bool test() {
check<cpp17_input_iterator<int*>>();
check<cpp20_input_iterator<int*>>();
check<forward_iterator<int*>>();
check<bidirectional_iterator<int*>>();
check<random_access_iterator<int*>>();
check<contiguous_iterator<int*>>();
check<output_iterator<int*>>();
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int n = 0; n != 9; ++n) {
check<cpp17_input_iterator<int*>>( range+n, range+n+1);
check<cpp20_input_iterator<int*>>( range+n, range+n+1);
check<forward_iterator<int*>>( range+n, range+n+1);
check<bidirectional_iterator<int*>>(range+n, range+n+1);
check<random_access_iterator<int*>>(range+n, range+n+1);
check<contiguous_iterator<int*>>( range+n, range+n+1);
check<output_iterator<int*>>( range+n, range+n+1);
check<int*>( range+n, range+n+1);
}
return true;
}

View File

@ -14,55 +14,41 @@
#include <iterator>
#include <cassert>
#include <concepts>
#include <utility>
#include "test_iterators.h"
template <std::input_or_output_iterator It>
constexpr void check_steps(It it, std::ptrdiff_t n, int const* expected) {
{
It result = std::ranges::next(std::move(it), n);
assert(&*result == expected);
}
// Count the number of operations
{
stride_counting_iterator strided_it(std::move(it));
stride_counting_iterator result = std::ranges::next(std::move(strided_it), n);
assert(&*result == expected);
if constexpr (std::random_access_iterator<It>) {
assert(result.stride_count() == 1); // uses += exactly once
assert(result.stride_displacement() == 1);
} else {
auto const abs_n = n < 0 ? -n : n;
assert(result.stride_count() == abs_n);
assert(result.stride_displacement() == n);
}
}
template <typename It>
constexpr void check(int* first, std::iter_difference_t<It> n, int* expected) {
It it(first);
std::same_as<It> auto result = std::ranges::next(std::move(it), n);
assert(base(result) == expected);
}
constexpr bool test() {
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
check_steps(cpp17_input_iterator(&range[0]), 1, &range[1]);
check_steps(cpp20_input_iterator(&range[6]), 2, &range[8]);
check_steps(forward_iterator(&range[0]), 3, &range[3]);
check_steps(bidirectional_iterator(&range[2]), 6, &range[8]);
check_steps(random_access_iterator(&range[3]), 4, &range[7]);
check_steps(contiguous_iterator(&range[0]), 5, &range[5]);
check_steps(output_iterator(&range[0]), 6, &range[6]);
// Check next() forward
for (int n = 0; n != 10; ++n) {
check<cpp17_input_iterator<int*>>( range, n, range+n);
check<cpp20_input_iterator<int*>>( range, n, range+n);
check<forward_iterator<int*>>( range, n, range+n);
check<bidirectional_iterator<int*>>(range, n, range+n);
check<random_access_iterator<int*>>(range, n, range+n);
check<contiguous_iterator<int*>>( range, n, range+n);
check<int*>( range, n, range+n);
check<output_iterator<int*> >( range, n, range+n);
}
check_steps(cpp17_input_iterator(&range[0]), 0, &range[0]);
check_steps(cpp20_input_iterator(&range[6]), 0, &range[6]);
check_steps(forward_iterator(&range[0]), 0, &range[0]);
check_steps(bidirectional_iterator(&range[2]), 0, &range[2]);
check_steps(random_access_iterator(&range[3]), 0, &range[3]);
check_steps(contiguous_iterator(&range[0]), 0, &range[0]);
check_steps(output_iterator(&range[0]), 0, &range[0]);
// Check next() backward
for (int n = 0; n != 10; ++n) {
check<bidirectional_iterator<int*>>(range+9, -n, range+9 - n);
check<random_access_iterator<int*>>(range+9, -n, range+9 - n);
check<contiguous_iterator<int*>>( range+9, -n, range+9 - n);
check<int*>( range+9, -n, range+9 - n);
}
check_steps(bidirectional_iterator(&range[8]), -5, &range[3]);
check_steps(random_access_iterator(&range[6]), -3, &range[3]);
check_steps(contiguous_iterator(&range[4]), -1, &range[3]);
return true;
}

View File

@ -12,56 +12,38 @@
// ranges::next(it, n, bound)
#include <iterator>
#include <cassert>
#include <type_traits>
#include <concepts>
#include <utility>
#include "test_iterators.h"
template <class It, class Sent = It>
constexpr void check(It it, std::ptrdiff_t n, Sent last) {
{
It result = std::ranges::next(it, n, last);
assert(result == last);
}
template <typename It>
constexpr void check(int* first, int* last, std::iter_difference_t<It> n, int* expected) {
It it(first);
auto sent = sentinel_wrapper(It(last));
// Count the number of operations
if constexpr (std::is_same_v<It, Sent>) {
stride_counting_iterator<It> strided_it(it);
stride_counting_iterator<It> strided_last(last);
stride_counting_iterator<It> result = std::ranges::next(strided_it, n, strided_last);
assert(result == strided_last);
if constexpr (std::random_access_iterator<It>) {
if (n == 0 || n >= (last - it)) {
assert(result.stride_count() == 0); // uses the assign-from-sentinel codepath
} else {
assert(result.stride_count() == 1); // uses += exactly once
}
} else {
std::ptrdiff_t const abs_n = n < 0 ? -n : n;
assert(result.stride_count() == abs_n);
assert(result.stride_displacement() == n);
}
}
std::same_as<It> auto result = std::ranges::next(std::move(it), n, sent);
assert(base(result) == expected);
}
constexpr bool test() {
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
check(cpp17_input_iterator(&range[0]), 1, sentinel_wrapper(cpp17_input_iterator(&range[1])));
check(forward_iterator(&range[0]), 2, forward_iterator(&range[2]));
check(bidirectional_iterator(&range[2]), 6, bidirectional_iterator(&range[8]));
check(random_access_iterator(&range[3]), 2, random_access_iterator(&range[5]));
check(contiguous_iterator(&range[0]), 5, contiguous_iterator(&range[5]));
for (int size = 0; size != 10; ++size) {
for (int n = 0; n != 20; ++n) {
int* expected = n > size ? range + size : range + n;
check<cpp17_input_iterator<int*>>( range, range+size, n, expected);
check<cpp20_input_iterator<int*>>( range, range+size, n, expected);
check<forward_iterator<int*>>( range, range+size, n, expected);
check<bidirectional_iterator<int*>>(range, range+size, n, expected);
check<random_access_iterator<int*>>(range, range+size, n, expected);
check<contiguous_iterator<int*>>( range, range+size, n, expected);
check<int*>( range, range+size, n, expected);
}
}
check(cpp17_input_iterator(&range[0]), 0, sentinel_wrapper(cpp17_input_iterator(&range[0])));
check(forward_iterator(&range[0]), 0, forward_iterator(&range[0]));
check(bidirectional_iterator(&range[2]), 0, bidirectional_iterator(&range[2]));
check(random_access_iterator(&range[3]), 0, random_access_iterator(&range[3]));
check(contiguous_iterator(&range[0]), 0, contiguous_iterator(&range[0]));
check(bidirectional_iterator(&range[6]), -1, bidirectional_iterator(&range[5]));
check(random_access_iterator(&range[7]), -2, random_access_iterator(&range[5]));
check(contiguous_iterator(&range[8]), -3, contiguous_iterator(&range[5]));
return true;
}

View File

@ -13,101 +13,68 @@
#include <iterator>
#include <array>
#include <cassert>
#include <concepts>
#include <utility>
#include "test_iterators.h"
#include "../types.h"
#include "test_iterators.h"
using range_t = std::array<int, 10>;
template <bool Count, typename It>
constexpr void check_assignable(int* it, int* last, int const* expected) {
{
It result = std::ranges::next(It(it), assignable_sentinel(It(last)));
template <typename It>
constexpr void check_assignable(int* first, int* last, int* expected) {
It it(first);
auto sent = assignable_sentinel(It(last));
It result = std::ranges::next(std::move(it), sent);
assert(base(result) == expected);
}
// Count operations
if constexpr (Count) {
auto strided_it = stride_counting_iterator(It(it));
auto strided_last = assignable_sentinel(stride_counting_iterator(It(last)));
stride_counting_iterator<It> result = std::ranges::next(std::move(strided_it), std::move(strided_last));
assert(base(result.base()) == expected);
assert(result.stride_count() == 0); // because we got here by assigning from last, not by incrementing
}
}
template <typename It>
constexpr void check_sized_sentinel(int* it, int* last, int const* expected) {
auto n = (last - it);
constexpr void check_sized_sentinel(int* first, int* last, int* expected) {
auto size = (last - first);
{
auto sent = distance_apriori_sentinel(n);
auto result = std::ranges::next(It(it), sent);
It it(first);
auto sent = distance_apriori_sentinel(size);
std::same_as<It> auto result = std::ranges::next(std::move(it), sent);
assert(base(result) == expected);
}
// Count operations
{
auto strided_it = stride_counting_iterator(It(it));
auto sent = distance_apriori_sentinel(n);
auto result = std::ranges::next(std::move(strided_it), sent);
assert(base(result.base()) == expected);
if constexpr (std::random_access_iterator<It>) {
assert(result.stride_count() == 1); // should have used exactly one +=
assert(result.stride_displacement() == 1);
} else {
assert(result.stride_count() == n);
assert(result.stride_displacement() == n);
}
}
}
template <bool Count, typename It>
constexpr void check_sentinel(int* it, int* last, int const* expected) {
auto n = (last - it);
{
template <typename It>
constexpr void check_sentinel(int* first, int* last, int* expected) {
It it(first);
auto sent = sentinel_wrapper(It(last));
It result = std::ranges::next(It(it), sent);
std::same_as<It> auto result = std::ranges::next(std::move(it), sent);
assert(base(result) == expected);
}
// Count operations
if constexpr (Count) {
auto strided_it = stride_counting_iterator(It(it));
auto sent = sentinel_wrapper(stride_counting_iterator(It(last)));
stride_counting_iterator result = std::ranges::next(std::move(strided_it), sent);
assert(base(result.base()) == expected);
assert(result.stride_count() == n); // must have used ++ until it hit the sentinel
}
}
constexpr bool test() {
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
check_assignable<false, cpp17_input_iterator<int*>>( &range[0], &range[2], &range[2]);
check_assignable<true, forward_iterator<int*>>( &range[0], &range[3], &range[3]);
check_assignable<true, bidirectional_iterator<int*>>(&range[0], &range[4], &range[4]);
check_assignable<true, random_access_iterator<int*>>(&range[0], &range[5], &range[5]);
check_assignable<true, contiguous_iterator<int*>>( &range[0], &range[6], &range[6]);
for (int n = 0; n != 10; ++n) {
check_assignable<cpp17_input_iterator<int*>>( range, range+n, range+n);
check_assignable<cpp20_input_iterator<int*>>( range, range+n, range+n);
check_assignable<forward_iterator<int*>>( range, range+n, range+n);
check_assignable<bidirectional_iterator<int*>>(range, range+n, range+n);
check_assignable<random_access_iterator<int*>>(range, range+n, range+n);
check_assignable<contiguous_iterator<int*>>( range, range+n, range+n);
check_assignable<int*>( range, range+n, range+n);
check_sized_sentinel<cpp17_input_iterator<int*>>( &range[0], &range[7], &range[7]);
check_sized_sentinel<cpp20_input_iterator<int*>>( &range[0], &range[6], &range[6]);
check_sized_sentinel<forward_iterator<int*>>( &range[0], &range[5], &range[5]);
check_sized_sentinel<bidirectional_iterator<int*>>(&range[0], &range[4], &range[4]);
check_sized_sentinel<random_access_iterator<int*>>(&range[0], &range[3], &range[3]);
check_sized_sentinel<contiguous_iterator<int*>>( &range[0], &range[2], &range[2]);
check_sized_sentinel<cpp17_input_iterator<int*>>( range, range+n, range+n);
check_sized_sentinel<cpp20_input_iterator<int*>>( range, range+n, range+n);
check_sized_sentinel<forward_iterator<int*>>( range, range+n, range+n);
check_sized_sentinel<bidirectional_iterator<int*>>(range, range+n, range+n);
check_sized_sentinel<random_access_iterator<int*>>(range, range+n, range+n);
check_sized_sentinel<contiguous_iterator<int*>>( range, range+n, range+n);
check_sized_sentinel<int*>( range, range+n, range+n);
check_sentinel<cpp17_input_iterator<int*>>( range, range+n, range+n);
check_sentinel<cpp20_input_iterator<int*>>( range, range+n, range+n);
check_sentinel<forward_iterator<int*>>( range, range+n, range+n);
check_sentinel<bidirectional_iterator<int*>>(range, range+n, range+n);
check_sentinel<random_access_iterator<int*>>(range, range+n, range+n);
check_sentinel<contiguous_iterator<int*>>( range, range+n, range+n);
check_sentinel<int*>( range, range+n, range+n);
}
check_sentinel<false, cpp17_input_iterator<int*>>( &range[0], &range[1], &range[1]);
// cpp20_input_iterator not copyable, so is omitted
check_sentinel<true, forward_iterator<int*>>( &range[0], &range[3], &range[3]);
check_sentinel<true, bidirectional_iterator<int*>>(&range[0], &range[4], &range[4]);
check_sentinel<true, random_access_iterator<int*>>(&range[0], &range[5], &range[5]);
check_sentinel<true, contiguous_iterator<int*>>( &range[0], &range[6], &range[6]);
return true;
}

View File

@ -17,23 +17,26 @@
#include "test_iterators.h"
template <class It>
constexpr void check() {
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
assert(std::ranges::prev(It(&range[1])) == It(&range[0]));
assert(std::ranges::prev(It(&range[4])) == It(&range[3]));
assert(std::ranges::prev(It(&range[5])) == It(&range[4]));
assert(std::ranges::prev(It(&range[6])) == It(&range[5]));
assert(std::ranges::prev(It(&range[10])) == It(&range[9]));
constexpr void check(int* first, int* expected) {
It it(first);
std::same_as<It> auto result = std::ranges::prev(std::move(it));
assert(base(result) == expected);
}
constexpr bool test() {
check<bidirectional_iterator<int*>>();
check<random_access_iterator<int*>>();
check<contiguous_iterator<int*>>();
check<int*>();
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int n = 1; n != 10; ++n) {
check<bidirectional_iterator<int*>>(range+n, range+n-1);
check<random_access_iterator<int*>>(range+n, range+n-1);
check<contiguous_iterator<int*>>( range+n, range+n-1);
check<int*>( range+n, range+n-1);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());

View File

@ -12,40 +12,39 @@
// ranges::prev(it, n)
#include <iterator>
#include <cassert>
#include <concepts>
#include <utility>
#include "test_iterators.h"
template <std::input_or_output_iterator It>
constexpr void check(It it, std::ptrdiff_t n, int const* expected) {
stride_counting_iterator result = std::ranges::prev(stride_counting_iterator(std::move(it)), n);
assert(result.base().base() == expected);
if constexpr (std::random_access_iterator<It>) {
assert(result.stride_count() <= 1);
// we can't say anything about the stride displacement, cause we could be using -= or +=.
} else {
std::ptrdiff_t const abs_n = n < 0 ? -n : n;
assert(result.stride_count() == abs_n);
assert(result.stride_displacement() == -n);
}
template <typename It>
constexpr void check(int* first, std::iter_difference_t<It> n, int* expected) {
It it(first);
std::same_as<It> auto result = std::ranges::prev(std::move(it), n);
assert(base(result) == expected);
}
constexpr bool test() {
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
check(bidirectional_iterator(&range[8]), 6, &range[2]);
check(random_access_iterator(&range[7]), 4, &range[3]);
check(contiguous_iterator(&range[5]), 5, &range[0]);
// Check prev() forward
for (int n = 0; n != 10; ++n) {
check<bidirectional_iterator<int*>>(range+n, n, range);
check<random_access_iterator<int*>>(range+n, n, range);
check<contiguous_iterator<int*>>( range+n, n, range);
check<int*>( range+n, n, range);
}
check(bidirectional_iterator(&range[2]), 0, &range[2]);
check(random_access_iterator(&range[3]), 0, &range[3]);
check(contiguous_iterator(&range[0]), 0, &range[0]);
// Check prev() backward
for (int n = 0; n != 10; ++n) {
check<bidirectional_iterator<int*>>(range, -n, range+n);
check<random_access_iterator<int*>>(range, -n, range+n);
check<contiguous_iterator<int*>>( range, -n, range+n);
check<int*>( range, -n, range+n);
}
check(bidirectional_iterator(&range[3]), -5, &range[8]);
check(random_access_iterator(&range[3]), -3, &range[6]);
check(contiguous_iterator(&range[3]), -1, &range[4]);
return true;
}

View File

@ -12,52 +12,38 @@
// ranges::prev(it, n, bound)
#include <iterator>
#include <cassert>
#include <concepts>
#include <utility>
#include "test_iterators.h"
template <std::bidirectional_iterator It>
constexpr void check(It it, std::ptrdiff_t n, It last) {
auto abs = [](auto x) { return x < 0 ? -x : x; };
template <typename It>
constexpr void check(int* first, int* last, std::iter_difference_t<It> n, int* expected) {
It it(last);
It sent(first); // for std::ranges::prev, the sentinel *must* have the same type as the iterator
{
It result = std::ranges::prev(it, n, last);
assert(result == last);
}
// Count the number of operations
{
stride_counting_iterator<It> strided_it(it);
stride_counting_iterator<It> strided_last(last);
stride_counting_iterator<It> result = std::ranges::prev(strided_it, n, strided_last);
assert(result == strided_last);
if constexpr (std::random_access_iterator<It>) {
if (n == 0 || abs(n) >= abs(last - it)) {
assert(result.stride_count() == 0); // uses the assign-from-sentinel codepath
} else {
assert(result.stride_count() == 1); // uses += exactly once
}
} else {
assert(result.stride_count() == abs(n));
assert(result.stride_displacement() == -n);
}
}
std::same_as<It> auto result = std::ranges::prev(std::move(it), n, std::move(sent));
assert(base(result) == expected);
}
// TODO: Re-enable once we fix the bug in ranges::advance
constexpr bool test() {
#if 0
int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
check(bidirectional_iterator(&range[8]), 6, bidirectional_iterator(&range[2]));
check(random_access_iterator(&range[5]), 2, random_access_iterator(&range[3]));
check(contiguous_iterator(&range[5]), 5, contiguous_iterator(&range[0]));
for (int size = 0; size != 10; ++size) {
for (int n = 0; n != 20; ++n) {
int* expected = n > size ? range : range + size - n;
check<bidirectional_iterator<int*>>(range, range+size, n, expected);
check<random_access_iterator<int*>>(range, range+size, n, expected);
check<contiguous_iterator<int*>>( range, range+size, n, expected);
check<int*>( range, range+size, n, expected);
}
}
#endif
check(bidirectional_iterator(&range[2]), 0, bidirectional_iterator(&range[2]));
check(random_access_iterator(&range[3]), 0, random_access_iterator(&range[3]));
check(contiguous_iterator(&range[0]), 0, contiguous_iterator(&range[0]));
check(bidirectional_iterator(&range[5]), -1, bidirectional_iterator(&range[6]));
check(random_access_iterator(&range[5]), -2, random_access_iterator(&range[7]));
check(contiguous_iterator(&range[5]), -3, contiguous_iterator(&range[8]));
return true;
}

View File

@ -14,6 +14,8 @@
#include <iterator>
#include <utility>
#include "test_iterators.h" // for the fallthrough base() function
class distance_apriori_sentinel {
public:
distance_apriori_sentinel() = default;