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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H 10 #define SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H 11 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 28 template <class T> 29 concept HasSize = requires (const T& value) { value.size(); }; 30 31 template <class Container, class Range> 32 concept 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 37 template <template <class...> class Container, class T, class U> 38 constexpr 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>`. 56 template <template <class ...> class Container, 57 class T, 58 class Iter, 59 class Sent, 60 class Alloc, 61 std::size_t N, 62 class ValidateFunc> 63 constexpr 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 89 template <template <class ...> class Container, 90 class T, 91 class Iter, 92 class Sent, 93 class Alloc, 94 class ValidateFunc> 95 constexpr void test_sequence_container(ValidateFunc validate) { 96 // Normal input. 97 test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{0, 5, 12, 7, -1, 8, 26}, validate); 98 // Empty input. 99 test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array<int, 0>{}, validate); 100 // Single-element input. 101 test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{5}, validate); 102 } 103 104 template <template <class ...> class Container> 105 constexpr void test_sequence_container_move_only() { 106 MoveOnly input[5]; 107 std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5}); 108 109 [[maybe_unused]] Container<MoveOnly> c(std::from_range, in); 110 } 111 112 template <class Iter, 113 class Sent, 114 class Alloc, 115 class ValidateFunc> 116 constexpr void test_vector_bool(ValidateFunc validate) { 117 // Normal input. 118 test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>( 119 std::array{true, false, false, true, false, true, true, true, false, true}, validate); 120 // Empty input. 121 test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array<bool, 0>{}, validate); 122 // Single-element input. 123 test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array{true}, validate); 124 } 125 126 template <template <class ...> class Container> 127 void test_exception_safety_throwing_copy() { 128 #if !defined(TEST_HAS_NO_EXCEPTIONS) 129 constexpr int ThrowOn = 3; 130 using T = ThrowingCopy<ThrowOn>; 131 test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) { 132 [[maybe_unused]] Container<T> c(std::from_range, std::ranges::subrange(from, to)); 133 }); 134 #endif 135 } 136 137 template <template <class ...> class Container, class T> 138 void test_exception_safety_throwing_allocator() { 139 #if !defined(TEST_HAS_NO_EXCEPTIONS) 140 T in[] = {0, 1}; 141 142 try { 143 ThrowingAllocator<T> alloc; 144 145 globalMemCounter.reset(); 146 Container<T, ThrowingAllocator<T>> c(std::from_range, in, alloc); 147 assert(false); // The constructor call above should throw. 148 149 } catch (int) { 150 assert(globalMemCounter.new_called == globalMemCounter.delete_called); 151 } 152 #endif 153 } 154 155 #endif // SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H 156