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_CONTAINER_ADAPTORS_H |
10 | #define SUPPORT_FROM_RANGE_CONTAINER_ADAPTORS_H |
11 | |
12 | #include <algorithm> |
13 | #include <cassert> |
14 | #include <cstddef> |
15 | #include <queue> |
16 | #include <ranges> |
17 | #include <utility> |
18 | #include <vector> |
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_macros.h" |
26 | #include "unwrap_container_adaptor.h" |
27 | |
28 | template <class Container, class Range> |
29 | concept HasFromRangeCtr = requires (Range&& range) { |
30 | Container(std::from_range, std::forward<Range>(range)); |
31 | Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>()); |
32 | }; |
33 | |
34 | template <template <class...> class Container, class T, class U> |
35 | constexpr bool test_constraints() { |
36 | // Input range with the same value type. |
37 | static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>); |
38 | // Input range with a convertible value type. |
39 | static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>); |
40 | // Input range with a non-convertible value type. |
41 | static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>); |
42 | // Not an input range. |
43 | static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFrom>); |
44 | static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotIndirectlyReadable>); |
45 | static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotInputOrOutputIterator>); |
46 | |
47 | return true; |
48 | } |
49 | |
50 | template <template <class ...> class Adaptor, |
51 | template <class ...> class UnderlyingContainer, |
52 | class T, |
53 | class Iter, |
54 | class Sent, |
55 | class Alloc> |
56 | constexpr void test_container_adaptor_with_input(std::vector<T>&& input) { |
57 | auto b = Iter(input.data()); |
58 | auto e = Iter(input.data() + input.size()); |
59 | std::ranges::subrange in(std::move(b), Sent(std::move(e))); |
60 | |
61 | { // (range) |
62 | Adaptor<T> adaptor(std::from_range, in); |
63 | UnwrapAdaptor<Adaptor<T>> unwrap_adaptor(std::move(adaptor)); |
64 | auto& c = unwrap_adaptor.get_container(); |
65 | |
66 | assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); |
67 | assert(std::ranges::equal(in, c)); |
68 | LIBCPP_ASSERT(c.__invariants()); |
69 | } |
70 | |
71 | { // (range, allocator) |
72 | using C = UnderlyingContainer<T, Alloc>; |
73 | Alloc alloc; |
74 | Adaptor<T, C> adaptor(std::from_range, in, alloc); |
75 | UnwrapAdaptor<Adaptor<T, C>> unwrap_adaptor(std::move(adaptor)); |
76 | auto& c = unwrap_adaptor.get_container(); |
77 | |
78 | assert(c.get_allocator() == alloc); |
79 | assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); |
80 | assert(std::ranges::equal(in, c)); |
81 | LIBCPP_ASSERT(c.__invariants()); |
82 | } |
83 | } |
84 | |
85 | template <template <class ...> class UnderlyingContainer, |
86 | class T, |
87 | class Iter, |
88 | class Sent, |
89 | class Comp, |
90 | class Alloc> |
91 | constexpr void test_priority_queue_with_input(std::vector<T>&& input) { |
92 | auto b = Iter(input.data()); |
93 | auto e = Iter(input.data() + input.size()); |
94 | std::ranges::subrange in(std::move(b), Sent(std::move(e))); |
95 | |
96 | { // (range) |
97 | std::priority_queue<T> adaptor(std::from_range, in); |
98 | UnwrapAdaptor<std::priority_queue<T>> unwrap_adaptor(std::move(adaptor)); |
99 | auto& c = unwrap_adaptor.get_container(); |
100 | |
101 | assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); |
102 | assert(std::ranges::is_permutation(input, c)); |
103 | LIBCPP_ASSERT(c.__invariants()); |
104 | } |
105 | |
106 | { // (range, comp) |
107 | using C = UnderlyingContainer<T>; |
108 | Comp comp; |
109 | |
110 | std::priority_queue<T, C, Comp> adaptor(std::from_range, in, comp); |
111 | UnwrapAdaptor<std::priority_queue<T, C, Comp>> unwrap_adaptor(std::move(adaptor)); |
112 | auto& c = unwrap_adaptor.get_container(); |
113 | |
114 | assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); |
115 | assert(std::ranges::is_permutation(input, c)); |
116 | LIBCPP_ASSERT(c.__invariants()); |
117 | assert(unwrap_adaptor.get_comparator() == comp); |
118 | } |
119 | |
120 | { // (range, allocator) |
121 | using C = UnderlyingContainer<T, Alloc>; |
122 | Alloc alloc; |
123 | |
124 | std::priority_queue<T, C> adaptor(std::from_range, in, alloc); |
125 | UnwrapAdaptor<std::priority_queue<T, C>> unwrap_adaptor(std::move(adaptor)); |
126 | auto& c = unwrap_adaptor.get_container(); |
127 | |
128 | assert(c.get_allocator() == alloc); |
129 | assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); |
130 | assert(std::ranges::is_permutation(input, c)); |
131 | LIBCPP_ASSERT(c.__invariants()); |
132 | } |
133 | |
134 | { // (range, comp, alloc) |
135 | using C = UnderlyingContainer<T, Alloc>; |
136 | Comp comp; |
137 | Alloc alloc; |
138 | |
139 | std::priority_queue<T, C, Comp> adaptor(std::from_range, in, comp, alloc); |
140 | UnwrapAdaptor<std::priority_queue<T, C, Comp>> unwrap_adaptor(std::move(adaptor)); |
141 | auto& c = unwrap_adaptor.get_container(); |
142 | |
143 | assert(c.get_allocator() == alloc); |
144 | assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); |
145 | assert(std::ranges::is_permutation(input, c)); |
146 | LIBCPP_ASSERT(c.__invariants()); |
147 | assert(unwrap_adaptor.get_comparator() == comp); |
148 | } |
149 | } |
150 | |
151 | template <template <class ...> class Adaptor, |
152 | template <class ...> class UnderlyingContainer, |
153 | class T, |
154 | class Iter, |
155 | class Sent, |
156 | class Alloc> |
157 | constexpr void test_container_adaptor() { |
158 | auto test_with_input = &test_container_adaptor_with_input<Adaptor, UnderlyingContainer, T, Iter, Sent, Alloc>; |
159 | |
160 | // Normal input. |
161 | test_with_input({0, 5, 12, 7, -1, 8, 26}); |
162 | // Empty input. |
163 | test_with_input({}); |
164 | // Single-element input. |
165 | test_with_input({5}); |
166 | } |
167 | |
168 | template <template <class ...> class UnderlyingContainer, |
169 | class T, |
170 | class Iter, |
171 | class Sent, |
172 | class Comp, |
173 | class Alloc> |
174 | constexpr void test_priority_queue() { |
175 | auto test_with_input = &test_priority_queue_with_input<UnderlyingContainer, T, Iter, Sent, Comp, Alloc>; |
176 | |
177 | // Normal input. |
178 | test_with_input({0, 5, 12, 7, -1, 8, 26}); |
179 | // Empty input. |
180 | test_with_input({}); |
181 | // Single-element input. |
182 | test_with_input({5}); |
183 | } |
184 | |
185 | template <template <class ...> class Container> |
186 | constexpr void test_container_adaptor_move_only() { |
187 | MoveOnly input[5]; |
188 | std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5}); |
189 | |
190 | [[maybe_unused]] Container<MoveOnly> c(std::from_range, in); |
191 | } |
192 | |
193 | template <template <class ...> class Adaptor> |
194 | void test_exception_safety_throwing_copy() { |
195 | #if !defined(TEST_HAS_NO_EXCEPTIONS) |
196 | constexpr int ThrowOn = 3; |
197 | using T = ThrowingCopy<ThrowOn>; |
198 | test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) { |
199 | [[maybe_unused]] Adaptor<T, std::vector<T>> c(std::from_range, std::ranges::subrange(from, to)); |
200 | }); |
201 | #endif |
202 | } |
203 | |
204 | template <template <class ...> class Adaptor, class T> |
205 | void test_exception_safety_throwing_allocator() { |
206 | #if !defined(TEST_HAS_NO_EXCEPTIONS) |
207 | T in[] = {0, 1}; |
208 | |
209 | try { |
210 | using C = std::vector<T, ThrowingAllocator<T>>; |
211 | ThrowingAllocator<T> alloc; |
212 | |
213 | globalMemCounter.reset(); |
214 | Adaptor<T, C> c(std::from_range, in, alloc); |
215 | assert(false); // The constructor call should throw. |
216 | |
217 | } catch (int) { |
218 | assert(globalMemCounter.new_called == globalMemCounter.delete_called); |
219 | } |
220 | #endif |
221 | } |
222 | |
223 | #endif // SUPPORT_FROM_RANGE_CONTAINER_ADAPTORS_H |
224 | |