1 //===----------------------------------------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
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&, CopyInsertible<2>&&]'
54 * calls 'alloc.construct(value_type*, Args&&...)' with the same types.
55 *
56 * // Typedefs for container
57 * using Key = CopyInsertible<1>;
58 * using Value = CopyInsertible<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 <functional>
89 #include <cassert>
90
91 #include "test_macros.h"
92
93 #if TEST_STD_VER < 11
94 #error This header requires C++11 or greater
95 #endif
96
97 namespace detail {
98 // TypeID - Represent a unique identifier for a type. TypeID allows equality
99 // comparisons between different types.
100 struct TypeID {
101 friend bool operator==(TypeID const& LHS, TypeID const& RHS)
102 {return LHS.m_id == RHS.m_id; }
103 friend bool operator!=(TypeID const& LHS, TypeID const& RHS)
104 {return LHS.m_id != RHS.m_id; }
105 private:
TypeIDTypeID106 explicit constexpr TypeID(const int* xid) : m_id(xid) {}
107 const int* const m_id;
108 template <class T> friend class TypeInfo;
109 };
110
111 // TypeInfo - Represent information for the specified type 'T', including a
112 // unique TypeID.
113 template <class T>
114 class TypeInfo {
115 public:
116 typedef T value_type;
117 typedef TypeID ID;
GetID()118 static ID const& GetID() { static ID id(&dummy_addr); return id; }
119
120 private:
121 static const int dummy_addr;
122 };
123
124 template <class L, class R>
125 inline bool operator==(TypeInfo<L> const&, TypeInfo<R> const&)
126 { return std::is_same<L, R>::value; }
127
128 template <class L, class R>
129 inline bool operator!=(TypeInfo<L> const& lhs, TypeInfo<R> const& rhs)
130 { return !(lhs == rhs); }
131
132 template <class T>
133 const int TypeInfo<T>::dummy_addr = 42;
134
135 // makeTypeID - Return the TypeID for the specified type 'T'.
136 template <class T>
makeTypeID()137 inline constexpr TypeID const& makeTypeID() { return TypeInfo<T>::GetID(); }
138
139 template <class ...Args>
140 struct ArgumentListID {};
141
142 // makeArgumentID - Create and return a unique identifier for a given set
143 // of arguments.
144 template <class ...Args>
makeArgumentID()145 inline constexpr TypeID const& makeArgumentID() {
146 return makeTypeID<ArgumentListID<Args...>>();
147 }
148
149 } // namespace detail
150
151 //===----------------------------------------------------------------------===//
152 // AllocatorConstructController
153 //===----------------------------------------------------------------------===//
154
155 struct AllocatorConstructController {
156 const detail::TypeID* m_expected_args;
157 bool m_allow_constructions;
158 bool m_allow_unchecked;
159 int m_expected_count;
160
clearAllocatorConstructController161 void clear() {
162 m_expected_args = nullptr;
163 m_expected_count = -1;
164 }
165
166 // Check for and consume an expected construction added by 'expect'.
167 // Return true if the construction was expected and false otherwise.
168 // This should only be called by 'Allocator.construct'.
checkAllocatorConstructController169 bool check(detail::TypeID const& tid) {
170 if (!m_expected_args) {
171 assert(m_allow_unchecked);
172 return m_allow_unchecked;
173 }
174 bool res = *m_expected_args == tid;
175 if (m_expected_count == -1 || --m_expected_count == -1)
176 m_expected_args = nullptr;
177 return res;
178 }
179
180 // Return true iff there is an unchecked construction expression.
uncheckedAllocatorConstructController181 bool unchecked() {
182 return m_expected_args != nullptr;
183 }
184
185 // Expect a call to Allocator::construct with Args that match 'tid'.
expectAllocatorConstructController186 void expect(detail::TypeID const& tid) {
187 assert(!unchecked());
188 m_expected_args = &tid;
189 }
190
191 template <class ...Args>
192 void expect(int times = 1) {
193 assert(!unchecked());
194 assert(times > 0);
195 m_expected_count = times - 1;
196 m_expected_args = &detail::makeArgumentID<Args...>();
197 }
198 template <class ...Args>
checkAllocatorConstructController199 bool check() {
200 return check(detail::makeArgumentID<Args...>());
201 }
202
203
204 // Return true iff the program is currently within a call to "Allocator::construct"
isInAllocatorConstructAllocatorConstructController205 bool isInAllocatorConstruct() const {
206 return m_allow_constructions;
207 }
208
209 void inAllocatorConstruct(bool value = true) {
210 m_allow_constructions = value;
211 }
212
213 void allowUnchecked(bool value = true) {
214 m_allow_unchecked = value;
215 }
216
resetAllocatorConstructController217 void reset() {
218 m_allow_constructions = false;
219 m_expected_args = nullptr;
220 m_allow_unchecked = false;
221 m_expected_count = -1;
222 }
223
224 private:
225 friend AllocatorConstructController* getConstructController();
AllocatorConstructControllerAllocatorConstructController226 AllocatorConstructController() { reset(); }
227 AllocatorConstructController(AllocatorConstructController const&);
228 AllocatorConstructController& operator=(AllocatorConstructController const&);
229 };
230
231 typedef AllocatorConstructController ConstructController;
232
233 // getConstructController - Return the global allocator construction controller.
getConstructController()234 inline ConstructController* getConstructController() {
235 static ConstructController c;
236 return &c;
237 }
238
239 template <class ...Args>
240 struct ExpectConstructGuard {
ExpectConstructGuardExpectConstructGuard241 ExpectConstructGuard(int N) {
242 auto CC = getConstructController();
243 assert(!CC->unchecked());
244 CC->expect<Args...>(N);
245 }
246
~ExpectConstructGuardExpectConstructGuard247 ~ExpectConstructGuard() {
248 assert(!getConstructController()->unchecked());
249 }
250 };
251
252 //===----------------------------------------------------------------------===//
253 // ContainerTestAllocator
254 //===----------------------------------------------------------------------===//
255
256 // ContainerTestAllocator - A STL allocator type that only allows 'construct'
257 // and 'destroy' to be called for 'AllowConstructT' types. ContainerTestAllocator
258 // uses the 'AllocatorConstructionController' interface.
259 template <class T, class AllowConstructT>
260 class ContainerTestAllocator
261 {
262 struct InAllocatorConstructGuard {
263 ConstructController *m_cc;
264 bool m_old;
InAllocatorConstructGuardInAllocatorConstructGuard265 InAllocatorConstructGuard(ConstructController* cc) : m_cc(cc) {
266 if (m_cc) {
267 m_old = m_cc->isInAllocatorConstruct();
268 m_cc->inAllocatorConstruct(true);
269 }
270 }
~InAllocatorConstructGuardInAllocatorConstructGuard271 ~InAllocatorConstructGuard() {
272 if (m_cc) m_cc->inAllocatorConstruct(m_old);
273 }
274 private:
275 InAllocatorConstructGuard(InAllocatorConstructGuard const&);
276 InAllocatorConstructGuard& operator=(InAllocatorConstructGuard const&);
277 };
278
279 public:
280 typedef T value_type;
281
282 int construct_called;
283 int destroy_called;
284 ConstructController* controller;
285
ContainerTestAllocator()286 ContainerTestAllocator() TEST_NOEXCEPT
287 : controller(getConstructController()) {}
288
ContainerTestAllocator(ConstructController * c)289 explicit ContainerTestAllocator(ConstructController* c)
290 : controller(c)
291 {}
292
293 template <class U>
ContainerTestAllocator(ContainerTestAllocator<U,AllowConstructT> other)294 ContainerTestAllocator(ContainerTestAllocator<U, AllowConstructT> other) TEST_NOEXCEPT
295 : controller(other.controller)
296 {}
297
allocate(std::size_t n)298 T* allocate(std::size_t n)
299 {
300 return static_cast<T*>(::operator new(n*sizeof(T)));
301 }
302
deallocate(T * p,std::size_t)303 void deallocate(T* p, std::size_t)
304 {
305 return ::operator delete(static_cast<void*>(p));
306 }
307
308 template <class Up, class ...Args>
construct(Up * p,Args &&...args)309 void construct(Up* p, Args&&... args) {
310 static_assert((std::is_same<Up, AllowConstructT>::value),
311 "Only allowed to construct Up");
312 assert(controller->check<Args&&...>());
313 {
314 InAllocatorConstructGuard g(controller);
315 ::new ((void*)p) Up(std::forward<Args>(args)...);
316 }
317 }
318
319 template <class Up>
destroy(Up * p)320 void destroy(Up* p) {
321 static_assert((std::is_same<Up, AllowConstructT>::value),
322 "Only allowed to destroy Up");
323 {
324 InAllocatorConstructGuard g(controller);
325 p->~Up();
326 }
327 }
328
329 friend bool operator==(ContainerTestAllocator, ContainerTestAllocator) {return true;}
330 friend bool operator!=(ContainerTestAllocator x, ContainerTestAllocator y) {return !(x == y);}
331 };
332
333
334 namespace test_detail {
335 typedef ContainerTestAllocator<int, int> A1;
336 typedef std::allocator_traits<A1> A1T;
337 typedef ContainerTestAllocator<float, int> A2;
338 typedef std::allocator_traits<A2> A2T;
339
340 static_assert(std::is_same<A1T::rebind_traits<float>, A2T>::value, "");
341 static_assert(std::is_same<A2T::rebind_traits<int>, A1T>::value, "");
342 } // end namespace test_detail
343
344 //===----------------------------------------------------------------------===//
345 // 'CopyInsertable', 'MoveInsertable' and 'EmplaceConstructible' test types
346 //===----------------------------------------------------------------------===//
347
348 template <int Dummy = 0>
349 struct CopyInsertable {
350 int data;
351 mutable bool copied_once;
352 bool constructed_under_allocator;
353
CopyInsertableCopyInsertable354 explicit CopyInsertable(int val) : data(val), copied_once(false),
355 constructed_under_allocator(false) {
356 if (getConstructController()->isInAllocatorConstruct()) {
357 copied_once = true;
358 constructed_under_allocator = true;
359 }
360 }
361
CopyInsertableCopyInsertable362 CopyInsertable() : data(0), copied_once(false), constructed_under_allocator(true)
363 {
364 assert(getConstructController()->isInAllocatorConstruct());
365 }
366
CopyInsertableCopyInsertable367 CopyInsertable(CopyInsertable const& other) : data(other.data),
368 copied_once(true),
369 constructed_under_allocator(true) {
370 assert(getConstructController()->isInAllocatorConstruct());
371 assert(other.copied_once == false);
372 other.copied_once = true;
373 }
374
CopyInsertableCopyInsertable375 CopyInsertable(CopyInsertable& other) : data(other.data), copied_once(true),
376 constructed_under_allocator(true) {
377 assert(getConstructController()->isInAllocatorConstruct());
378 assert(other.copied_once == false);
379 other.copied_once = true;
380 }
381
CopyInsertableCopyInsertable382 CopyInsertable(CopyInsertable&& other) : CopyInsertable(other) {}
383
384 // Forgive pair for not downcasting this to an lvalue in its constructors.
CopyInsertableCopyInsertable385 CopyInsertable(CopyInsertable const && other) : CopyInsertable(other) {}
386
387
388 template <class ...Args>
CopyInsertableCopyInsertable389 CopyInsertable(Args&&...) {
390 assert(false);
391 }
392
~CopyInsertableCopyInsertable393 ~CopyInsertable() {
394 assert(constructed_under_allocator == getConstructController()->isInAllocatorConstruct());
395 }
396
resetCopyInsertable397 void reset(int value) {
398 data = value;
399 copied_once = false;
400 constructed_under_allocator = false;
401 }
402 };
403
404 template <int ID>
405 bool operator==(CopyInsertable<ID> const& L, CopyInsertable<ID> const& R) {
406 return L.data == R.data;
407 }
408
409
410 template <int ID>
411 bool operator!=(CopyInsertable<ID> const& L, CopyInsertable<ID> const& R) {
412 return L.data != R.data;
413 }
414
415 template <int ID>
416 bool operator <(CopyInsertable<ID> const& L, CopyInsertable<ID> const& R) {
417 return L.data < R.data;
418 }
419
420
421 #ifdef _LIBCPP_BEGIN_NAMESPACE_STD
422 _LIBCPP_BEGIN_NAMESPACE_STD
423 #else
424 namespace std {
425 #endif
426 template <int ID>
427 struct hash< ::CopyInsertable<ID> > {
428 typedef ::CopyInsertable<ID> argument_type;
429 typedef size_t result_type;
430
431 size_t operator()(argument_type const& arg) const {
432 return arg.data;
433 }
434 };
435 template <class T, class Alloc>
436 class vector;
437 template <class T, class Alloc>
438 class deque;
439 template <class T, class Alloc>
440 class list;
441 template <class _Key, class _Value, class _Less, class _Alloc>
442 class map;
443 template <class _Key, class _Value, class _Less, class _Alloc>
444 class multimap;
445 template <class _Value, class _Less, class _Alloc>
446 class set;
447 template <class _Value, class _Less, class _Alloc>
448 class multiset;
449 template <class _Key, class _Value, class _Hash, class _Equals, class _Alloc>
450 class unordered_map;
451 template <class _Key, class _Value, class _Hash, class _Equals, class _Alloc>
452 class unordered_multimap;
453 template <class _Value, class _Hash, class _Equals, class _Alloc>
454 class unordered_set;
455 template <class _Value, class _Hash, class _Equals, class _Alloc>
456 class unordered_multiset;
457
458 #ifdef _LIBCPP_END_NAMESPACE_STD
459 _LIBCPP_END_NAMESPACE_STD
460 #else
461 } // end namespace std
462 #endif
463
464 // TCT - Test container type
465 namespace TCT {
466
467 template <class T = CopyInsertable<1>>
468 using vector = std::vector<T, ContainerTestAllocator<T, T> >;
469 template <class T = CopyInsertable<1>>
470 using deque = std::deque<T, ContainerTestAllocator<T, T> >;
471 template <class T = CopyInsertable<1>>
472 using list = std::list<T, ContainerTestAllocator<T, T> >;
473
474 template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>,
475 class ValueTp = std::pair<const Key, Value> >
476 using unordered_map =
477 std::unordered_map<Key, Value, std::hash<Key>, std::equal_to<Key>,
478 ContainerTestAllocator<ValueTp, ValueTp> >;
479
480 template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>,
481 class ValueTp = std::pair<const Key, Value> >
482 using map =
483 std::map<Key, Value, std::less<Key>,
484 ContainerTestAllocator<ValueTp, ValueTp> >;
485
486 template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>,
487 class ValueTp = std::pair<const Key, Value> >
488 using unordered_multimap =
489 std::unordered_multimap<Key, Value, std::hash<Key>, std::equal_to<Key>,
490 ContainerTestAllocator<ValueTp, ValueTp> >;
491
492 template <class Key = CopyInsertable<1>, class Value = CopyInsertable<2>,
493 class ValueTp = std::pair<const Key, Value> >
494 using multimap =
495 std::multimap<Key, Value, std::less<Key>,
496 ContainerTestAllocator<ValueTp, ValueTp> >;
497
498 template <class Value = CopyInsertable<1> >
499 using unordered_set =
500 std::unordered_set<Value, std::hash<Value>, std::equal_to<Value>,
501 ContainerTestAllocator<Value, Value> >;
502
503 template <class Value = CopyInsertable<1> >
504 using set =
505 std::set<Value, std::less<Value>, ContainerTestAllocator<Value, Value> >;
506
507 template <class Value = CopyInsertable<1> >
508 using unordered_multiset =
509 std::unordered_multiset<Value, std::hash<Value>, std::equal_to<Value>,
510 ContainerTestAllocator<Value, Value> >;
511
512 template <class Value = CopyInsertable<1> >
513 using multiset =
514 std::multiset<Value, std::less<Value>, ContainerTestAllocator<Value, Value> >;
515
516 } // end namespace TCT
517
518 #endif // SUPPORT_CONTAINER_TEST_TYPES_H
519