blob: 859b9c535f7ba3714f4abdd5f2f3b418dd86ede6 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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 "../exception_safety_helpers.h"
#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)
constexpr int ThrowOn = 3;
using T = ThrowingCopy<ThrowOn>;
test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) {
[[maybe_unused]] Container<T> c(std::from_range, std::ranges::subrange(from, to));
});
#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