llvm-project/libcxx/test/std/containers/sequences/from_range_sequence_containers.h
varconst 17bbb224f9 [libc++][ranges] Implement the changes to vector from P1206 (ranges::to):
- add the `from_range_t` constructors and the related deduction guides;
- add the `insert_range`/`assign_range`/etc. member functions.
(Note: this patch is split from https://reviews.llvm.org/D142335)

Differential Revision: https://reviews.llvm.org/D149826
2023-05-08 23:40:55 -07:00

162 lines
5.3 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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_FROM_RANGE_SEQUENCE_CONTAINERS_H
#define SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <ranges>
#include <utility>
#include "../from_range_helpers.h"
#include "MoveOnly.h"
#include "almost_satisfies_types.h"
#include "count_new.h"
#include "test_iterators.h"
#include "test_macros.h"
template <class T>
concept HasSize = requires (const T& value) { value.size(); };
template <class Container, class Range>
concept HasFromRangeCtr = requires (Range&& range) {
Container(std::from_range, std::forward<Range>(range));
Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>());
};
template <template <class...> class Container, class T, class U>
constexpr bool test_constraints() {
// Input range with the same value type.
static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>);
// Input range with a convertible value type.
static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>);
// Input range with a non-convertible value type.
static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>);
// Not an input range.
static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFrom>);
static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotIndirectlyReadable>);
static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotInputOrOutputIterator>);
// Note: there are no constraints on the allocator (it's not a separate template type of the constructor)`.
return true;
}
// Note: `std::array` is used to avoid dealing with `vector<bool>`.
template <template <class ...> class Container,
class T,
class Iter,
class Sent,
class Alloc,
std::size_t N,
class ValidateFunc>
constexpr void test_sequence_container_with_input(std::array<T, N>&& input, ValidateFunc validate) {
auto in = wrap_input<Iter, Sent>(input);
{ // (range)
Container<T> c(std::from_range, in);
if constexpr (HasSize<Container<T>>) {
assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
}
assert(std::ranges::equal(in, c));
validate(c);
}
{ // (range, allocator)
Alloc alloc;
Container<T, Alloc> c(std::from_range, in, alloc);
assert(c.get_allocator() == alloc);
if constexpr (HasSize<Container<T, Alloc>>) {
assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
}
assert(std::ranges::equal(in, c));
validate(c);
}
}
template <template <class ...> class Container,
class T,
class Iter,
class Sent,
class Alloc,
class ValidateFunc>
constexpr void test_sequence_container(ValidateFunc validate) {
// Normal input.
test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{0, 5, 12, 7, -1, 8, 26}, validate);
// Empty input.
test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array<int, 0>{}, validate);
// Single-element input.
test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{5}, validate);
}
template <template <class ...> class Container>
constexpr void test_sequence_container_move_only() {
MoveOnly input[5];
std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
[[maybe_unused]] Container<MoveOnly> c(std::from_range, in);
}
template <class Iter,
class Sent,
class Alloc,
class ValidateFunc>
constexpr void test_vector_bool(ValidateFunc validate) {
// Normal input.
test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(
std::array{true, false, false, true, false, true, true, true, false, true}, validate);
// Empty input.
test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array<bool, 0>{}, validate);
// Single-element input.
test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array{true}, validate);
}
template <template <class ...> class Container>
void test_exception_safety_throwing_copy() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
using T = ThrowingCopy<3>;
T::reset();
T in[5];
try {
Container<T> c(std::from_range, in);
assert(false); // The constructor call above should throw.
} catch (int) {
assert(T::created_by_copying == 3);
assert(T::destroyed == 2); // No destructor call for the partially-constructed element.
}
#endif
}
template <template <class ...> class Container, class T>
void test_exception_safety_throwing_allocator() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
T in[] = {0, 1};
try {
ThrowingAllocator<T> alloc;
globalMemCounter.reset();
Container<T, ThrowingAllocator<T>> c(std::from_range, in, alloc);
assert(false); // The constructor call above should throw.
} catch (int) {
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
}
#endif
}
#endif // SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H