1 // -*- coding: utf-8 -*-
2 //
3 // Unit tests for reference counting and smart pointer classes
4
5 #include <iostream>
6 #include <utility>
7
8 #include <cstdlib> // EXIT_SUCCESS
9
10 #include <simgear/misc/test_macros.hxx>
11
12 #include "SGSharedPtr.hxx"
13 #include "SGWeakPtr.hxx"
14
15 static int instance_count = 0;
16 struct ReferenceCounted:
17 public SGReferenced
18 {
ReferenceCountedReferenceCounted19 ReferenceCounted()
20 {
21 ++instance_count;
22 }
23
~ReferenceCountedReferenceCounted24 ~ReferenceCounted()
25 {
26 --instance_count;
27 }
28 };
29 typedef SGSharedPtr<ReferenceCounted> RefPtr;
30
test_SGSharedPtr()31 void test_SGSharedPtr()
32 {
33 std::cout << "Testing SGSharedPtr and SGReferenced" << std::endl;
34
35 SG_CHECK_EQUAL( ReferenceCounted::count(0), 0 );
36
37 RefPtr ptr( new ReferenceCounted() );
38 SG_CHECK_EQUAL( instance_count, 1 );
39 SG_CHECK_EQUAL( ReferenceCounted::count(ptr.get()), 1 );
40 SG_CHECK_EQUAL( ptr.getNumRefs(), 1 );
41
42 // Test SGSharedPtr's copy assignment operator
43 RefPtr ptr2 = ptr;
44 SG_CHECK_EQUAL( ptr.getNumRefs(), 2 );
45 SG_CHECK_EQUAL( ptr2.getNumRefs(), 2 );
46
47 SG_CHECK_EQUAL( ptr, ptr2 );
48 SG_CHECK_EQUAL( ptr.get(), ptr2.get() );
49
50 // Test SGSharedPtr::reset() with no argument
51 ptr.reset();
52 SG_CHECK_IS_NULL( ptr.get() );
53 SG_CHECK_EQUAL( ptr.getNumRefs(), 0 );
54 SG_CHECK_EQUAL( ReferenceCounted::count(ptr2.get()), 1 );
55 SG_CHECK_EQUAL( ptr2.getNumRefs(), 1 );
56
57 ptr2.reset();
58 SG_CHECK_IS_NULL( ptr2.get() );
59 SG_CHECK_EQUAL( ptr.getNumRefs(), 0 );
60 SG_CHECK_EQUAL( ptr2.getNumRefs(), 0 );
61 SG_CHECK_EQUAL( instance_count, 0) ;
62
63 // Test operator==() and operator!=() for SGSharedPtr
64 {
65 RefPtr ptrA(new ReferenceCounted());
66 RefPtr ptrB(ptrA);
67 RefPtr ptrC(new ReferenceCounted());
68 RefPtr emptyPtr{};
69 SG_CHECK_EQUAL( ptrA, ptrB );
70 SG_CHECK_EQUAL( ptrA.get(), ptrB.get() ); // same thing by definition
71 SG_CHECK_NE( ptrA, ptrC );
72 SG_CHECK_NE( ptrA.get(), ptrC.get() );
73 SG_CHECK_NE( ptrB, ptrC );
74 SG_CHECK_NE( ptrA, emptyPtr );
75 SG_CHECK_EQUAL( emptyPtr, emptyPtr );
76 }
77
78 // Test SGSharedPtr::reset(T* p) and SGSharedPtr::operator T*()
79 {
80 RefPtr ptrA(new ReferenceCounted());
81 SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 );
82
83 RefPtr ptrB(new ReferenceCounted());
84 SG_CHECK_NE( ptrA, ptrB );
85 ptrB.reset(ptrA);
86 SG_CHECK_EQUAL( ptrA, ptrB );
87 SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 );
88 SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 );
89
90 RefPtr ptrC(new ReferenceCounted());
91 SG_CHECK_NE( ptrA, ptrC );
92 SG_CHECK_EQUAL( ptrC.getNumRefs(), 1 );
93 // ptrA is implicit converted to ReferenceCounted*
94 ptrC.reset(ptrA);
95 SG_CHECK_EQUAL( ptrA.getNumRefs(), 3 );
96 SG_CHECK_EQUAL( ptrB.getNumRefs(), 3 );
97 SG_CHECK_EQUAL( ptrC.getNumRefs(), 3 );
98 SG_CHECK_EQUAL( ptrA, ptrB );
99 SG_CHECK_EQUAL( ptrB, ptrC );
100 }
101
102 // Test SGSharedPtr's copy constructor
103 {
104 RefPtr ptrA(new ReferenceCounted());
105 SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 );
106
107 RefPtr ptrB(ptrA);
108 SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 );
109 SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 );
110 SG_CHECK_EQUAL( ptrA, ptrB );
111 }
112
113 // Test SGSharedPtr's move constructor
114 {
115 RefPtr ptrA(new ReferenceCounted());
116 RefPtr ptrB(ptrA);
117 RefPtr ptrC(std::move(ptrA));
118
119 SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 );
120 SG_CHECK_EQUAL( ptrC.getNumRefs(), 2 );
121 SG_CHECK_EQUAL( ptrB, ptrC );
122 // Although our implementation has these two properties, they are
123 // absolutely *not* guaranteed by the C++ move semantics:
124 SG_CHECK_EQUAL( ptrA.getNumRefs(), 0 );
125 SG_CHECK_IS_NULL( ptrA.get() );
126 }
127
128 // Test SGSharedPtr's move assignment operator: self-move, supposedly
129 // undefined behavior but certainly safer as a no-op---which the
130 // copy-and-swap idiom offers for free.
131 {
132 RefPtr ptrA(new ReferenceCounted());
133 RefPtr ptrB(ptrA);
134
135 ptrA = std::move(ptrA);
136 SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 );
137 SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 );
138 SG_CHECK_IS_NOT_NULL( ptrA.get() );
139 SG_CHECK_EQUAL( ptrA, ptrB );
140 }
141
142 // Test SGSharedPtr's move assignment operator: move to an empty SGSharedPtr
143 {
144 RefPtr ptrA;
145 RefPtr ptrB(new ReferenceCounted());
146
147 ptrA = std::move(ptrB);
148 SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 );
149 SG_CHECK_IS_NOT_NULL( ptrA.get() );
150 // Implementation detail that is *not* guaranteed by the C++ move
151 // semantics:
152 SG_CHECK_EQUAL( ptrB.getNumRefs(), 0 );
153 SG_CHECK_IS_NULL( ptrB.get() );
154 }
155
156 // Test SGSharedPtr's move assignment operator: move to a non-empty
157 // SGSharedPtr
158 {
159 RefPtr ptrA(new ReferenceCounted());
160 RefPtr ptrB(ptrA);
161 RefPtr ptrC(new ReferenceCounted());
162
163 SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 );
164 SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 );
165 SG_CHECK_EQUAL( ptrC.getNumRefs(), 1 );
166 SG_CHECK_EQUAL( ptrA, ptrB );
167 SG_CHECK_NE( ptrA, ptrC );
168
169 ptrA = std::move(ptrC);
170 SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 );
171 SG_CHECK_EQUAL( ptrB.getNumRefs(), 1 );
172 SG_CHECK_NE( ptrA, ptrB );
173 // Implementation detail that is *not* guaranteed by the C++ move
174 // semantics:
175 SG_CHECK_IS_NULL( ptrC.get() );
176 }
177 }
178
179 class Base1:
180 public virtual SGVirtualWeakReferenced
181 {};
182
183 class Base2:
184 public virtual SGVirtualWeakReferenced
185 {};
186
187 class VirtualDerived:
188 public Base1,
189 public Base2
190 {};
191
test_SGWeakPtr()192 void test_SGWeakPtr()
193 {
194 std::cout << "Testing SGWeakPtr and SGVirtualWeakReferenced" << std::endl;
195
196 SGSharedPtr<VirtualDerived> ptr( new VirtualDerived() );
197 SGWeakPtr<VirtualDerived> weak_ptr( ptr );
198 SG_CHECK_EQUAL( ptr.getNumRefs(), 1 );
199
200 SGSharedPtr<Base1> ptr1( weak_ptr.lock() );
201 SG_CHECK_EQUAL( ptr.getNumRefs(), 2 );
202
203 // converting constructor
204 SG_CHECK_EQUAL( SGSharedPtr<Base1>(weak_ptr), ptr1 );
205
206 SGSharedPtr<Base2> ptr2( weak_ptr.lock() );
207 SG_CHECK_EQUAL( ptr.getNumRefs(), 3 );
208
209 SG_CHECK_IS_NOT_NULL( ptr );
210 SG_CHECK_EQUAL( ptr.get(), ptr1.get() );
211 SG_CHECK_EQUAL( ptr.get(), ptr2.get() );
212
213 SGWeakPtr<Base1> weak_base1( ptr );
214 SGWeakPtr<Base2> weak_base2( ptr );
215 ptr1 = dynamic_cast<VirtualDerived*>(weak_base1.lock().get());
216 ptr2 = dynamic_cast<VirtualDerived*>(weak_base2.lock().get());
217
218 SG_CHECK_EQUAL( ptr.get(), ptr1.get() );
219 SG_CHECK_EQUAL( ptr.get(), ptr2.get() );
220 SG_CHECK_EQUAL( ptr.getNumRefs(), 3 );
221 }
222
main(int argc,char * argv[])223 int main(int argc, char* argv[])
224 {
225 test_SGSharedPtr();
226 test_SGWeakPtr();
227
228 return EXIT_SUCCESS;
229 }
230