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