ObjectPool_test.cc 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #include "catch.hpp"
  2. #include "ObjectPool.hh"
  3. #include <vector>
  4. struct Tracked
  5. {
  6. static inline std::vector<int> constructed;
  7. static inline std::vector<int> destructed;
  8. Tracked(int i_) : i(i_) {
  9. constructed.push_back(i);
  10. }
  11. ~Tracked() {
  12. destructed.push_back(i);
  13. }
  14. int i;
  15. };
  16. TEST_CASE("ObjectPool")
  17. {
  18. REQUIRE(Tracked::constructed.empty());
  19. REQUIRE(Tracked::destructed.empty());
  20. {
  21. ObjectPool<Tracked> pool;
  22. CHECK(pool.capacity() == 0); // empty pool does no memory allocation
  23. // insert '10'
  24. auto [idx1, ptr1] = pool.emplace(10);
  25. CHECK(ptr1->i == 10);
  26. CHECK(&pool[idx1] == ptr1);
  27. CHECK(Tracked::constructed == std::vector{10});
  28. CHECK(Tracked::destructed == std::vector<int>{});
  29. CHECK(pool.capacity() == 256); // allocates in chunks of 256
  30. // insert '20'
  31. auto [idx2, ptr2] = pool.emplace(20);
  32. CHECK(ptr1->i == 10);
  33. CHECK(ptr2->i == 20);
  34. CHECK(&pool[idx1] == ptr1);
  35. CHECK(&pool[idx2] == ptr2);
  36. CHECK(Tracked::constructed == std::vector{10, 20});
  37. CHECK(Tracked::destructed == std::vector<int>{});
  38. // insert '30'
  39. auto [idx3, ptr3] = pool.emplace(30);
  40. CHECK(ptr1->i == 10);
  41. CHECK(ptr2->i == 20);
  42. CHECK(ptr3->i == 30);
  43. CHECK(&pool[idx1] == ptr1);
  44. CHECK(&pool[idx2] == ptr2);
  45. CHECK(&pool[idx3] == ptr3);
  46. CHECK(Tracked::constructed == std::vector{10, 20, 30});
  47. CHECK(Tracked::destructed == std::vector<int>{});
  48. // remove '20'
  49. pool.remove(idx2);
  50. CHECK(ptr1->i == 10);
  51. CHECK(ptr3->i == 30);
  52. CHECK(&pool[idx1] == ptr1);
  53. CHECK(&pool[idx3] == ptr3);
  54. CHECK(Tracked::constructed == std::vector{10, 20, 30});
  55. CHECK(Tracked::destructed == std::vector{20});
  56. // insert '40'
  57. auto [idx4, ptr4] = pool.emplace(40);
  58. CHECK(idx4 == idx2); // recycled
  59. CHECK(ptr1->i == 10);
  60. CHECK(ptr3->i == 30);
  61. CHECK(ptr4->i == 40);
  62. CHECK(&pool[idx1] == ptr1);
  63. CHECK(&pool[idx3] == ptr3);
  64. CHECK(&pool[idx4] == ptr4);
  65. CHECK(Tracked::constructed == std::vector{10, 20, 30, 40});
  66. CHECK(Tracked::destructed == std::vector{20});
  67. CHECK(pool.capacity() == 256);
  68. // insert a lot more (force allocating more memory in the pool)
  69. for (int i = 0; i < 1000; ++i) {
  70. auto val = 1000 + i;
  71. auto [idx, ptr] = pool.emplace(val);
  72. CHECK(ptr->i == val);
  73. CHECK(&pool[idx] == ptr);
  74. }
  75. CHECK(Tracked::constructed.size() == 1004);
  76. CHECK(Tracked::destructed == std::vector{20});
  77. CHECK(pool.capacity() == 1024);
  78. }
  79. // When 'pool' goes out of scope it does _not_ destruct the elements it
  80. // still contained. (But it does free the memory it used to store those
  81. // elements.)
  82. CHECK(Tracked::constructed.size() == 1004);
  83. CHECK(Tracked::destructed == std::vector{20});
  84. }
  85. // To (manually) inspect quality of generated code
  86. #if 0
  87. auto inspect_operator(ObjectPool<int>& pool, unsigned idx)
  88. {
  89. return pool[idx];
  90. }
  91. auto inspect_emplace(ObjectPool<int>& pool, int val)
  92. {
  93. return pool.emplace(val);
  94. }
  95. void inspect_remove(ObjectPool<int>& pool, unsigned idx)
  96. {
  97. return pool.remove(idx);
  98. }
  99. #endif