xref: /llvm-project/libcxx/test/support/container_test_types.h (revision 953af0e7f1bcb42136be1a0ea9cdd5aa1fb74852)
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_CONTAINER_TEST_TYPES_H
10 #define SUPPORT_CONTAINER_TEST_TYPES_H
11 
12 // container_test_types.h - A set of types used for testing STL containers.
13 // The types container within this header are used to test the requirements in
14 // [container.requirements.general]. The header is made up of 3 main components:
15 //
16 // * test-types: 'CopyInsertable', 'MoveInsertable' and 'EmplaceConstructible' -
17 //    These test types are used to test the container requirements of the same
18 //    name. These test types use the global 'AllocatorConstructController' to
19 //    assert that they are only constructed by the containers allocator.
20 //
21 // * test-allocator: 'ContainerTestAllocator' - This test allocator is used to
22 //    test the portions of [container.requirements.general] that pertain to the
23 //    containers allocator. The three primary jobs of the test allocator are:
24 //      1. Enforce that 'a.construct(...)' and 'a.destroy(...)' are only ever
25 //         instantiated for 'Container::value_type'.
26 //      2. Provide a mechanism of checking calls to 'a.construct(Args...)'.
27 //         Including controlling when and with what types 'a.construct(...)'
28 //         may be called with.
29 //      3. Support the test types internals by controlling the global
30 //        'AllocatorConstructController' object.
31 //
32 // * 'AllocatorConstructController' - This type defines an interface for testing
33 //   the construction of types using an allocator. This type is used to communicate
34 //   between the test author, the containers allocator, and the types
35 //   being constructed by the container.
36 //   The controller's primary functions are:
37 //     1. Allow calls to 'a.construct(p, args...)' to be checked by a test.
38 //        The test uses 'cc->expect<Args...>()' to specify that the allocator
39 //        should expect one call to 'a.construct' with the specified argument
40 //        types.
41 //     2. Controlling the value of 'cc->isInAllocatorConstruct()' within the
42 //        'construct' method. The test-types use this value to assert that
43 //         they are being constructed by the allocator.
44 //
45 //   'AllocatorConstructController' enforces the Singleton pattern since the
46 //    test-types, test-allocator and test need to share the same controller
47 //    object. A pointer to the global controller is returned by
48 //   'getConstructController()'.
49 //
50 //----------------------------------------------------------------------------
51 /*
52  * Usage: The following example checks that 'unordered_map::emplace(Args&&...)'
53  *        with 'Args = [CopyInsertable<1> const&, CopyInsertable<2>&&]'
54  *        calls 'alloc.construct(value_type*, Args&&...)' with the same types.
55  *
56  * // Typedefs for container
57  * using Key = CopyInsertable<1>;
58  * using Value = CopyInsertable<2>;
59  * using ValueTp = std::pair<const Key, Value>;
60  * using Alloc = ContainerTestAllocator<ValueTp, ValueTp>;
61  * using Map = std::unordered_map<Key, Value, std::hash<Key>, std::equal_to<Key>, Alloc>;
62  *
63  * // Get the global controller, reset it, and construct an allocator with
64  * // the controller.
65  * ConstructController* cc = getConstructController();
66  * cc->reset();
67  *
68  * // Create a Map and a Key and Value to insert. Note that the test-allocator
69  * // does not need to be given 'cc'.
70  * Map m;
71  * const Key k(1);
72  * Value v(1);
73  *
74  * // Tell the controller to expect a construction from the specified types.
75  * cc->expect<Key const&, Value&&>();
76  *
77  * // Emplace the objects into the container. 'Alloc.construct(p, UArgs...)'
78  * // will assert 'cc->check<UArgs&&>()' is true which will consume
79  * // the call to 'cc->expect<...>()'.
80  * m.emplace(k, std::move(v));
81  *
82  * // Assert that the "expect" was consumed by a matching "check" call within
83  * // Alloc.
84  * assert(!cc->unexpected());
85  *
86  */
87 
88 #include <cassert>
89 #include <cstddef>
90 #include <deque>
91 #include <functional>
92 #include <list>
93 #include <map>
94 #include <new>
95 #include <set>
96 #include <unordered_map>
97 #include <unordered_set>
98 #include <utility>
99 #include <vector>
100 
101 #include "test_macros.h"
102 
103 #if TEST_STD_VER < 11
104 #error This header requires C++11 or greater
105 #endif
106 
107 namespace detail {
108 // TypeID - Represent a unique identifier for a type. TypeID allows equality
109 // comparisons between different types.
110 struct TypeID {
111   friend bool operator==(TypeID const& LHS, TypeID const& RHS)
112   {return LHS.m_id == RHS.m_id; }
113   friend bool operator!=(TypeID const& LHS, TypeID const& RHS)
114   {return LHS.m_id != RHS.m_id; }
115 private:
116   explicit constexpr TypeID(const int* xid) : m_id(xid) {}
117   const int* const m_id;
118   template <class T> friend class TypeInfo;
119 };
120 
121 // TypeInfo - Represent information for the specified type 'T', including a
122 // unique TypeID.
123 template <class T>
124 class TypeInfo {
125 public:
126   typedef T value_type;
127   typedef TypeID ID;
128   static  ID const& GetID() { static ID id(&dummy_addr); return id; }
129 
130 private:
131   static const int dummy_addr;
132 };
133 
134 template <class L, class R>
135 inline bool operator==(TypeInfo<L> const&, TypeInfo<R> const&)
136 { return std::is_same<L, R>::value; }
137 
138 template <class L, class R>
139 inline bool operator!=(TypeInfo<L> const& lhs, TypeInfo<R> const& rhs)
140 { return !(lhs == rhs); }
141 
142 template <class T>
143 const int TypeInfo<T>::dummy_addr = 42;
144 
145 // makeTypeID - Return the TypeID for the specified type 'T'.
146 template <class T>
147 inline constexpr TypeID const& makeTypeID() { return TypeInfo<T>::GetID(); }
148 
149 template <class ...Args>
150 struct ArgumentListID {};
151 
152 // makeArgumentID - Create and return a unique identifier for a given set
153 // of arguments.
154 template <class ...Args>
155 inline constexpr TypeID const& makeArgumentID() {
156   return makeTypeID<ArgumentListID<Args...>>();
157 }
158 
159 } // namespace detail
160 
161 //===----------------------------------------------------------------------===//
162 //                        AllocatorConstructController
163 //===----------------------------------------------------------------------===//
164 
165 struct AllocatorConstructController {
166   const detail::TypeID* m_expected_args;
167   bool m_allow_constructions;
168   bool m_allow_unchecked;
169   int m_expected_count;
170 
171   void clear() {
172     m_expected_args = nullptr;
173     m_expected_count = -1;
174   }
175 
176   // Check for and consume an expected construction added by 'expect'.
177   // Return true if the construction was expected and false otherwise.
178   // This should only be called by 'Allocator.construct'.
179   bool check(detail::TypeID const& tid) {
180     if (!m_expected_args) {
181       assert(m_allow_unchecked);
182       return m_allow_unchecked;
183     }
184     bool res = *m_expected_args == tid;
185     if (m_expected_count == -1 || --m_expected_count == -1)
186       m_expected_args = nullptr;
187     return res;
188   }
189 
190   // Return true iff there is an unchecked construction expression.
191   bool unchecked() {
192     return m_expected_args != nullptr;
193   }
194 
195   // Expect a call to Allocator::construct with Args that match 'tid'.
196   void expect(detail::TypeID const& tid) {
197     assert(!unchecked());
198     m_expected_args = &tid;
199   }
200 
201   template <class ...Args>
202   void expect(int times = 1) {
203     assert(!unchecked());
204     assert(times > 0);
205     m_expected_count = times - 1;
206     m_expected_args = &detail::makeArgumentID<Args...>();
207   }
208   template <class ...Args>
209   bool check() {
210     return check(detail::makeArgumentID<Args...>());
211   }
212 
213 
214   // Return true iff the program is currently within a call to "Allocator::construct"
215   bool isInAllocatorConstruct() const {
216     return m_allow_constructions;
217   }
218 
219   void inAllocatorConstruct(bool value = true) {
220     m_allow_constructions = value;
221   }
222 
223   void allowUnchecked(bool value = true) {
224     m_allow_unchecked = value;
225   }
226 
227   void reset() {
228     m_allow_constructions = false;
229     m_expected_args = nullptr;
230     m_allow_unchecked = false;
231     m_expected_count = -1;
232   }
233 
234 private:
235   friend AllocatorConstructController* getConstructController();
236   AllocatorConstructController()  { reset(); }
237   AllocatorConstructController(AllocatorConstructController const&);
238   AllocatorConstructController& operator=(AllocatorConstructController const&);
239 };
240 
241 typedef AllocatorConstructController ConstructController;
242 
243 // getConstructController - Return the global allocator construction controller.
244 inline ConstructController* getConstructController() {
245   static ConstructController c;
246   return &c;
247 }
248 
249 template <class ...Args>
250 struct ExpectConstructGuard {
251   ExpectConstructGuard(int N)  {
252     auto CC = getConstructController();
253     assert(!CC->unchecked());
254     CC->expect<Args...>(N);
255   }
256 
257   ~ExpectConstructGuard() {
258     assert(!getConstructController()->unchecked());
259   }
260 };
261 
262 //===----------------------------------------------------------------------===//
263 //                       ContainerTestAllocator
264 //===----------------------------------------------------------------------===//
265 
266 // ContainerTestAllocator - A STL allocator type that only allows 'construct'
267 // and 'destroy' to be called for 'AllowConstructT' types. ContainerTestAllocator
268 // uses the 'AllocatorConstructionController' interface.
269 template <class T, class AllowConstructT>
270 class ContainerTestAllocator
271 {
272   struct InAllocatorConstructGuard {
273     ConstructController *m_cc;
274     bool m_old;
275     InAllocatorConstructGuard(ConstructController* cc) : m_cc(cc) {
276       if (m_cc) {
277         m_old = m_cc->isInAllocatorConstruct();
278         m_cc->inAllocatorConstruct(true);
279       }
280     }
281     ~InAllocatorConstructGuard() {
282       if (m_cc) m_cc->inAllocatorConstruct(m_old);
283     }
284   private:
285     InAllocatorConstructGuard(InAllocatorConstructGuard const&);
286     InAllocatorConstructGuard& operator=(InAllocatorConstructGuard const&);
287   };
288 
289 public:
290     typedef T value_type;
291 
292     int construct_called;
293     int destroy_called;
294     ConstructController* controller;
295 
296     ContainerTestAllocator() TEST_NOEXCEPT
297         : controller(getConstructController()) {}
298 
299     explicit ContainerTestAllocator(ConstructController* c)
300        : controller(c)
301     {}
302 
303     template <class U>
304     ContainerTestAllocator(ContainerTestAllocator<U, AllowConstructT> other) TEST_NOEXCEPT
305       : controller(other.controller)
306     {}
307 
308     T* allocate(std::size_t n)
309     {
310         return static_cast<T*>(::operator new(n*sizeof(T)));
311     }
312 
313     void deallocate(T* p, std::size_t)
314     {
315         return ::operator delete(static_cast<void*>(p));
316     }
317 
318     template <class Up, class ...Args>
319     void construct(Up* p, Args&&... args) {
320       static_assert((std::is_same<Up, AllowConstructT>::value),
321                     "Only allowed to construct Up");
322       assert(controller->check<Args&&...>());
323       {
324         InAllocatorConstructGuard g(controller);
325         ::new ((void*)p) Up(std::forward<Args>(args)...);
326       }
327     }
328 
329     template <class Up>
330     void destroy(Up* p) {
331       static_assert((std::is_same<Up, AllowConstructT>::value),
332                     "Only allowed to destroy Up");
333       {
334         InAllocatorConstructGuard g(controller);
335         p->~Up();
336       }
337     }
338 
339     friend bool operator==(ContainerTestAllocator, ContainerTestAllocator) {return true;}
340     friend bool operator!=(ContainerTestAllocator x, ContainerTestAllocator y) {return !(x == y);}
341 };
342 
343 
344 namespace test_detail {
345 typedef ContainerTestAllocator<int, int> A1;
346 typedef std::allocator_traits<A1> A1T;
347 typedef ContainerTestAllocator<float, int> A2;
348 typedef std::allocator_traits<A2> A2T;
349 
350 static_assert(std::is_same<A1T::rebind_traits<float>, A2T>::value, "");
351 static_assert(std::is_same<A2T::rebind_traits<int>, A1T>::value, "");
352 } // namespace test_detail
353 
354 //===----------------------------------------------------------------------===//
355 //  'CopyInsertable', 'MoveInsertable' and 'EmplaceConstructible' test types
356 //===----------------------------------------------------------------------===//
357 
358 template <int Dummy = 0>
359 struct CopyInsertable {
360   int data;
361   mutable bool copied_once;
362   bool constructed_under_allocator;
363 
364   explicit CopyInsertable(int val) : data(val), copied_once(false),
365                                      constructed_under_allocator(false) {
366     if (getConstructController()->isInAllocatorConstruct()) {
367       copied_once = true;
368       constructed_under_allocator = true;
369     }
370   }
371 
372   CopyInsertable() : data(0), copied_once(false), constructed_under_allocator(true)
373   {
374     assert(getConstructController()->isInAllocatorConstruct());
375   }
376 
377   CopyInsertable(CopyInsertable const& other) : data(other.data),
378                                                 copied_once(true),
379                                                 constructed_under_allocator(true) {
380     assert(getConstructController()->isInAllocatorConstruct());
381     assert(other.copied_once == false);
382     other.copied_once = true;
383   }
384 
385   CopyInsertable(CopyInsertable& other) : data(other.data), copied_once(true),
386                                           constructed_under_allocator(true) {
387     assert(getConstructController()->isInAllocatorConstruct());
388     assert(other.copied_once == false);
389     other.copied_once = true;
390   }
391 
392   CopyInsertable(CopyInsertable&& other) : CopyInsertable(other) {}
393 
394   // Forgive pair for not downcasting this to an lvalue in its constructors.
395   CopyInsertable(CopyInsertable const && other) : CopyInsertable(other) {}
396 
397 
398   template <class ...Args>
399   CopyInsertable(Args&&...) {
400     assert(false);
401   }
402 
403   ~CopyInsertable() {
404     assert(constructed_under_allocator == getConstructController()->isInAllocatorConstruct());
405   }
406 
407   void reset(int value) {
408     data = value;
409     copied_once = false;
410     constructed_under_allocator = false;
411   }
412 };
413 
414 template <int ID>
415 bool operator==(CopyInsertable<ID> const& L, CopyInsertable<ID> const& R) {
416   return L.data == R.data;
417 }
418 
419 
420 template <int ID>
421 bool operator!=(CopyInsertable<ID> const& L, CopyInsertable<ID> const& R) {
422   return L.data != R.data;
423 }
424 
425 template <int ID>
426 bool operator <(CopyInsertable<ID> const& L, CopyInsertable<ID> const& R) {
427   return L.data < R.data;
428 }
429 
430 namespace std {
431   template <int ID>
432   struct hash< ::CopyInsertable<ID> > {
433     typedef ::CopyInsertable<ID> argument_type;
434     typedef std::size_t result_type;
435 
436     std::size_t operator()(argument_type const& arg) const {
437       return arg.data;
438     }
439   };
440 }
441 
442 // TCT - Test container type
443 namespace TCT {
444 
445 template <class T = CopyInsertable<1>>
446 using vector = std::vector<T, ContainerTestAllocator<T, T> >;
447 template <class T = CopyInsertable<1>>
448 using deque = std::deque<T, ContainerTestAllocator<T, T> >;
449 template <class T = CopyInsertable<1>>
450 using list = std::list<T, ContainerTestAllocator<T, T> >;
451 
452 template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>,
453           class ValueTp = std::pair<const Key, Value> >
454 using unordered_map =
455       std::unordered_map<Key, Value, std::hash<Key>, std::equal_to<Key>,
456                               ContainerTestAllocator<ValueTp, ValueTp> >;
457 
458 template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>,
459           class ValueTp = std::pair<const Key, Value> >
460 using map =
461       std::map<Key, Value, std::less<Key>,
462                               ContainerTestAllocator<ValueTp, ValueTp> >;
463 
464 template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>,
465           class ValueTp = std::pair<const Key, Value> >
466 using unordered_multimap =
467       std::unordered_multimap<Key, Value, std::hash<Key>, std::equal_to<Key>,
468                                    ContainerTestAllocator<ValueTp, ValueTp> >;
469 
470 template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>,
471           class ValueTp = std::pair<const Key, Value> >
472 using multimap =
473       std::multimap<Key, Value, std::less<Key>,
474                               ContainerTestAllocator<ValueTp, ValueTp> >;
475 
476 template <class Value = CopyInsertable<1> >
477 using unordered_set =
478   std::unordered_set<Value, std::hash<Value>, std::equal_to<Value>,
479                                ContainerTestAllocator<Value, Value> >;
480 
481 template <class Value = CopyInsertable<1> >
482 using set =
483     std::set<Value, std::less<Value>, ContainerTestAllocator<Value, Value> >;
484 
485 template <class Value = CopyInsertable<1> >
486 using unordered_multiset =
487     std::unordered_multiset<Value, std::hash<Value>, std::equal_to<Value>,
488                                     ContainerTestAllocator<Value, Value> >;
489 
490 template <class Value = CopyInsertable<1> >
491 using multiset =
492     std::multiset<Value, std::less<Value>, ContainerTestAllocator<Value, Value> >;
493 
494 } // namespace TCT
495 
496 #endif // SUPPORT_CONTAINER_TEST_TYPES_H
497