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
) {
63 auto in
= wrap_input
<Iter
, Sent
>(input
);
64 Container
<K
, V
> c(std::from_range
, in
);
66 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
67 assert(std::ranges::is_permutation(input
, c
));
71 auto in
= wrap_input
<Iter
, Sent
>(input
);
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)
81 auto in
= wrap_input
<Iter
, Sent
>(input
);
83 Container
<K
, V
, std::less
<K
>, Alloc
> c(std::from_range
, in
, alloc
);
85 assert(c
.get_allocator() == alloc
);
86 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
87 assert(std::ranges::is_permutation(input
, c
));
90 { // (range, comp, allocator)
91 auto in
= wrap_input
<Iter
, Sent
>(input
);
94 Container
<K
, V
, Comp
, Alloc
> c(std::from_range
, in
, comp
, alloc
);
96 assert(c
.key_comp() == comp
);
97 assert(c
.get_allocator() == alloc
);
98 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
99 assert(std::ranges::is_permutation(input
, c
));
103 template <template <class ...> class Container
,
110 void test_associative_map() {
111 auto test_with_input
= &test_associative_map_with_input
<Container
, K
, V
, Iter
, Sent
, Comp
, Alloc
>;
114 test_with_input({{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}});
117 // Single-element input.
118 test_with_input({{1, 2}});
121 template <template <class ...> class Container
>
122 void test_associative_map_move_only() {
123 std::pair
<const int, MoveOnly
> input
[5];
124 std::ranges::subrange
in(std::move_iterator
{input
}, std::move_iterator
{input
+ 5});
126 [[maybe_unused
]] Container
<int, MoveOnly
> c(std::from_range
, in
);
129 template <template <class ...> class Container
>
130 void test_map_exception_safety_throwing_copy() {
131 #if !defined(TEST_HAS_NO_EXCEPTIONS)
133 using V
= ThrowingCopy
<3>;
135 V::throwing_enabled
= false;
136 std::pair
<const K
, V
> in
[5] = {
137 {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}
139 V::throwing_enabled
= true;
143 Container
<K
, V
> c(std::from_range
, in
);
144 assert(false); // The constructor call above should throw.
147 assert(V::created_by_copying
== 3);
148 assert(V::destroyed
== 2); // No destructor call for the partially-constructed element.
153 template <template <class ...> class Container
, class K
, class V
>
154 void test_map_exception_safety_throwing_allocator() {
155 #if !defined(TEST_HAS_NO_EXCEPTIONS)
156 using ValueType
= std::pair
<const K
, V
>;
158 ValueType
{K
{1}, V
{1}}
162 ThrowingAllocator
<ValueType
> alloc
;
164 globalMemCounter
.reset();
165 Container
<K
, V
, test_less
<K
>, ThrowingAllocator
<ValueType
>> c(std::from_range
, in
, alloc
);
166 assert(false); // The constructor call above should throw.
169 assert(globalMemCounter
.new_called
== globalMemCounter
.delete_called
);
174 template <class Container
, class Range
>
175 concept SetHasFromRangeCtr
= requires (Range
&& range
) {
176 Container(std::from_range
, std::forward
<Range
>(range
));
177 Container(std::from_range
, std::forward
<Range
>(range
), std::less
<typename
Container::value_type
>());
178 Container(std::from_range
, std::forward
<Range
>(range
), std::less
<typename
Container::value_type
>(),
179 std::allocator
<typename
Container::value_type
>());
180 Container(std::from_range
, std::forward
<Range
>(range
), std::allocator
<typename
Container::value_type
>());
183 template <template <class...> class Container
, class T
, class U
>
184 constexpr bool test_set_constraints() {
185 // Input range with the same value type.
186 static_assert(SetHasFromRangeCtr
<Container
<T
>, InputRange
<T
>>);
187 // Input range with a convertible value type.
188 static_assert(SetHasFromRangeCtr
<Container
<T
>, InputRange
<U
>>);
189 // Input range with a non-convertible value type.
190 static_assert(!SetHasFromRangeCtr
<Container
<T
>, InputRange
<Empty
>>);
191 // Not an input range.
192 static_assert(!SetHasFromRangeCtr
<Container
<T
>, InputRangeNotDerivedFromGeneric
<T
>>);
197 template <template <class ...> class Container
,
203 void test_associative_set_with_input(std::vector
<T
>&& input
) {
205 std::ranges::subrange
in(Iter(input
.data()), Sent(Iter(input
.data() + input
.size())));
206 Container
<T
> c(std::from_range
, in
);
208 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
209 assert(std::ranges::is_permutation(input
, c
));
213 std::ranges::subrange
in(Iter(input
.data()), Sent(Iter(input
.data() + input
.size())));
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)
223 std::ranges::subrange
in(Iter(input
.data()), Sent(Iter(input
.data() + input
.size())));
225 Container
<T
, std::less
<T
>, Alloc
> c(std::from_range
, in
, alloc
);
227 assert(c
.get_allocator() == alloc
);
228 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
229 assert(std::ranges::is_permutation(input
, c
));
232 { // (range, comp, allocator)
233 std::ranges::subrange
in(Iter(input
.data()), Sent(Iter(input
.data() + input
.size())));
236 Container
<T
, Comp
, Alloc
> c(std::from_range
, in
, comp
, alloc
);
238 assert(c
.key_comp() == comp
);
239 assert(c
.get_allocator() == alloc
);
240 assert(c
.size() == static_cast<std::size_t>(std::distance(c
.begin(), c
.end())));
241 assert(std::ranges::is_permutation(input
, c
));
245 template <template <class ...> class Container
,
251 void test_associative_set() {
252 auto test_with_input
= &test_associative_set_with_input
<Container
, T
, Iter
, Sent
, Comp
, Alloc
>;
255 test_with_input({0, 5, 12, 7, -1, 8, 26});
258 // Single-element input.
259 test_with_input({5});
262 template <template <class ...> class Container
>
263 void test_associative_set_move_only() {
265 std::ranges::subrange
in(std::move_iterator
{input
}, std::move_iterator
{input
+ 5});
267 [[maybe_unused
]] Container
<MoveOnly
> c(std::from_range
, in
);
270 template <template <class ...> class Container
>
271 void test_set_exception_safety_throwing_copy() {
272 #if !defined(TEST_HAS_NO_EXCEPTIONS)
273 using T
= ThrowingCopy
<3>;
275 T in
[5] = {{1}, {2}, {3}, {4}, {5}};
278 Container
<T
> c(std::from_range
, in
);
279 assert(false); // The constructor call above should throw.
282 assert(T::created_by_copying
== 3);
283 assert(T::destroyed
== 2); // No destructor call for the partially-constructed element.
288 template <template <class ...> class Container
, class T
>
289 void test_set_exception_safety_throwing_allocator() {
290 #if !defined(TEST_HAS_NO_EXCEPTIONS)
294 ThrowingAllocator
<T
> alloc
;
296 globalMemCounter
.reset();
297 Container
<T
, test_less
<T
>, ThrowingAllocator
<T
>> c(std::from_range
, in
, alloc
);
298 assert(false); // The constructor call above should throw.
301 assert(globalMemCounter
.new_called
== globalMemCounter
.delete_called
);
306 #endif // SUPPORT_FROM_RANGE_ASSOCIATIVE_CONTAINERS_H