123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- #include "catch.hpp"
- #include "ObjectPool.hh"
- #include <vector>
- struct Tracked
- {
- static inline std::vector<int> constructed;
- static inline std::vector<int> destructed;
- Tracked(int i_) : i(i_) {
- constructed.push_back(i);
- }
- ~Tracked() {
- destructed.push_back(i);
- }
- int i;
- };
- TEST_CASE("ObjectPool")
- {
- REQUIRE(Tracked::constructed.empty());
- REQUIRE(Tracked::destructed.empty());
- {
- ObjectPool<Tracked> pool;
- CHECK(pool.capacity() == 0); // empty pool does no memory allocation
- // insert '10'
- auto [idx1, ptr1] = pool.emplace(10);
- CHECK(ptr1->i == 10);
- CHECK(&pool[idx1] == ptr1);
- CHECK(Tracked::constructed == std::vector{10});
- CHECK(Tracked::destructed == std::vector<int>{});
- CHECK(pool.capacity() == 256); // allocates in chunks of 256
- // insert '20'
- auto [idx2, ptr2] = pool.emplace(20);
- CHECK(ptr1->i == 10);
- CHECK(ptr2->i == 20);
- CHECK(&pool[idx1] == ptr1);
- CHECK(&pool[idx2] == ptr2);
- CHECK(Tracked::constructed == std::vector{10, 20});
- CHECK(Tracked::destructed == std::vector<int>{});
- // insert '30'
- auto [idx3, ptr3] = pool.emplace(30);
- CHECK(ptr1->i == 10);
- CHECK(ptr2->i == 20);
- CHECK(ptr3->i == 30);
- CHECK(&pool[idx1] == ptr1);
- CHECK(&pool[idx2] == ptr2);
- CHECK(&pool[idx3] == ptr3);
- CHECK(Tracked::constructed == std::vector{10, 20, 30});
- CHECK(Tracked::destructed == std::vector<int>{});
- // remove '20'
- pool.remove(idx2);
- CHECK(ptr1->i == 10);
- CHECK(ptr3->i == 30);
- CHECK(&pool[idx1] == ptr1);
- CHECK(&pool[idx3] == ptr3);
- CHECK(Tracked::constructed == std::vector{10, 20, 30});
- CHECK(Tracked::destructed == std::vector{20});
- // insert '40'
- auto [idx4, ptr4] = pool.emplace(40);
- CHECK(idx4 == idx2); // recycled
- CHECK(ptr1->i == 10);
- CHECK(ptr3->i == 30);
- CHECK(ptr4->i == 40);
- CHECK(&pool[idx1] == ptr1);
- CHECK(&pool[idx3] == ptr3);
- CHECK(&pool[idx4] == ptr4);
- CHECK(Tracked::constructed == std::vector{10, 20, 30, 40});
- CHECK(Tracked::destructed == std::vector{20});
- CHECK(pool.capacity() == 256);
- // insert a lot more (force allocating more memory in the pool)
- for (int i = 0; i < 1000; ++i) {
- auto val = 1000 + i;
- auto [idx, ptr] = pool.emplace(val);
- CHECK(ptr->i == val);
- CHECK(&pool[idx] == ptr);
- }
- CHECK(Tracked::constructed.size() == 1004);
- CHECK(Tracked::destructed == std::vector{20});
- CHECK(pool.capacity() == 1024);
- }
- // When 'pool' goes out of scope it does _not_ destruct the elements it
- // still contained. (But it does free the memory it used to store those
- // elements.)
- CHECK(Tracked::constructed.size() == 1004);
- CHECK(Tracked::destructed == std::vector{20});
- }
- // To (manually) inspect quality of generated code
- #if 0
- auto inspect_operator(ObjectPool<int>& pool, unsigned idx)
- {
- return pool[idx];
- }
- auto inspect_emplace(ObjectPool<int>& pool, int val)
- {
- return pool.emplace(val);
- }
- void inspect_remove(ObjectPool<int>& pool, unsigned idx)
- {
- return pool.remove(idx);
- }
- #endif
|