148 lines · c
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H10#define SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H11 12#include <algorithm>13#include <array>14#include <cassert>15#include <cstddef>16#include <iterator>17#include <ranges>18#include <utility>19 20#include "../exception_safety_helpers.h"21#include "../from_range_helpers.h"22#include "MoveOnly.h"23#include "almost_satisfies_types.h"24#include "count_new.h"25#include "test_iterators.h"26#include "test_macros.h"27 28template <class T>29concept HasSize = requires(const T& value) { value.size(); };30 31template <class Container, class Range>32concept HasFromRangeCtr = requires(Range&& range) {33 Container(std::from_range, std::forward<Range>(range));34 Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>());35};36 37template <template <class...> class Container, class T, class U>38constexpr bool test_constraints() {39 // Input range with the same value type.40 static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>);41 // Input range with a convertible value type.42 static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>);43 // Input range with a non-convertible value type.44 static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>);45 // Not an input range.46 static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFrom>);47 static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotIndirectlyReadable>);48 static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotInputOrOutputIterator>);49 50 // Note: there are no constraints on the allocator (it's not a separate template type of the constructor)`.51 52 return true;53}54 55// Note: `std::array` is used to avoid dealing with `vector<bool>`.56template <template <class...> class Container,57 class T,58 class Iter,59 class Sent,60 class Alloc,61 std::size_t N,62 class ValidateFunc>63constexpr void test_sequence_container_with_input(std::array<T, N>&& input, ValidateFunc validate) {64 { // (range)65 auto in = wrap_input<Iter, Sent>(input);66 Container<T> c(std::from_range, in);67 68 if constexpr (HasSize<Container<T>>) {69 assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));70 }71 assert(std::ranges::equal(input, c));72 validate(c);73 }74 75 { // (range, allocator)76 auto in = wrap_input<Iter, Sent>(input);77 Alloc alloc;78 Container<T, Alloc> c(std::from_range, in, alloc);79 80 assert(c.get_allocator() == alloc);81 if constexpr (HasSize<Container<T, Alloc>>) {82 assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));83 }84 assert(std::ranges::equal(input, c));85 validate(c);86 }87}88 89template <template <class...> class Container, class T, class Iter, class Sent, class Alloc, class ValidateFunc>90constexpr void test_sequence_container(ValidateFunc validate) {91 // Normal input.92 test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{0, 5, 12, 7, -1, 8, 26}, validate);93 // Empty input.94 test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array<int, 0>{}, validate);95 // Single-element input.96 test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{5}, validate);97}98 99template <template <class...> class Container>100constexpr void test_sequence_container_move_only() {101 MoveOnly input[5];102 std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});103 104 [[maybe_unused]] Container<MoveOnly> c(std::from_range, in);105}106 107template <class Iter, class Sent, class Alloc, class ValidateFunc>108constexpr void test_vector_bool(ValidateFunc validate) {109 // Normal input.110 test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(111 std::array{true, false, false, true, false, true, true, true, false, true}, validate);112 // Empty input.113 test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array<bool, 0>{}, validate);114 // Single-element input.115 test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array{true}, validate);116}117 118template <template <class...> class Container>119void test_exception_safety_throwing_copy() {120#if !defined(TEST_HAS_NO_EXCEPTIONS)121 constexpr int ThrowOn = 3;122 using T = ThrowingCopy<ThrowOn>;123 test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) {124 [[maybe_unused]] Container<T> c(std::from_range, std::ranges::subrange(from, to));125 });126#endif127}128 129template <template <class...> class Container, class T>130void test_exception_safety_throwing_allocator() {131#if !defined(TEST_HAS_NO_EXCEPTIONS)132 T in[] = {0, 1};133 134 try {135 ThrowingAllocator<T> alloc;136 137 globalMemCounter.reset();138 Container<T, ThrowingAllocator<T>> c(std::from_range, in, alloc);139 assert(false); // The constructor call above should throw.140 141 } catch (int) {142 assert(globalMemCounter.new_called == globalMemCounter.delete_called);143 }144#endif145}146 147#endif // SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H148