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_FROM_RANGE_ASSOCIATIVE_CONTAINERS_H
10 #define SUPPORT_FROM_RANGE_ASSOCIATIVE_CONTAINERS_H
19 #include "../exception_safety_helpers.h"
20 #include "../from_range_helpers.h"
21 #include "../test_compare.h"
23 #include "almost_satisfies_types.h"
24 #include "count_new.h"
25 #include "test_macros.h"
27 template <class Container
, class Range
>
28 concept HasFromRangeCtr
= requires (Range
&& range
) {
29 Container(std::from_range
, std::forward
<Range
>(range
));
30 Container(std::from_range
, std::forward
<Range
>(range
), std::less
<typename
Container::key_type
>());
31 Container(std::from_range
, std::forward
<Range
>(range
), std::less
<typename
Container::key_type
>(),
32 std::allocator
<typename
Container::value_type
>());
33 Container(std::from_range
, std::forward
<Range
>(range
), std::allocator
<typename
Container::value_type
>());
36 template <template <class...> class Container
, class K
, class V
, class K2
, class V2
>
37 constexpr bool test_map_constraints() {
38 using ValueType
= std::pair
<const K
, V
>;
40 // Input range with the same value type.
41 static_assert(HasFromRangeCtr
<Container
<K
, V
>, InputRange
<ValueType
>>);
42 // Input range with a convertible value type.
43 static_assert(HasFromRangeCtr
<Container
<K
, V
>, InputRange
<std::pair
<const K2
, V2
>>>);
44 // Input range with a non-convertible value type.
45 static_assert(!HasFromRangeCtr
<Container
<K
, V
>, InputRange
<std::pair
<const Empty
, V
>>>);
46 static_assert(!HasFromRangeCtr
<Container
<K
, V
>, InputRange
<std::pair
<const K
, Empty
>>>);
47 // Not an input range.
48 static_assert(!HasFromRangeCtr
<Container
<K
, V
>, InputRangeNotDerivedFromGeneric
<ValueType
>>);
53 template <template <class ...> class Container
,
60 class ValueType
= std::pair
<const K
, V
>>
61 void test_associative_map_with_input(std::vector
<ValueType
>&& input
) {
62 auto in
= wrap_input
<Iter
, Sent
>(input
);
65 Container
<K
, V
> c(std::from_range
, in
);
67 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
68 assert(std::ranges::is_permutation(input
, c
));
73 Container
<K
, V
, Comp
> c(std::from_range
, in
, comp
);
75 assert(c
.key_comp() == comp
);
76 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
77 assert(std::ranges::is_permutation(input
, c
));
80 { // (range, allocator)
82 Container
<K
, V
, std::less
<K
>, Alloc
> c(std::from_range
, in
, alloc
);
84 assert(c
.get_allocator() == alloc
);
85 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
86 assert(std::ranges::is_permutation(input
, c
));
89 { // (range, comp, allocator)
92 Container
<K
, V
, Comp
, Alloc
> c(std::from_range
, in
, comp
, alloc
);
94 assert(c
.key_comp() == comp
);
95 assert(c
.get_allocator() == alloc
);
96 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
97 assert(std::ranges::is_permutation(input
, c
));
101 template <template <class ...> class Container
,
108 void test_associative_map() {
109 auto test_with_input
= &test_associative_map_with_input
<Container
, K
, V
, Iter
, Sent
, Comp
, Alloc
>;
112 test_with_input({{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}});
115 // Single-element input.
116 test_with_input({{1, 2}});
119 template <template <class ...> class Container
>
120 void test_associative_map_move_only() {
121 std::pair
<const int, MoveOnly
> input
[5];
122 std::ranges::subrange
in(std::move_iterator
{input
}, std::move_iterator
{input
+ 5});
124 [[maybe_unused
]] Container
<int, MoveOnly
> c(std::from_range
, in
);
127 template <template <class ...> class Container
>
128 void test_map_exception_safety_throwing_copy() {
129 #if !defined(TEST_HAS_NO_EXCEPTIONS)
131 using V
= ThrowingCopy
<3>;
133 V::throwing_enabled
= false;
134 std::pair
<const K
, V
> in
[5] = {
135 {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}
137 V::throwing_enabled
= true;
141 Container
<K
, V
> c(std::from_range
, in
);
142 assert(false); // The constructor call above should throw.
145 assert(V::created_by_copying
== 3);
146 assert(V::destroyed
== 2); // No destructor call for the partially-constructed element.
151 template <template <class ...> class Container
, class K
, class V
>
152 void test_map_exception_safety_throwing_allocator() {
153 #if !defined(TEST_HAS_NO_EXCEPTIONS)
154 using ValueType
= std::pair
<const K
, V
>;
156 ValueType
{K
{1}, V
{1}}
160 ThrowingAllocator
<ValueType
> alloc
;
162 globalMemCounter
.reset();
163 Container
<K
, V
, test_less
<K
>, ThrowingAllocator
<ValueType
>> c(std::from_range
, in
, alloc
);
164 assert(false); // The constructor call above should throw.
167 assert(globalMemCounter
.new_called
== globalMemCounter
.delete_called
);
172 template <class Container
, class Range
>
173 concept SetHasFromRangeCtr
= requires (Range
&& range
) {
174 Container(std::from_range
, std::forward
<Range
>(range
));
175 Container(std::from_range
, std::forward
<Range
>(range
), std::less
<typename
Container::value_type
>());
176 Container(std::from_range
, std::forward
<Range
>(range
), std::less
<typename
Container::value_type
>(),
177 std::allocator
<typename
Container::value_type
>());
178 Container(std::from_range
, std::forward
<Range
>(range
), std::allocator
<typename
Container::value_type
>());
181 template <template <class...> class Container
, class T
, class U
>
182 constexpr bool test_set_constraints() {
183 // Input range with the same value type.
184 static_assert(SetHasFromRangeCtr
<Container
<T
>, InputRange
<T
>>);
185 // Input range with a convertible value type.
186 static_assert(SetHasFromRangeCtr
<Container
<T
>, InputRange
<U
>>);
187 // Input range with a non-convertible value type.
188 static_assert(!SetHasFromRangeCtr
<Container
<T
>, InputRange
<Empty
>>);
189 // Not an input range.
190 static_assert(!SetHasFromRangeCtr
<Container
<T
>, InputRangeNotDerivedFromGeneric
<T
>>);
195 template <template <class ...> class Container
,
201 void test_associative_set_with_input(std::vector
<T
>&& input
) {
202 auto b
= Iter(input
.data());
203 auto e
= Iter(input
.data() + input
.size());
204 std::ranges::subrange
in(std::move(b
), Sent(std::move(e
)));
207 Container
<T
> c(std::from_range
, in
);
209 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
210 assert(std::ranges::is_permutation(input
, c
));
215 Container
<T
, Comp
> c(std::from_range
, in
, comp
);
217 assert(c
.key_comp() == comp
);
218 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
219 assert(std::ranges::is_permutation(input
, c
));
222 { // (range, allocator)
224 Container
<T
, std::less
<T
>, Alloc
> c(std::from_range
, in
, alloc
);
226 assert(c
.get_allocator() == alloc
);
227 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
228 assert(std::ranges::is_permutation(input
, c
));
231 { // (range, comp, allocator)
234 Container
<T
, Comp
, Alloc
> c(std::from_range
, in
, comp
, alloc
);
236 assert(c
.key_comp() == comp
);
237 assert(c
.get_allocator() == alloc
);
238 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
239 assert(std::ranges::is_permutation(input
, c
));
243 template <template <class ...> class Container
,
249 void test_associative_set() {
250 auto test_with_input
= &test_associative_set_with_input
<Container
, T
, Iter
, Sent
, Comp
, Alloc
>;
253 test_with_input({0, 5, 12, 7, -1, 8, 26});
256 // Single-element input.
257 test_with_input({5});
260 template <template <class ...> class Container
>
261 void test_associative_set_move_only() {
263 std::ranges::subrange
in(std::move_iterator
{input
}, std::move_iterator
{input
+ 5});
265 [[maybe_unused
]] Container
<MoveOnly
> c(std::from_range
, in
);
268 template <template <class ...> class Container
>
269 void test_set_exception_safety_throwing_copy() {
270 #if !defined(TEST_HAS_NO_EXCEPTIONS)
271 using T
= ThrowingCopy
<3>;
273 T in
[5] = {{1}, {2}, {3}, {4}, {5}};
276 Container
<T
> c(std::from_range
, in
);
277 assert(false); // The constructor call above should throw.
280 assert(T::created_by_copying
== 3);
281 assert(T::destroyed
== 2); // No destructor call for the partially-constructed element.
286 template <template <class ...> class Container
, class T
>
287 void test_set_exception_safety_throwing_allocator() {
288 #if !defined(TEST_HAS_NO_EXCEPTIONS)
292 ThrowingAllocator
<T
> alloc
;
294 globalMemCounter
.reset();
295 Container
<T
, test_less
<T
>, ThrowingAllocator
<T
>> c(std::from_range
, in
, alloc
);
296 assert(false); // The constructor call above should throw.
299 assert(globalMemCounter
.new_called
== globalMemCounter
.delete_called
);
304 #endif // SUPPORT_FROM_RANGE_ASSOCIATIVE_CONTAINERS_H