OXYGENHUB
◆ main openmsx / src / unittest / ObjectPool_test.cc
3.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "catch.hpp"
2
3
#include "ObjectPool.hh"
4
#include "xrange.hh"
5
#include <vector>
6
7
8
struct Tracked
9
{
10
	static inline std::vector<int> constructed;
11
	static inline std::vector<int> destructed;
12
13
	explicit Tracked(int i_) : i(i_) {
14
		constructed.push_back(i);
15
	}
16
	Tracked(const Tracked&) = delete;
17
	Tracked(Tracked&&) = delete;
18
	Tracked& operator=(const Tracked&) = delete;
19
	Tracked& operator=(Tracked&&) = delete;
20
	~Tracked() {
21
		destructed.push_back(i);
22
	}
23
	int i;
24
};
25
26
TEST_CASE("ObjectPool")
27
{
28
	REQUIRE(Tracked::constructed.empty());
29
	REQUIRE(Tracked::destructed.empty());
30
31
	{
32
		ObjectPool<Tracked> pool;
33
		CHECK(pool.capacity() == 0); // empty pool does no memory allocation
34
35
		// insert '10'
36
		auto [idx1, ptr1] = pool.emplace(10);
37
		CHECK(ptr1->i == 10);
38
		CHECK(&pool[idx1] == ptr1);
39
		CHECK(Tracked::constructed == std::vector{10});
40
		CHECK(Tracked::destructed  == std::vector<int>{});
41
		CHECK(pool.capacity() == 256); // allocates in chunks of 256
42
43
		// insert '20'
44
		auto [idx2, ptr2] = pool.emplace(20);
45
		CHECK(ptr1->i == 10);
46
		CHECK(ptr2->i == 20);
47
		CHECK(&pool[idx1] == ptr1);
48
		CHECK(&pool[idx2] == ptr2);
49
		CHECK(Tracked::constructed == std::vector{10, 20});
50
		CHECK(Tracked::destructed  == std::vector<int>{});
51
52
		// insert '30'
53
		auto [idx3, ptr3] = pool.emplace(30);
54
		CHECK(ptr1->i == 10);
55
		CHECK(ptr2->i == 20);
56
		CHECK(ptr3->i == 30);
57
		CHECK(&pool[idx1] == ptr1);
58
		CHECK(&pool[idx2] == ptr2);
59
		CHECK(&pool[idx3] == ptr3);
60
		CHECK(Tracked::constructed == std::vector{10, 20, 30});
61
		CHECK(Tracked::destructed  == std::vector<int>{});
62
63
		// remove '20'
64
		pool.remove(idx2);
65
		CHECK(ptr1->i == 10);
66
		CHECK(ptr3->i == 30);
67
		CHECK(&pool[idx1] == ptr1);
68
		CHECK(&pool[idx3] == ptr3);
69
		CHECK(Tracked::constructed == std::vector{10, 20, 30});
70
		CHECK(Tracked::destructed  == std::vector{20});
71
72
		// insert '40'
73
		auto [idx4, ptr4] = pool.emplace(40);
74
		CHECK(idx4 == idx2); // recycled
75
		CHECK(ptr1->i == 10);
76
		CHECK(ptr3->i == 30);
77
		CHECK(ptr4->i == 40);
78
		CHECK(&pool[idx1] == ptr1);
79
		CHECK(&pool[idx3] == ptr3);
80
		CHECK(&pool[idx4] == ptr4);
81
		CHECK(Tracked::constructed == std::vector{10, 20, 30, 40});
82
		CHECK(Tracked::destructed  == std::vector{20});
83
		CHECK(pool.capacity() == 256);
84
85
		// insert a lot more (force allocating more memory in the pool)
86
		for (auto i : xrange(1000)) {
87
			auto val = 1000 + i;
88
			auto [idx, ptr] = pool.emplace(val);
89
			CHECK(ptr->i == val);
90
			CHECK(&pool[idx] == ptr);
91
		}
92
		CHECK(Tracked::constructed.size() == 1004);
93
		CHECK(Tracked::destructed == std::vector{20});
94
		CHECK(pool.capacity() == 1024);
95
	}
96
97
	// When 'pool' goes out of scope it does _not_ destruct the elements it
98
	// still contained. (But it does free the memory it used to store those
99
	// elements.)
100
	CHECK(Tracked::constructed.size() == 1004);
101
	CHECK(Tracked::destructed == std::vector{20});
102
}
103
104
105
// To (manually) inspect quality of generated code
106
#if 0
107
108
auto inspect_operator(ObjectPool<int>& pool, unsigned idx)
109
{
110
	return pool[idx];
111
}
112
113
auto inspect_emplace(ObjectPool<int>& pool, int val)
114
{
115
	return pool.emplace(val);
116
}
117
118
void inspect_remove(ObjectPool<int>& pool, unsigned idx)
119
{
120
	return pool.remove(idx);
121
}
122
123
#endif