#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() {
static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>);
static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>);
static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>);
static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFrom>);
static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotIndirectlyReadable>);
static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotInputOrOutputIterator>);
return true;
}
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);
{
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);
}
{
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) {
test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{0, 5, 12, 7, -1, 8, 26}, validate);
test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array<int, 0>{}, validate);
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) {
test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(
std::array{true, false, false, true, false, true, true, true, false, true}, validate);
test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array<bool, 0>{}, validate);
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, 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);
} catch (int) {
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
}
#endif
}
#endif