1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef SUPPORT_INSERT_RANGE_MAPS_SETS_H
10 #define SUPPORT_INSERT_RANGE_MAPS_SETS_H
17 #include <type_traits>
21 #include "almost_satisfies_types.h"
22 #include "count_new.h"
23 #include "exception_safety_helpers.h"
24 #include "insert_range_helpers.h"
25 #include "min_allocator.h"
26 #include "test_allocator.h"
27 #include "test_compare.h"
28 #include "test_hash.h"
29 #include "test_iterators.h"
30 #include "test_macros.h"
31 #include "type_algorithms.h"
33 template <class Container
, class Range
>
34 concept HasInsertRange
= requires (Container
& c
, Range
&& range
) {
35 c
.insert_range(range
);
38 template <template <class...> class Container
, class T
, class U
>
39 constexpr bool test_set_constraints_insert_range() {
40 // Input range with the same value type.
41 static_assert(HasInsertRange
<Container
<T
>, InputRange
<T
>>);
42 // Input range with a convertible value type.
43 static_assert(HasInsertRange
<Container
<T
>, InputRange
<U
>>);
44 // Input range with a non-convertible value type.
45 static_assert(!HasInsertRange
<Container
<T
>, InputRange
<Empty
>>);
46 // Not an input range.
47 static_assert(!HasInsertRange
<Container
<T
>, InputRangeNotDerivedFrom
>);
48 static_assert(!HasInsertRange
<Container
<T
>, InputRangeNotIndirectlyReadable
>);
49 static_assert(!HasInsertRange
<Container
<T
>, InputRangeNotInputOrOutputIterator
>);
54 template <template <class...> class Container
, class K
, class V
, class K2
, class V2
>
55 constexpr bool test_map_constraints_insert_range() {
56 using ValueType
= std::pair
<const K
, V
>;
58 // Input range with the same value type.
59 static_assert(HasInsertRange
<Container
<K
, V
>, InputRange
<ValueType
>>);
60 // Input range with a convertible value type.
61 static_assert(HasInsertRange
<Container
<K
, V
>, InputRange
<std::pair
<const K2
, V2
>>>);
62 // Input range with a non-convertible value type.
63 static_assert(!HasInsertRange
<Container
<K
, V
>, InputRange
<std::pair
<const K
, Empty
>>>);
64 static_assert(!HasInsertRange
<Container
<K
, V
>, InputRange
<std::pair
<const Empty
, V
>>>);
65 // Not an input range.
66 static_assert(!HasInsertRange
<Container
<K
, V
>, InputRangeNotDerivedFromGeneric
<ValueType
>>);
72 struct TestCaseMapSet
{
76 Buffer
<T
> expected_multi
;
82 TestCaseMapSet
<T
> constexpr EmptyContainer_EmptyRange
{
83 .initial
= {}, .input
= {}, .expected
= {}
87 TestCaseMapSet
<T
> constexpr EmptyContainer_OneElementRange
{
88 .initial
= {}, .input
= {1}, .expected
= {1}
90 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
91 constexpr EmptyContainer_OneElementRange
<std::pair
<K
, V
>> {
92 .initial
= {}, .input
= {{1, 'a'}}, .expected
= {{1, 'a'}}
96 TestCaseMapSet
<T
> constexpr EmptyContainer_RangeNoDuplicates
{
97 .initial
= {}, .input
= {5, 1, 3, 8, 6}, .expected
= {5, 1, 3, 8, 6}
99 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
100 constexpr EmptyContainer_RangeNoDuplicates
<std::pair
<K
, V
>> {
101 .initial
= {}, .input
= {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}},
102 .expected
= {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}}
106 TestCaseMapSet
<T
> constexpr EmptyContainer_RangeWithDuplicates
{
108 .input
= {5, 1, 1, 3, 5, 8, 5, 6, 10},
109 .expected
= {5, 1, 3, 8, 6, 10},
110 .expected_multi
= {5, 1, 1, 3, 5, 8, 5, 6, 10}
112 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
113 constexpr EmptyContainer_RangeWithDuplicates
<std::pair
<K
, V
>> {
115 .input
= {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}},
116 .expected
= {{5, 'a'}, {1, 'a'}, {3, 'a'}, {8, 'a'}, {6, 'a'}, {10, 'b'}},
117 .expected_multi
= {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}}
120 // One-element container.
123 TestCaseMapSet
<T
> constexpr OneElementContainer_EmptyRange
{
124 .initial
= {10}, .input
= {}, .expected
= {10}
126 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
127 constexpr OneElementContainer_EmptyRange
<std::pair
<K
, V
>> {
128 .initial
= {{10, 'A'}}, .input
= {}, .expected
= {{10, 'A'}}
132 TestCaseMapSet
<T
> constexpr OneElementContainer_OneElementRange
{
133 .initial
= {10}, .input
= {1}, .expected
= {1, 10}
135 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
136 constexpr OneElementContainer_OneElementRange
<std::pair
<K
, V
>> {
137 .initial
= {{10, 'A'}}, .input
= {{1, 'a'}}, .expected
= {{1, 'a'}, {10, 'A'}}
141 TestCaseMapSet
<T
> constexpr OneElementContainer_RangeNoDuplicates
{
142 .initial
= {10}, .input
= {5, 1, 3, 8, 6}, .expected
= {5, 1, 3, 8, 6, 10}
144 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
145 constexpr OneElementContainer_RangeNoDuplicates
<std::pair
<K
, V
>> {
146 .initial
= {{10, 'A'}}, .input
= {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}},
147 .expected
= {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}, {10, 'A'}}
151 TestCaseMapSet
<T
> constexpr OneElementContainer_RangeWithDuplicates
{
153 .input
= {5, 1, 1, 3, 5, 8, 5, 6, 10},
154 .expected
= {5, 1, 3, 8, 6, 10},
155 .expected_multi
= {5, 1, 1, 3, 5, 8, 5, 6, 10, 10}
157 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
158 constexpr OneElementContainer_RangeWithDuplicates
<std::pair
<K
, V
>> {
159 .initial
= {{10, 'A'}},
160 .input
= {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}},
161 .expected
= {{5, 'a'}, {1, 'a'}, {3, 'a'}, {8, 'a'}, {6, 'a'}, {10, 'A'}},
163 {5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'A'}, {10, 'b'}
167 // N-elements container.
170 TestCaseMapSet
<T
> constexpr NElementsContainer_EmptyRange
{
171 .initial
= {10, 15, 19, 16}, .input
= {}, .expected
= {10, 15, 19, 16}
173 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
174 constexpr NElementsContainer_EmptyRange
<std::pair
<K
, V
>> {
175 .initial
= {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}, .input
= {},
176 .expected
= {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}
180 TestCaseMapSet
<T
> constexpr NElementsContainer_OneElementRange
{
181 .initial
= {10, 15, 19, 16}, .input
= {1}, .expected
= {1, 10, 15, 19, 16}
183 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
184 constexpr NElementsContainer_OneElementRange
<std::pair
<K
, V
>> {
185 .initial
= {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}, .input
= {{1, 'a'}},
186 .expected
= {{1, 'a'}, {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}
190 TestCaseMapSet
<T
> constexpr NElementsContainer_RangeNoDuplicates
{
191 .initial
= {10, 15, 19, 16}, .input
= {5, 1, 3, 8, 6}, .expected
= {5, 1, 3, 8, 6, 10, 15, 19, 16}
193 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
194 constexpr NElementsContainer_RangeNoDuplicates
<std::pair
<K
, V
>> {
195 .initial
= {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}},
196 .input
= {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}},
197 .expected
= {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}, {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}
201 TestCaseMapSet
<T
> constexpr NElementsContainer_RangeWithDuplicates
{
202 .initial
= {10, 15, 19, 16},
203 .input
= {5, 1, 1, 3, 5, 8, 5, 6, 10},
204 .expected
= {5, 1, 3, 8, 6, 10, 15, 19, 16},
205 .expected_multi
= {5, 1, 1, 3, 5, 8, 5, 6, 10, 10, 15, 19, 16}
207 template <class K
, class V
> TestCaseMapSet
<std::pair
<K
, V
>>
208 constexpr NElementsContainer_RangeWithDuplicates
<std::pair
<K
, V
>> {
209 .initial
= {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}},
210 .input
= {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}},
211 .expected
= {{5, 'a'}, {1, 'a'}, {3, 'a'}, {8, 'a'}, {6, 'a'}, {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}},
213 {5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'},
214 {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}
218 template <class Container
, class T
, class Iter
, class Sent
>
219 void test_map_set_insert_range(bool allow_duplicates
= false) {
220 auto test
= [&](const TestCaseMapSet
<T
>& test_case
, bool check_multi
= false) {
221 Container
c(test_case
.initial
.begin(), test_case
.initial
.end());
222 auto in
= wrap_input
<Iter
, Sent
>(test_case
.input
);
226 return std::ranges::is_permutation(c
, test_case
.expected_multi
);
228 return std::ranges::is_permutation(c
, test_case
.expected
);
232 { // Empty container.
233 // empty_c.insert_range(empty_range)
234 assert(test(EmptyContainer_EmptyRange
<T
>));
235 // empty_c.insert_range(one_element_range)
236 assert(test(EmptyContainer_OneElementRange
<T
>));
237 // empty_c.insert_range(range_no_duplicates)
238 assert(test(EmptyContainer_RangeNoDuplicates
<T
>));
239 // empty_c.insert_range(range_with_duplicates)
240 assert(test(EmptyContainer_RangeWithDuplicates
<T
>, allow_duplicates
));
243 { // One-element container.
244 // one_element_c.insert_range(empty_range)
245 assert(test(OneElementContainer_EmptyRange
<T
>));
246 // one_element_c.insert_range(one_element_range)
247 assert(test(OneElementContainer_OneElementRange
<T
>));
248 // one_element_c.insert_range(range_no_duplicates)
249 assert(test(OneElementContainer_RangeNoDuplicates
<T
>));
250 // one_element_c.insert_range(range_with_duplicates)
251 assert(test(OneElementContainer_RangeWithDuplicates
<T
>, allow_duplicates
));
254 { // N-elements container.
255 // n_elements_c.insert_range(empty_range)
256 assert(test(NElementsContainer_EmptyRange
<T
>));
257 // n_elements_c.insert_range(one_element_range)
258 assert(test(NElementsContainer_OneElementRange
<T
>));
259 // n_elements_c.insert_range(range_no_duplicates)
260 assert(test(NElementsContainer_RangeNoDuplicates
<T
>));
261 // n_elements_c.insert_range(range_with_duplicates)
262 assert(test(NElementsContainer_RangeWithDuplicates
<T
>, allow_duplicates
));
268 template <template <class ...> class Container
>
269 void test_set_insert_range_move_only() {
271 std::ranges::subrange
in(std::move_iterator
{input
}, std::move_iterator
{input
+ 5});
273 Container
<MoveOnly
> c
;
277 template <template <class ...> class Container
>
278 void test_map_insert_range_move_only() {
279 using Value
= std::pair
<const int, MoveOnly
>;
281 std::ranges::subrange
in(std::move_iterator
{input
}, std::move_iterator
{input
+ 5});
283 Container
<int, MoveOnly
> c
;
289 template <template <class ...> class Container
>
290 void test_set_insert_range_exception_safety_throwing_copy() {
291 #if !defined(TEST_HAS_NO_EXCEPTIONS)
292 using T
= ThrowingCopy
<3>;
294 T in
[5] = {{1}, {2}, {3}, {4}, {5}};
299 assert(false); // The constructor call above should throw.
302 assert(T::created_by_copying
== 3);
303 assert(T::destroyed
== 2); // No destructor call for the partially-constructed element.
308 template <template <class ...> class Container
>
309 void test_map_insert_range_exception_safety_throwing_copy() {
310 #if !defined(TEST_HAS_NO_EXCEPTIONS)
312 using V
= ThrowingCopy
<3>;
314 V::throwing_enabled
= false;
315 std::pair
<const K
, V
> in
[5] = {
316 {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}
318 V::throwing_enabled
= true;
324 assert(false); // The function call above should throw.
327 assert(V::created_by_copying
== 3);
328 assert(V::destroyed
== 2); // No destructor call for the partially-constructed element.
333 template <template <class ...> class Container
, class T
>
334 void test_assoc_set_insert_range_exception_safety_throwing_allocator() {
335 #if !defined(TEST_HAS_NO_EXCEPTIONS)
339 ThrowingAllocator
<T
> alloc
;
341 globalMemCounter
.reset();
342 Container
<T
, test_less
<T
>, ThrowingAllocator
<T
>> c(alloc
);
344 assert(false); // The function call above should throw.
347 assert(globalMemCounter
.new_called
== globalMemCounter
.delete_called
);
352 template <template <class ...> class Container
, class T
>
353 void test_unord_set_insert_range_exception_safety_throwing_allocator() {
354 #if !defined(TEST_HAS_NO_EXCEPTIONS)
358 ThrowingAllocator
<T
> alloc
;
360 globalMemCounter
.reset();
361 Container
<T
, test_hash
<T
>, test_equal_to
<T
>, ThrowingAllocator
<T
>> c(alloc
);
363 assert(false); // The function call above should throw.
366 assert(globalMemCounter
.new_called
== globalMemCounter
.delete_called
);
371 template <template <class ...> class Container
, class K
, class V
>
372 void test_assoc_map_insert_range_exception_safety_throwing_allocator() {
373 #if !defined(TEST_HAS_NO_EXCEPTIONS)
374 using ValueType
= std::pair
<const K
, V
>;
376 ValueType
{K
{1}, V
{1}}
380 ThrowingAllocator
<ValueType
> alloc
;
382 globalMemCounter
.reset();
383 Container
<K
, V
, test_less
<K
>, ThrowingAllocator
<ValueType
>> c(alloc
);
385 assert(false); // The function call above should throw.
388 assert(globalMemCounter
.new_called
== globalMemCounter
.delete_called
);
393 template <template <class ...> class Container
, class K
, class V
>
394 void test_unord_map_insert_range_exception_safety_throwing_allocator() {
395 #if !defined(TEST_HAS_NO_EXCEPTIONS)
396 using ValueType
= std::pair
<const K
, V
>;
398 ValueType
{K
{1}, V
{1}}
402 ThrowingAllocator
<ValueType
> alloc
;
404 globalMemCounter
.reset();
405 Container
<K
, V
, test_hash
<K
>, test_equal_to
<K
>, ThrowingAllocator
<ValueType
>> c(alloc
);
407 assert(false); // The function call above should throw.
410 assert(globalMemCounter
.new_called
== globalMemCounter
.delete_called
);
415 #endif // SUPPORT_INSERT_RANGE_MAPS_SETS_H