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