xref: /llvm-project/libcxx/test/support/test_container_comparisons.h (revision f5832bab6f5024cabe32a9f668b7f44e6b7cfef5)
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 //  Utility functions to test comparisons on containers.
9 
10 #ifndef TEST_CONTAINER_COMPARISONS
11 #define TEST_CONTAINER_COMPARISONS
12 
13 #include <functional>
14 #include <set>
15 
16 #include "test_comparisons.h"
17 
18 // Implementation detail of `test_sequence_container_spaceship`
19 template <template <typename...> typename Container, typename Elem, typename Order>
test_sequence_container_spaceship_with_type()20 constexpr void test_sequence_container_spaceship_with_type() {
21   // Empty containers
22   {
23     Container<Elem> l1;
24     Container<Elem> l2;
25     assert(testOrder(l1, l2, Order::equivalent));
26   }
27   // Identical contents
28   {
29     Container<Elem> l1{1, 1};
30     Container<Elem> l2{1, 1};
31     assert(testOrder(l1, l2, Order::equivalent));
32   }
33   // Less, due to contained values
34   {
35     Container<Elem> l1{1, 1};
36     Container<Elem> l2{1, 2};
37     assert(testOrder(l1, l2, Order::less));
38   }
39   // Greater, due to contained values
40   {
41     Container<Elem> l1{1, 3};
42     Container<Elem> l2{1, 2};
43     assert(testOrder(l1, l2, Order::greater));
44   }
45   // Shorter list
46   {
47     Container<Elem> l1{1};
48     Container<Elem> l2{1, 2};
49     assert(testOrder(l1, l2, Order::less));
50   }
51   // Longer list
52   {
53     Container<Elem> l1{1, 2};
54     Container<Elem> l2{1};
55     assert(testOrder(l1, l2, Order::greater));
56   }
57   // Unordered
58   if constexpr (std::is_same_v<Elem, PartialOrder>) {
59     Container<Elem> l1{1, std::numeric_limits<int>::min()};
60     Container<Elem> l2{1, 2};
61     assert(testOrder(l1, l2, Order::unordered));
62   }
63 }
64 
65 // Tests the `operator<=>` on sequence containers
66 template <template <typename...> typename Container>
test_sequence_container_spaceship()67 constexpr bool test_sequence_container_spaceship() {
68   // The container should fulfill `std::three_way_comparable`
69   static_assert(std::three_way_comparable<Container<int>>);
70 
71   // Test different comparison categories
72   test_sequence_container_spaceship_with_type<Container, int, std::strong_ordering>();
73   test_sequence_container_spaceship_with_type<Container, StrongOrder, std::strong_ordering>();
74   test_sequence_container_spaceship_with_type<Container, WeakOrder, std::weak_ordering>();
75   test_sequence_container_spaceship_with_type<Container, PartialOrder, std::partial_ordering>();
76 
77   // `LessAndEqComp` does not have `operator<=>`. Ordering is synthesized based on `operator<`
78   test_sequence_container_spaceship_with_type<Container, LessAndEqComp, std::weak_ordering>();
79 
80   // Thanks to SFINAE, the following is not a compiler error but returns `false`
81   struct NonComparable {};
82   static_assert(!std::three_way_comparable<Container<NonComparable>>);
83 
84   return true;
85 }
86 
87 // Implementation detail of `test_sequence_container_adaptor_spaceship`
88 template <template <typename...> typename ContainerAdaptor,
89           template <typename...>
90           typename Container,
91           typename Elem,
92           typename Order>
test_sequence_container_adaptor_spaceship_with_type()93 constexpr void test_sequence_container_adaptor_spaceship_with_type() {
94   // Empty containers
95   {
96     Container<Elem> l1;
97     ContainerAdaptor<Elem, Container<Elem>> ca1{l1};
98     Container<Elem> l2;
99     ContainerAdaptor<Elem, Container<Elem>> ca2{l2};
100     assert(testOrder(ca1, ca2, Order::equivalent));
101   }
102   // Identical contents
103   {
104     Container<Elem> l1{1, 1};
105     ContainerAdaptor<Elem, Container<Elem>> ca1{l1};
106     Container<Elem> l2{1, 1};
107     ContainerAdaptor<Elem, Container<Elem>> ca2{l2};
108     assert(testOrder(ca1, ca2, Order::equivalent));
109   }
110   // Less, due to contained values
111   {
112     Container<Elem> l1{1, 1};
113     ContainerAdaptor<Elem, Container<Elem>> ca1{l1};
114     Container<Elem> l2{1, 2};
115     ContainerAdaptor<Elem, Container<Elem>> ca2{l2};
116     assert(testOrder(ca1, ca2, Order::less));
117   }
118   // Greater, due to contained values
119   {
120     Container<Elem> l1{1, 3};
121     ContainerAdaptor<Elem, Container<Elem>> ca1{l1};
122     Container<Elem> l2{1, 2};
123     ContainerAdaptor<Elem, Container<Elem>> ca2{l2};
124     assert(testOrder(ca1, ca2, Order::greater));
125   }
126   // Shorter list
127   {
128     Container<Elem> l1{1};
129     ContainerAdaptor<Elem, Container<Elem>> ca1{l1};
130     Container<Elem> l2{1, 2};
131     ContainerAdaptor<Elem, Container<Elem>> ca2{l2};
132     assert(testOrder(ca1, ca2, Order::less));
133   }
134   // Longer list
135   {
136     Container<Elem> l1{1, 2};
137     ContainerAdaptor<Elem, Container<Elem>> ca1{l1};
138     Container<Elem> l2{1};
139     ContainerAdaptor<Elem, Container<Elem>> ca2{l2};
140     assert(testOrder(ca1, ca2, Order::greater));
141   }
142   // Unordered
143   if constexpr (std::is_same_v<Elem, PartialOrder>) {
144     Container<Elem> l1{1, std::numeric_limits<int>::min()};
145     ContainerAdaptor<Elem, Container<Elem>> ca1{l1};
146     Container<Elem> l2{1, 2};
147     ContainerAdaptor<Elem, Container<Elem>> ca2{l2};
148     assert(testOrder(ca1, ca2, Order::unordered));
149   }
150 }
151 
152 // Tests the `operator<=>` on sequence container adaptors
153 template <template <typename...> typename ContainerAdaptor, template <typename...> typename Container>
test_sequence_container_adaptor_spaceship()154 constexpr bool test_sequence_container_adaptor_spaceship() {
155   // Thanks to SFINAE, the following is not a compiler error but returns `false`
156   struct NonComparable {};
157   static_assert(!std::three_way_comparable<ContainerAdaptor<NonComparable>>);
158 
159   // The container should fulfill `std::three_way_comparable`
160   static_assert(std::three_way_comparable<ContainerAdaptor<int, Container<int>>>);
161 
162   // Test different comparison categories
163   test_sequence_container_adaptor_spaceship_with_type<ContainerAdaptor, Container, int, std::strong_ordering>();
164   test_sequence_container_adaptor_spaceship_with_type<ContainerAdaptor, Container, StrongOrder, std::strong_ordering>();
165   test_sequence_container_adaptor_spaceship_with_type<ContainerAdaptor, Container, WeakOrder, std::weak_ordering>();
166   test_sequence_container_adaptor_spaceship_with_type<ContainerAdaptor,
167                                                       Container,
168                                                       PartialOrder,
169                                                       std::partial_ordering>();
170 
171   // `LessAndEqComp` does not have `operator<=>`. Ordering is synthesized based on `operator<`
172   test_sequence_container_adaptor_spaceship_with_type<ContainerAdaptor, Container, LessAndEqComp, std::weak_ordering>();
173 
174   return true;
175 }
176 
177 // Implementation detail of `test_ordered_map_container_spaceship`
178 template <template <typename...> typename Container, typename Key, typename Val, typename Order, typename Compare>
test_ordered_map_container_spaceship_with_type(Compare comp)179 constexpr void test_ordered_map_container_spaceship_with_type(Compare comp) {
180   // Empty containers
181   {
182     Container<Key, Val, Compare> l1{{}, comp};
183     Container<Key, Val, Compare> l2{{}, comp};
184     assert(testOrder(l1, l2, Order::equivalent));
185   }
186   // Identical contents
187   {
188     Container<Key, Val, Compare> l1{{{1, 1}, {2, 1}}, comp};
189     Container<Key, Val, Compare> l2{{{1, 1}, {2, 1}}, comp};
190     assert(testOrder(l1, l2, Order::equivalent));
191   }
192   // Less, due to contained values
193   {
194     Container<Key, Val, Compare> l1{{{1, 1}, {2, 1}}, comp};
195     Container<Key, Val, Compare> l2{{{1, 1}, {2, 2}}, comp};
196     assert(testOrder(l1, l2, Order::less));
197   }
198   // Greater, due to contained values
199   {
200     Container<Key, Val, Compare> l1{{{1, 1}, {2, 3}}, comp};
201     Container<Key, Val, Compare> l2{{{1, 1}, {2, 2}}, comp};
202     assert(testOrder(l1, l2, Order::greater));
203   }
204   // Shorter list
205   {
206     Container<Key, Val, Compare> l1{{{1, 1}}, comp};
207     Container<Key, Val, Compare> l2{{{1, 1}, {2, 2}}, comp};
208     assert(testOrder(l1, l2, Order::less));
209   }
210   // Longer list
211   {
212     Container<Key, Val, Compare> l1{{{1, 2}, {2, 2}}, comp};
213     Container<Key, Val, Compare> l2{{{1, 1}}, comp};
214     assert(testOrder(l1, l2, Order::greater));
215   }
216   // Unordered
217   if constexpr (std::is_same_v<Val, PartialOrder>) {
218     Container<Key, Val, Compare> l1{{{1, 1}, {2, std::numeric_limits<int>::min()}}, comp};
219     Container<Key, Val, Compare> l2{{{1, 1}, {2, 2}}, comp};
220     assert(testOrder(l1, l2, Order::unordered));
221   }
222 
223   // Identical contents
224   {
225     Container<Key, Val, Compare> l1{{{1, 1}, {2, 1}, {2, 2}}, comp};
226     Container<Key, Val, Compare> l2{{{1, 1}, {2, 1}, {2, 2}}, comp};
227     assert(testOrder(l1, l2, Order::equivalent));
228 
229     Container<Key, Val, Compare> l3{{{1, 1}, {2, 1}, {2, 2}}, comp};
230     Container<Key, Val, Compare> l4{{{2, 1}, {2, 2}, {1, 1}}, comp};
231     assert(testOrder(l3, l4, Order::equivalent));
232   }
233   // Less, due to contained values
234   {
235     Container<Key, Val, Compare> l1{{{1, 1}, {2, 1}, {2, 1}}, comp};
236     Container<Key, Val, Compare> l2{{{1, 1}, {2, 2}, {2, 2}}, comp};
237     assert(testOrder(l1, l2, Order::less));
238 
239     Container<Key, Val, Compare> l3{{{1, 1}, {2, 1}, {2, 1}}, comp};
240     Container<Key, Val, Compare> l4{{{2, 2}, {2, 2}, {1, 1}}, comp};
241     assert(testOrder(l3, l4, Order::less));
242   }
243   // Greater, due to contained values
244   {
245     Container<Key, Val, Compare> l1{{{1, 1}, {2, 3}, {2, 3}}, comp};
246     Container<Key, Val, Compare> l2{{{1, 1}, {2, 2}, {2, 2}}, comp};
247     assert(testOrder(l1, l2, Order::greater));
248 
249     Container<Key, Val, Compare> l3{{{1, 1}, {2, 3}, {2, 3}}, comp};
250     Container<Key, Val, Compare> l4{{{2, 2}, {2, 2}, {1, 1}}, comp};
251     assert(testOrder(l3, l4, Order::greater));
252   }
253   // Shorter list
254   {
255     Container<Key, Val, Compare> l1{{{1, 1}, {2, 2}}, comp};
256     Container<Key, Val, Compare> l2{{{1, 1}, {2, 2}, {2, 2}, {3, 1}}, comp};
257     assert(testOrder(l1, l2, Order::less));
258 
259     Container<Key, Val, Compare> l3{{{1, 1}, {2, 2}}, comp};
260     Container<Key, Val, Compare> l4{{{3, 1}, {2, 2}, {2, 2}, {1, 1}}, comp};
261     assert(testOrder(l3, l4, Order::less));
262   }
263   // Longer list
264   {
265     Container<Key, Val, Compare> l1{{{1, 2}, {2, 2}, {2, 2}, {3, 1}}, comp};
266     Container<Key, Val, Compare> l2{{{1, 1}, {2, 2}}, comp};
267     assert(testOrder(l1, l2, Order::greater));
268 
269     Container<Key, Val, Compare> l3{{{1, 2}, {2, 2}, {2, 2}, {3, 1}}, comp};
270     Container<Key, Val, Compare> l4{{{2, 2}, {1, 1}}, comp};
271     assert(testOrder(l3, l4, Order::greater));
272   }
273   // Unordered
274   if constexpr (std::is_same_v<Val, PartialOrder>) {
275     Container<Key, Val, Compare> l1{{{1, 1}, {2, std::numeric_limits<int>::min()}, {2, 3}}, comp};
276     Container<Key, Val, Compare> l2{{{1, 1}, {2, 2}, {2, 3}}, comp};
277     assert(testOrder(l1, l2, Order::unordered));
278 
279     Container<Key, Val, Compare> l3{{{1, 1}, {2, std::numeric_limits<int>::min()}, {2, 3}}, comp};
280     Container<Key, Val, Compare> l4{{{2, 3}, {2, 2}, {1, 1}}, comp};
281     assert(testOrder(l3, l4, Order::unordered));
282   }
283 }
284 
285 // Tests the `operator<=>` on ordered map containers
286 template <template <typename...> typename Container>
test_ordered_map_container_spaceship()287 constexpr bool test_ordered_map_container_spaceship() {
288   // Thanks to SFINAE, the following is not a compiler error but returns `false`
289   struct NonComparable {};
290   static_assert(!std::three_way_comparable<Container<int, NonComparable>>);
291 
292   // The container should fulfill `std::three_way_comparable`
293   static_assert(std::three_way_comparable<Container<int, int>>);
294 
295   // Test different comparison categories
296   test_ordered_map_container_spaceship_with_type<Container, int, int, std::strong_ordering>(std::less{});
297   test_ordered_map_container_spaceship_with_type<Container, int, int, std::strong_ordering>(std::greater{});
298   test_ordered_map_container_spaceship_with_type<Container, int, StrongOrder, std::strong_ordering>(std::less{});
299   test_ordered_map_container_spaceship_with_type<Container, int, StrongOrder, std::strong_ordering>(std::greater{});
300   test_ordered_map_container_spaceship_with_type<Container, int, WeakOrder, std::weak_ordering>(std::less{});
301   test_ordered_map_container_spaceship_with_type<Container, int, WeakOrder, std::weak_ordering>(std::greater{});
302   test_ordered_map_container_spaceship_with_type<Container, int, PartialOrder, std::partial_ordering>(std ::less{});
303   test_ordered_map_container_spaceship_with_type<Container, int, PartialOrder, std::partial_ordering>(std ::greater{});
304 
305   // `LessAndEqComp` does not have `operator<=>`. Ordering is synthesized based on `operator<`
306   test_ordered_map_container_spaceship_with_type<Container, int, LessAndEqComp, std::weak_ordering>(std::less{});
307 
308   return true;
309 }
310 
311 // Implementation detail of `test_ordered_set_container_spaceship`
312 template <template <typename...> typename Container, typename Elem, typename Order, typename Compare>
test_ordered_set_spaceship_with_type(Compare comp)313 constexpr void test_ordered_set_spaceship_with_type(Compare comp) {
314   // Empty containers
315   {
316     Container<Elem, Compare> l1{{}, comp};
317     Container<Elem, Compare> l2{{}, comp};
318     assert(testOrder(l1, l2, Order::equivalent));
319   }
320   // Identical contents
321   {
322     Container<Elem, Compare> l1{{1, 1, 2}, comp};
323     Container<Elem, Compare> l2{{1, 1, 2}, comp};
324     assert(testOrder(l1, l2, Order::equivalent));
325   }
326   // Less, due to contained values
327   {
328     Container<Elem, Compare> l1{{1, 1, 2, 3}, comp};
329     Container<Elem, Compare> l2{{1, 2, 2, 4}, comp};
330     assert(testOrder(l1, l2, Order::less));
331   }
332   // Greater, due to contained values
333   {
334     Container<Elem, Compare> l1{{1, 2, 2, 4}, comp};
335     Container<Elem, Compare> l2{{1, 1, 2, 3}, comp};
336     assert(testOrder(l1, l2, Order::greater));
337   }
338   // Shorter list
339   {
340     Container<Elem, Compare> l1{{1, 1, 2, 2}, comp};
341     Container<Elem, Compare> l2{{1, 1, 2, 2, 3}, comp};
342     assert(testOrder(l1, l2, Order::less));
343   }
344   // Longer list
345   {
346     Container<Elem, Compare> l1{{1, 1, 2, 2, 3}, comp};
347     Container<Elem, Compare> l2{{1, 1, 2, 2}, comp};
348     assert(testOrder(l1, l2, Order::greater));
349   }
350   // Unordered
351   if constexpr (std::is_same_v< Container<Elem>, std::multiset<PartialOrder>>) {
352     if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::less{})>) {
353       Container<Elem, Compare> l1{{1, std::numeric_limits<int>::min()}, comp};
354       Container<Elem, Compare> l2{{1, 2}, comp};
355       assert(testOrder(l1, l2, Order::unordered));
356     }
357     if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::less{})>) {
358       Container<Elem, Compare> l1{{1, std::numeric_limits<int>::max()}, comp};
359       Container<Elem, Compare> l2{{1, 2}, comp};
360       assert(testOrder(l1, l2, Order::unordered));
361     }
362   }
363   if constexpr (std::is_same_v< Container<Elem>, std::set<PartialOrder>>) {
364     // Unordered values are not supported for `set`
365     if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::less{})>) {
366       Container<Elem, Compare> l1{{1, std::numeric_limits<int>::min()}, comp};
367       Container<Elem, Compare> l2{{1, 2}, comp};
368       assert(testOrder(l1, l2, Order::less));
369     }
370     if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::less{})>) {
371       Container<Elem, Compare> l1{{1, std::numeric_limits<int>::max()}, comp};
372       Container<Elem, Compare> l2{{1, 2}, comp};
373       assert(testOrder(l1, l2, Order::less));
374     }
375   }
376   if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::greater{})>) {
377     Container<Elem, Compare> l1{{1, std::numeric_limits<int>::min()}, comp};
378     Container<Elem, Compare> l2{{1, 2}, comp};
379     assert(testOrder(l1, l2, Order::less));
380   }
381   if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::greater{})>) {
382     Container<Elem, Compare> l1{{1, std::numeric_limits<int>::max()}, comp};
383     Container<Elem, Compare> l2{{1, 2}, comp};
384     assert(testOrder(l1, l2, Order::less));
385   }
386 }
387 
388 // Tests the `operator<=>` on ordered set containers
389 template <template <typename...> typename Container>
test_ordered_set_container_spaceship()390 constexpr bool test_ordered_set_container_spaceship() {
391   // Thanks to SFINAE, the following is not a compiler error but returns `false`
392   struct NonComparable {};
393   static_assert(!std::three_way_comparable<Container<NonComparable>>);
394 
395   // The container should fulfill `std::three_way_comparable`
396   static_assert(std::three_way_comparable<Container<int>>);
397 
398   // Test different comparison categories
399   test_ordered_set_spaceship_with_type<Container, int, std::strong_ordering>(std::less{});
400   test_ordered_set_spaceship_with_type<Container, int, std::strong_ordering>(std::greater{});
401   test_ordered_set_spaceship_with_type<Container, StrongOrder, std::strong_ordering>(std::less{});
402   test_ordered_set_spaceship_with_type<Container, StrongOrder, std::strong_ordering>(std::greater{});
403   test_ordered_set_spaceship_with_type<Container, WeakOrder, std::weak_ordering>(std::less{});
404   test_ordered_set_spaceship_with_type<Container, WeakOrder, std::weak_ordering>(std::greater{});
405   test_ordered_set_spaceship_with_type<Container, PartialOrder, std::partial_ordering>(std::less{});
406   test_ordered_set_spaceship_with_type<Container, PartialOrder, std::partial_ordering>(std::greater{});
407 
408   // `LessAndEqComp` does not have `operator<=>`. Ordering is synthesized based on `operator<`
409   test_ordered_set_spaceship_with_type<Container, LessAndEqComp, std::weak_ordering>(std::less{});
410 
411   return true;
412 }
413 
414 #endif
415