Examples.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "UserTypes.h"
  9. #include <AzCore/std/typetraits/typetraits.h>
  10. #include <AzCore/std/parallel/atomic.h>
  11. #include <AzCore/std/parallel/mutex.h>
  12. #include <AzCore/std/parallel/semaphore.h>
  13. #include <AzCore/std/parallel/lock.h>
  14. #include <AzCore/std/containers/vector.h>
  15. #include <AzCore/std/containers/fixed_vector.h>
  16. #include <AzCore/std/containers/array.h>
  17. #include <AzCore/std/containers/deque.h>
  18. #include <AzCore/std/containers/list.h>
  19. #include <AzCore/std/containers/fixed_list.h>
  20. #include <AzCore/std/containers/intrusive_list.h>
  21. #include <AzCore/std/containers/queue.h>
  22. #include <AzCore/std/containers/stack.h>
  23. #include <AzCore/std/containers/unordered_map.h>
  24. #include <AzCore/std/containers/unordered_set.h>
  25. namespace UnitTest
  26. {
  27. using namespace AZStd;
  28. using namespace UnitTestInternal;
  29. class TypeTraitExamples
  30. {
  31. public:
  32. void run()
  33. {
  34. // TypeTraitExample-Begin
  35. // Checks the alignment of different types.
  36. AZ_TEST_STATIC_ASSERT(alignment_of<int>::value == 4);
  37. AZ_TEST_STATIC_ASSERT(alignment_of<char>::value == 1);
  38. AZ_TEST_STATIC_ASSERT(alignment_of<MyClass>::value == 16);
  39. // aligned_storage example
  40. // Create an int type aligned on 32 bytes.
  41. typedef aligned_storage<sizeof(int), 32>::type int_aligned32_type;
  42. AZ_TEST_STATIC_ASSERT((alignment_of<int_aligned32_type>::value) == 32);
  43. // aligned_storage example
  44. // Declare a buffer of 100 bytes, aligned on 16 bytes. (don't use more than 16 bytes alignment on the stack. It doesn't work on all platforms.)
  45. typedef aligned_storage<100, 16>::type buffer100_16alinged_type;
  46. // Check that our type is aligned on 16 bytes
  47. AZ_TEST_STATIC_ASSERT((alignment_of< buffer100_16alinged_type>::value) == 16);
  48. buffer100_16alinged_type myAlignedBuffer;
  49. // Make sure the buffer pointer is aligned to 16 bytes.
  50. AZ_TEST_ASSERT((AZStd::size_t(&myAlignedBuffer) & 15) == 0);
  51. // POD
  52. // Checks if a type is POD (Plain Old Data).
  53. AZ_TEST_STATIC_ASSERT(is_pod<MyStruct>::value == true);
  54. AZ_TEST_STATIC_ASSERT(is_pod<MyClass>::value == false);
  55. // TypeTraitExample-End
  56. }
  57. };
  58. TEST(Allocator, Examples)
  59. {
  60. // AllocatorExamples-Begin
  61. // Sharing allocator between containers
  62. {
  63. // I will use a 16 KB static_buffer_allocator (on the stack) for this sample. You can use any of your own allocators
  64. // unless they don't already point to your memory manager (which is the common way to use STL allocators)
  65. typedef static_buffer_allocator<16*1024, 16> static_buffer_16KB_aligned16;
  66. static_buffer_16KB_aligned16 bufferAllocator;
  67. typedef allocator_ref<static_buffer_16KB_aligned16> static_buffer_16KB_aligned16_ref;
  68. static_buffer_16KB_aligned16_ref sharedAllocator(bufferAllocator);
  69. // All containers will allocator from the same buffer. Here it is not
  70. // important that we will never actually free the data because of the static_buffer_allocator.
  71. // But if we consider that fact this is great example for some temporary containers, when
  72. // we don't want to even involve any memory managers.
  73. vector<int, static_buffer_16KB_aligned16_ref> int_vector(sharedAllocator);
  74. list<float, static_buffer_16KB_aligned16_ref> float_list(sharedAllocator);
  75. deque<MyClass, static_buffer_16KB_aligned16_ref> myclass_deque(sharedAllocator);
  76. }
  77. // AllocatorExamples-End
  78. }
  79. class ContainersExamples
  80. : public LeakDetectionFixture
  81. {
  82. public:
  83. void Array()
  84. {
  85. // ArrayExamples-Begin
  86. // Array class is like a regular C array, but if gives it container functionality.
  87. // All elements are initialized, when the array is created.
  88. // Create an array of 5 ints. All elements will be uninitialized (they will call the default ctor)
  89. array<int, 5> int10_uninit_array;
  90. // Array of 5 ints initialized to some values.
  91. array<int, 5> int10_init_array = {
  92. {1, 2, 3, 4, 5}
  93. };
  94. // just check if the first element is 1 and last is 5
  95. AZ_TEST_ASSERT(int10_init_array[0] == 1);
  96. AZ_TEST_ASSERT(int10_init_array[4] == 5);
  97. // set all elements to 11
  98. int10_init_array.fill(11);
  99. AZ_TEST_ASSERT(int10_init_array[0] == 11);
  100. AZ_TEST_ASSERT(int10_init_array[4] == 11);
  101. // Create an array of my class with default init.
  102. array<MyClass, 5> myclass_array;
  103. // default value for MyClass::m_data is 10, verify this.
  104. AZ_TEST_ASSERT(myclass_array[0].m_data == 10);
  105. AZ_TEST_ASSERT(myclass_array[4].m_data == 10);
  106. // My class pointer should be aligned on 32 bytes so verify this too.
  107. AZ_TEST_ASSERT((((AZStd::size_t)&myclass_array[0]) & (alignment_of<MyClass>::value - 1)) == 0);
  108. // ArrayExamples-End
  109. (void)int10_uninit_array;
  110. }
  111. void Vector()
  112. {
  113. // VectorExamples-Begin
  114. // Int vector using the default allocator
  115. typedef vector<int> int_vector_type;
  116. // 100 constant elements.
  117. {
  118. // Bad way, lot's of allocations and slow
  119. int_vector_type int_vec1;
  120. for (int i = 0; i < 100; ++i)
  121. {
  122. int_vec1.push_back(10);
  123. }
  124. // Correct ways...
  125. int_vector_type int_vec2(100, 10); // Best way
  126. int_vec1.resize(100, 10); // Similar with a few more function calls
  127. int_vec1.assign(100, 10); // Similar with even more function calls
  128. }
  129. // 100 random values (0 to 99 in this example)
  130. {
  131. // Bad way, lot's of allocation and slow
  132. int_vector_type int_vec1;
  133. for (int i = 0; i < 100; ++i)
  134. {
  135. int_vec1.push_back(i);
  136. }
  137. // Bad way, one allocation but pointless copies
  138. int_vec1.resize(100, 0 /*reset to 0*/); // one allocation, but sets all the values to 0
  139. for (int i = 0; i < 100; ++i)
  140. {
  141. int_vec1[i] = i;
  142. }
  143. // Bad because it's tricky (sometimes correct)
  144. int_vec1.resize(100); // AZStd extension. This will work fast only for POD data types (like this int is), otherwise it will call default ctor.
  145. for (int i = 0; i < 100; ++i)
  146. {
  147. int_vec1[i] = i;
  148. }
  149. // Correct way
  150. int_vec1.reserve(100); // as part of the standard or you can use AZStd extension set_capacity(), this will trim is down if necessary
  151. for (int i = 0; i < 100; ++i)
  152. {
  153. int_vec1.push_back(i);
  154. }
  155. }
  156. // Copy values from other containers
  157. {
  158. int_vector_type int_vec1(100, 10);
  159. int_vector_type int_vec2;
  160. // Bad ways
  161. // Slow with many allocations
  162. for (int_vector_type::size_type i = 0; i < int_vec1.size(); ++i)
  163. {
  164. int_vec2.push_back(int_vec1[i]);
  165. }
  166. // Correct if it's the same type
  167. int_vector_type int_vec3(int_vec1);
  168. int_vector_type int_vec4 = int_vec1;
  169. // Correct from different types
  170. list<int> int_list(10, 10);
  171. array<int, 4> int_array = {
  172. {1, 2, 3, 4}
  173. };
  174. // C array
  175. int int_carray[] = {1, 2, 3, 4};
  176. int_vector_type int_vec5(int_list.begin(), int_list.end());
  177. int_vector_type int_vec6(int_array.begin(), int_array.end());
  178. int_vector_type int_vec7(&int_carray[0], &int_carray[4]);
  179. }
  180. //
  181. // As you know from STL avoid using insert and erase on a vector, since they are slow operations.
  182. //
  183. // Clearing a container
  184. {
  185. int_vector_type int_vec1(100, 55); // 100 elements, 55 value
  186. // Bad way
  187. while (!int_vec1.empty())
  188. {
  189. int_vec1.pop_back();
  190. }
  191. // Correct ways
  192. int_vec1.clear();
  193. int_vec1.erase(int_vec1.begin(), int_vec1.end()); // a few more function calls than clear
  194. // If you want to clear and make sure we free the memory.
  195. int_vec1.set_capacity(0); // AZStd extension.
  196. }
  197. // Exchanging the content of 2 vectors
  198. {
  199. int_vector_type int_vec1(100, 10);
  200. int_vector_type int_vec2(10, 11);
  201. // Only one way is correct, everything else is BAD (even if the allocators are different, it will do the proper job as fast as possible).
  202. int_vec1.swap(int_vec2);
  203. }
  204. // Quick tear-down (leak_and_reset extension)
  205. {
  206. // Assuming you have your own temporary allocators, I will use static_buffer_allocator for this sample.
  207. // \note this allocator already instruct the vector the he doesn't need to delete it's memory
  208. typedef static_buffer_allocator<16*1024, 1> static_buffer_16KB;
  209. // Add 100 elements on the stack
  210. // and YES having fixed_vector<int, (16*1024)/sizeof(T) > is the same.
  211. vector<int, static_buffer_16KB> tempVector(100, 10);
  212. // .. do some crazy operations, sorting whatever...
  213. // clearing (when the can afford to NOT call the destructor - it will not leak or something), otherwise just use the regular functions.
  214. // Bad ways, although all the bad way will be work as fast for POD data types, we consider them tricky, because you rely on the vector value_type.
  215. tempVector.clear(); // even it will not free any memory, it will call if the type is not POD.
  216. tempVector.erase(tempVector.begin(), tempVector.end());
  217. tempVector.set_capacity(0);
  218. // Correct way to NOT call the dtor.
  219. // IMPORTANT: Leak and reset can be used on normal vectors too for instance if you have garbage collector, you are
  220. // just exiting the process and rely on somebody else to clean after you.
  221. tempVector.leak_and_reset();
  222. }
  223. // Allocators
  224. {
  225. // I will use static_buffer_allocator for this sample.
  226. typedef static_buffer_allocator<16*1024, 1> static_buffer_16KB;
  227. static_buffer_16KB otherAllocator;
  228. // change allocator name
  229. // All of this depends if your allocator assignment is slow/expensive. Otherwise this is valid code
  230. vector<int, static_buffer_16KB> int_vec1(100, 10, static_buffer_16KB());
  231. // Changing the allocator, will force the vector to re-allocate itself, if it has any elements.
  232. int_vec1.set_allocator(otherAllocator);
  233. // As in the allocators sample, you can share an allocator if you the allocator_ref.
  234. typedef allocator_ref<static_buffer_16KB> static_buffer_16KB_ref;
  235. // Both int_vec2 and int_vec3 will allocate from the otherAllocator.
  236. static_buffer_16KB_ref sharedAlloc(otherAllocator);
  237. vector<int, static_buffer_16KB_ref> int_vec2(sharedAlloc);
  238. vector<int, static_buffer_16KB_ref> int_vec3(sharedAlloc);
  239. // using the container allocator, for other purpose... allocate 100 bytes on 16 byte alignment
  240. void* myData = int_vec2.get_allocator().allocate(100, 16);
  241. // do something...
  242. // free if you should, in the static_buffer_allocator you should not care about this.
  243. int_vec2.get_allocator().deallocate(myData, 100, 16);
  244. }
  245. // VectorExamples-End
  246. }
  247. void List()
  248. {
  249. // ListExamples-Begin
  250. // Use the list node type to pre allocate memory pools.
  251. {
  252. // One of the futures of the AZStd containers, that we node allocation type for each container (not only the list).
  253. // This allows us to know at compile time the size of the allocations (vector class is exception).
  254. // This example if very similar to what this fixed_list container does.
  255. // Some let's create pool for int list nodes.
  256. typedef static_pool_allocator< list<int>::node_type, 1000 > int_list_pool_allocator_type;
  257. typedef allocator_ref<int_list_pool_allocator_type> int_pool_alloc_ref_type;
  258. int_list_pool_allocator_type myPool;
  259. int_pool_alloc_ref_type myPoolRef(myPool);
  260. // Now we want to share that pool in multiple containers.
  261. list<int, int_pool_alloc_ref_type> int_list(myPoolRef);
  262. list<int, int_pool_alloc_ref_type> int_list1(myPoolRef);
  263. // in addition we can use the pool to allocate nodes that a smaller than the int type.
  264. list<char, int_pool_alloc_ref_type> char_list(myPoolRef);
  265. list<short, int_pool_alloc_ref_type> short_list(myPoolRef);
  266. // Now all of the above containers will allocate from the list<int> pool.
  267. int_list.assign(10, 202);
  268. AZ_TEST_ASSERT(int_list.size() == 10);
  269. AZ_TEST_ASSERT(int_list.front() == 202);
  270. int_list1.assign(10, 302);
  271. AZ_TEST_ASSERT(int_list1.size() == 10);
  272. AZ_TEST_ASSERT(int_list1.front() == 302);
  273. char_list.assign(30, (char)120);
  274. AZ_TEST_ASSERT(char_list.size() == 30);
  275. AZ_TEST_ASSERT(char_list.front() == 120);
  276. short_list.assign(20, (short)32000);
  277. AZ_TEST_ASSERT(short_list.size() == 20);
  278. AZ_TEST_ASSERT(short_list.front() == 32000);
  279. // Now after we did some work with the containers, we can tear them down faster. Which is another example of the
  280. // use of leak_and_reset.
  281. // If you look at the static_pool_allocator allocator, you will notice that the deallocate function returns the allocated node
  282. // to the pool. At this moment we don't really want to do that since we will not use this pool anymore and the memory will be free once we
  283. // destroy the pool. On the other hand we use integral type (which is POD types) and we don't need to worry about the destructor at all... so to be fast
  284. // instead of deallocating each node on it's own.
  285. int_list.leak_and_reset();
  286. int_list1.leak_and_reset();
  287. char_list.leak_and_reset();
  288. short_list.leak_and_reset();
  289. myPool.leak_before_destroy(); // tell the pool it's ok that we have allocated nodes.
  290. }
  291. // ListExamples-End
  292. }
  293. void Deque()
  294. {
  295. // DequeExamples-Begin
  296. // Customize the deque so it fits better our allocation needs
  297. {
  298. // Specialize the deque so we do allocate 20 int at in a block. If you look at the default
  299. // settings you can see for 4 byte types we will allocate blocks with 4 elements. 20 can be a little wasteful,
  300. // but a lot lass allocations will happen.
  301. deque<int, AZStd::allocator, 20> int_deque;
  302. int_deque.push_back(10);
  303. int_deque.push_front(11);
  304. AZ_TEST_ASSERT(int_deque.size() == 2);
  305. AZ_TEST_ASSERT(int_deque.front() == 11);
  306. AZ_TEST_ASSERT(int_deque.back() == 10);
  307. }
  308. // DequeExamples-End
  309. }
  310. void Hashed()
  311. {
  312. #ifdef AZ_PLATFORM_WINDOWS // Just to make sure examples actually work
  313. // UnorderedExamples-Begin
  314. // Advanced examples. This examples may be a little hard to read or understand if you are familiar with
  315. // way Hashed containers work. Read about hash_table and check the papers references.
  316. // Keep in mind that this customizations and speed ups should be used if you really know what they do, and
  317. // you really need it!
  318. // You should try to be complaint with the standard wherever possible, to avoid problems
  319. // if you use other STL. In 99% of the cases copying keys and value types is very fast or doesn't happed as often at all.
  320. // So using this you will make the code look more complicated and not compatible with the standard for no any real benefit.
  321. // Of course if you use these containers in rendering code for example and you insert hundreds (or even thousands) entries every frame,
  322. // these examples might help a lot.
  323. //////////////////////////////////////////////////////////////////////////
  324. // Some example classes we use
  325. struct MyExpensiveKeyType
  326. {
  327. MyExpensiveKeyType()
  328. : m_keyData(0) { /* expensive operations */ }
  329. MyExpensiveKeyType(int data)
  330. : m_keyData(data) { /* expensive operations */ }
  331. AZStd::size_t GetHashed() const { return m_keyData; /* just some hashing function */ }
  332. bool IsEqual(const MyExpensiveKeyType& rhs) const { return m_keyData == rhs.m_keyData; }
  333. int m_keyData;
  334. };
  335. // KeyHasher
  336. struct MyExpensiveKeyHasher
  337. {
  338. AZStd::size_t operator()(const MyExpensiveKeyType& k) const
  339. {
  340. return k.GetHashed();
  341. }
  342. };
  343. // KeyTypeCompare
  344. struct MyExpensiveKeyEqualTo
  345. {
  346. AZ_FORCE_INLINE bool operator()(const MyExpensiveKeyType& left, const MyExpensiveKeyType& right) const { return left.IsEqual(right); }
  347. // We use this class to compare the AZStd::size_t to our key type.
  348. AZ_FORCE_INLINE bool operator()(const AZStd::size_t leftKey, const MyExpensiveKeyType& right) const { return (int)leftKey == right.m_keyData; }
  349. };
  350. // Map expensive value type.
  351. class MyExpensiveValueType
  352. {
  353. public:
  354. MyExpensiveValueType()
  355. : m_data(0) { /* expensive operations */ }
  356. MyExpensiveValueType(int data)
  357. : m_data(data) { /* expensive operations */ }
  358. private:
  359. [[maybe_unused]] int m_data;
  360. };
  361. //////////////////////////////////////////////////////////////////////////
  362. // Customization for expensive value type.
  363. {
  364. // Let's say the MyExpensiveValueType is expensive to construct. Ex. Does allocations register itself in systems, etc.
  365. // so if we have the map with it.
  366. // - In many cases people try to avoid this by storing pointer in the container
  367. // so the value type is not expensive to move around, but even if this works. It's not the idea of the container.
  368. typedef unordered_map<int, MyExpensiveValueType> myclass_map_type;
  369. myclass_map_type myMap;
  370. int myNewClassKey = 100;
  371. // So to use the regular insert, we need to create temp pair. Even if the key exists, or we don't really have a source for
  372. // for MyClass. Then this insert can be a big overhead.
  373. // - People sometimes try to fix this problem by calling myMap.find(myNewClassKey) to see if it's there and then make
  374. // the pair, this is ok. But you do the find 2 times... if it's not there. Even time is constant time operation O(1) it's pointless.
  375. myclass_map_type::value_type tempPair = AZStd::make_pair(myNewClassKey, MyExpensiveValueType());
  376. myMap.insert(tempPair);
  377. // When we don't have MyClass source value, a call to the extension insert_key() will to the job.
  378. myMap.insert_key(myNewClassKey); // So if the key doesn't't exist it will insert pair with default value. Which is g).
  379. }
  380. // Customization for expensive key type
  381. {
  382. // Like the example with word counting... in the 14CrazyIdeas paper. Sometimes the key can be expensive.
  383. typedef unordered_set<MyExpensiveKeyType, MyExpensiveKeyHasher, MyExpensiveKeyEqualTo> myclass_set_type;
  384. myclass_set_type mySet;
  385. // Let say as in the above example MyClass is expensive to construct, copy, etc. But we know can know the search key (we use hash
  386. // values for the unordered set).
  387. AZStd::size_t myNewClassKey = 101; // So we know the key (that hash_function(MyClass) will return, a good practical example is when you have string literal and you can get the hash without making string object)
  388. // This way you don't need to make the expensive MyClass object at all. This the situation is the same for maps. We need
  389. // to provide functions how to compare AZStd::size_t (in this case) to MyExpensiveKeyType (in this case). Which MyExpensiveKeyEqualTo class does.
  390. mySet.find_as(myNewClassKey, AZStd::hash<AZStd::size_t>(), MyExpensiveKeyEqualTo());
  391. }
  392. // Customization for expensive key or value with non default ctor
  393. {
  394. // This is most complicated of advanced example. It shows how to customize the insert function for both complex key and complex value types.
  395. typedef unordered_map<MyExpensiveKeyType, MyExpensiveValueType, MyExpensiveKeyHasher, MyExpensiveKeyEqualTo> expensive_map_type;
  396. // So again for the sake of discussion let's say MyExpensiveValueType is expensive to construct and the key MyExpensiveKeyType is expensive too.
  397. // As we saw from prev examples we can compare they key quickly to AZStd::size_t if we know the comparable to key, without making the expensive key
  398. // class. But this time if they key is not found we you like to construct the expensive value type with an input (non default)... so we have the fallowinf
  399. // quick insert struct...
  400. struct QuickInsert
  401. {
  402. AZStd::size_t m_comparebleToKey;
  403. int m_keyInput;
  404. int m_valueInput;
  405. };
  406. // Then we need a converter class that will convert form this struct to the map key and value
  407. struct Converter
  408. {
  409. typedef AZStd::size_t key_type; // required because we might use the Map::key_type or Comparable to Key type.
  410. const key_type& to_key(const QuickInsert& qi) const { return qi.m_comparebleToKey; }
  411. expensive_map_type::value_type to_value(const QuickInsert& qi) const
  412. {
  413. // This is the place where the expensive ctors will be called only of really necessary
  414. return AZStd::make_pair(MyExpensiveKeyType(qi.m_keyInput), MyExpensiveValueType(qi.m_valueInput));
  415. }
  416. };
  417. Converter convQuickInsertToMapType;
  418. expensive_map_type myMap;
  419. QuickInsert qi;
  420. qi.m_comparebleToKey = 10;
  421. qi.m_keyInput = 100; ///< Input for ctors or whatever
  422. qi.m_valueInput = 200; ///< Input for ctors or whatever
  423. myMap.insert_from(qi, convQuickInsertToMapType, AZStd::hash<AZStd::size_t>() /*hasher for the comparable to key*/, MyExpensiveKeyEqualTo());
  424. // And that's about it, this way you will do a fast (find_as like) compare without constructing the key, if doesn't exist, the insert
  425. // structure (QuickInsert) will be converted to the map types (expensive). This way if they key is in the map, it will be lightning fast.
  426. // Other example of the same thing imagine you have a map unordered_map<string,ExpensiveObject> everytime when you want to insert an object
  427. // and name is not string but string literal, you will one or more copies of the string object. Just to compute the key. You can compute (hash)
  428. // the string literal 100% the same as the string. This way you can avoid creating string object. This example is similar to the word count example
  429. // in the lazy_insert sample.
  430. }
  431. // UnorderedExamples-End
  432. #endif // AZ_PLATFORM_WINDOWS
  433. }
  434. };
  435. TEST_F(ContainersExamples, Array)
  436. {
  437. Array();
  438. }
  439. TEST_F(ContainersExamples, Vector)
  440. {
  441. Vector();
  442. }
  443. TEST_F(ContainersExamples, List)
  444. {
  445. List();
  446. }
  447. TEST_F(ContainersExamples, Deque)
  448. {
  449. Deque();
  450. }
  451. TEST_F(ContainersExamples, Hashed)
  452. {
  453. Hashed();
  454. }
  455. }