Commit ae819b5318bf0a0a21b80d6269ef73ed8123d5d6

Authored by Jay Berkenbilt
1 parent ec219100

Rewrite PointerHolder as derived from std::shared_ptr

include/qpdf/PointerHolder.hh
@@ -39,6 +39,8 @@ @@ -39,6 +39,8 @@
39 # define POINTERHOLDER_TRANSITION 0 39 # define POINTERHOLDER_TRANSITION 0
40 #endif // !defined(POINTERHOLDER_TRANSITION) 40 #endif // !defined(POINTERHOLDER_TRANSITION)
41 41
  42 +#if POINTERHOLDER_TRANSITION < 4
  43 +
42 // *** WHAT IS HAPPENING *** 44 // *** WHAT IS HAPPENING ***
43 45
44 // In qpdf 11, PointerHolder will be replaced with std::shared_ptr 46 // In qpdf 11, PointerHolder will be replaced with std::shared_ptr
@@ -140,211 +142,94 @@ @@ -140,211 +142,94 @@
140 // written code. If you are relying on the incorrect behavior, use 142 // written code. If you are relying on the incorrect behavior, use
141 // PointerHolder<T const> instead. 143 // PointerHolder<T const> instead.
142 144
143 -// OLD DOCUMENTATION  
144 -  
145 -// This class is basically std::shared_ptr but predates that by  
146 -// several years.  
147 -  
148 -// This class expects to be initialized with a dynamically allocated  
149 -// object pointer. It keeps a reference count and deletes this once  
150 -// the reference count goes to zero. PointerHolder objects are  
151 -// explicitly safe for use in STL containers.  
152 -  
153 -// It is very important that a client who pulls the pointer out of  
154 -// this holder does not let the holder go out of scope until it is  
155 -// finished with the pointer. It is also important that exactly one  
156 -// instance of this object ever gets initialized with a given pointer.  
157 -// Otherwise, the pointer will be deleted twice, and before that, some  
158 -// objects will be left with a pointer to a deleted object. In other  
159 -// words, the only legitimate way for two PointerHolder objects to  
160 -// contain the same pointer is for one to be a copy of the other.  
161 -// Copy and assignment semantics are well-defined and essentially  
162 -// allow you to use PointerHolder as a means to get pass-by-reference  
163 -// semantics in a pass-by-value environment without having to worry  
164 -// about memory management details.  
165 -  
166 -// Comparison (== and <) are defined and operate on the internally  
167 -// stored pointers, not on the data. This makes it possible to store  
168 -// PointerHolder objects in sorted lists or to find them in STL  
169 -// containers just as one would be able to store pointers. Comparing  
170 -// the underlying pointers provides a well-defined, if not  
171 -// particularly meaningful, ordering.  
172 -  
173 -#include <cstddef> 145 +# include <cstddef>
  146 +# include <memory>
174 147
175 template <class T> 148 template <class T>
176 -class PointerHolder 149 +class PointerHolder: public std::shared_ptr<T>
177 { 150 {
178 - private:  
179 - class Data  
180 - {  
181 - public:  
182 - Data(T* pointer, bool array) :  
183 - pointer(pointer),  
184 - array(array),  
185 - refcount(0)  
186 - {  
187 - }  
188 - ~Data()  
189 - {  
190 - if (array) {  
191 - delete[] this->pointer;  
192 - } else {  
193 - delete this->pointer;  
194 - }  
195 - }  
196 - T* pointer;  
197 - bool array;  
198 - int refcount;  
199 -  
200 - private:  
201 - Data(Data const&) = delete;  
202 - Data& operator=(Data const&) = delete;  
203 - };  
204 -  
205 public: 151 public:
206 -#if POINTERHOLDER_TRANSITION >= 1  
207 - explicit  
208 -#endif // POINTERHOLDER_TRANSITION >= 1  
209 - PointerHolder(T* pointer = 0)  
210 - {  
211 - this->init(new Data(pointer, false));  
212 - }  
213 - // Special constructor indicating to free memory with delete []  
214 - // instead of delete  
215 - PointerHolder(bool, T* pointer)  
216 - {  
217 - this->init(new Data(pointer, true));  
218 - }  
219 - PointerHolder(PointerHolder const& rhs)  
220 - {  
221 - this->copy(rhs);  
222 - }  
223 - PointerHolder&  
224 - operator=(PointerHolder const& rhs)  
225 - {  
226 - if (this != &rhs) {  
227 - this->destroy();  
228 - this->copy(rhs);  
229 - }  
230 - return *this;  
231 - }  
232 - PointerHolder&  
233 - operator=(decltype(nullptr)) 152 +# if POINTERHOLDER_TRANSITION >= 3
  153 + [[deprecated("use std::shared_ptr<T> instead")]]
  154 +# endif // POINTERHOLDER_TRANSITION >= 3
  155 + PointerHolder(std::shared_ptr<T> other) :
  156 + std::shared_ptr<T>(other)
234 { 157 {
235 - this->operator=(PointerHolder<T>());  
236 - return *this;  
237 } 158 }
238 - ~PointerHolder()  
239 - {  
240 - this->destroy();  
241 - }  
242 - bool  
243 - operator==(PointerHolder const& rhs) const  
244 - {  
245 - return this->data->pointer == rhs.data->pointer;  
246 - }  
247 - bool  
248 - operator==(decltype(nullptr)) const 159 +# if POINTERHOLDER_TRANSITION >= 3
  160 + [[deprecated("use std::shared_ptr<T> instead")]]
  161 +# if POINTERHOLDER_TRANSITION >= 1
  162 + explicit
  163 +# endif // POINTERHOLDER_TRANSITION >= 1
  164 +# endif // POINTERHOLDER_TRANSITION >= 3
  165 + PointerHolder(T* pointer = 0) :
  166 + std::shared_ptr<T>(pointer)
249 { 167 {
250 - return this->data->pointer == nullptr;  
251 } 168 }
252 - bool  
253 - operator<(PointerHolder const& rhs) const 169 + // Create a shared pointer to an array
  170 +# if POINTERHOLDER_TRANSITION >= 3
  171 + [[deprecated("use std::shared_ptr<T> instead")]]
  172 +# endif // POINTERHOLDER_TRANSITION >= 3
  173 + PointerHolder(bool, T* pointer) :
  174 + std::shared_ptr<T>(pointer, std::default_delete<T[]>())
254 { 175 {
255 - return this->data->pointer < rhs.data->pointer;  
256 } 176 }
257 177
258 - // get() is for interface compatibility with std::shared_ptr  
259 - T*  
260 - get() const  
261 - {  
262 - return this->data->pointer;  
263 - } 178 + virtual ~PointerHolder() = default;
264 179
265 - // NOTE: The pointer returned by getPointer turns into a pumpkin  
266 - // when the last PointerHolder that contains it disappears.  
267 -#if POINTERHOLDER_TRANSITION >= 2 180 +# if POINTERHOLDER_TRANSITION >= 2
268 [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]] 181 [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]]
269 -#endif // POINTERHOLDER_TRANSITION >= 2 182 +# endif // POINTERHOLDER_TRANSITION >= 2
270 T* 183 T*
271 getPointer() 184 getPointer()
272 { 185 {
273 - return this->data->pointer; 186 + return this->get();
274 } 187 }
275 -#if POINTERHOLDER_TRANSITION >= 2 188 +# if POINTERHOLDER_TRANSITION >= 2
276 [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]] 189 [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]]
277 -#endif // POINTERHOLDER_TRANSITION >= 2 190 +# endif // POINTERHOLDER_TRANSITION >= 2
278 T const* 191 T const*
279 getPointer() const 192 getPointer() const
280 { 193 {
281 - return this->data->pointer; 194 + return this->get();
282 } 195 }
283 -#if POINTERHOLDER_TRANSITION >= 2  
284 - [[deprecated("use use_count() instead of getRefcount()")]]  
285 -#endif // POINTERHOLDER_TRANSITION >= 2 196 +
  197 +# if POINTERHOLDER_TRANSITION >= 2
  198 + [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]]
  199 +# endif // POINTERHOLDER_TRANSITION >= 2
286 int 200 int
287 getRefcount() const 201 getRefcount() const
288 { 202 {
289 - return this->data->refcount; 203 + return static_cast<int>(this->use_count());
290 } 204 }
291 205
292 - // use_count() is for compatibility with std::shared_ptr  
293 - long  
294 - use_count() 206 + PointerHolder&
  207 + operator=(decltype(nullptr))
295 { 208 {
296 - return static_cast<long>(this->data->refcount); 209 + std::shared_ptr<T>::operator=(nullptr);
  210 + return *this;
297 } 211 }
298 -  
299 T const& 212 T const&
300 operator*() const 213 operator*() const
301 { 214 {
302 - return *this->data->pointer; 215 + return *(this->get());
303 } 216 }
304 T& 217 T&
305 operator*() 218 operator*()
306 { 219 {
307 - return *this->data->pointer; 220 + return *(this->get());
308 } 221 }
309 222
310 T const* 223 T const*
311 operator->() const 224 operator->() const
312 { 225 {
313 - return this->data->pointer; 226 + return this->get();
314 } 227 }
315 T* 228 T*
316 operator->() 229 operator->()
317 { 230 {
318 - return this->data->pointer; 231 + return this->get();
319 } 232 }
320 -  
321 - private:  
322 - void  
323 - init(Data* data)  
324 - {  
325 - this->data = data;  
326 - ++this->data->refcount;  
327 - }  
328 - void  
329 - copy(PointerHolder const& rhs)  
330 - {  
331 - this->init(rhs.data);  
332 - }  
333 - void  
334 - destroy()  
335 - {  
336 - bool gone = false;  
337 - {  
338 - if (--this->data->refcount == 0) {  
339 - gone = true;  
340 - }  
341 - }  
342 - if (gone) {  
343 - delete this->data;  
344 - }  
345 - }  
346 -  
347 - Data* data;  
348 }; 233 };
349 234
350 template <typename T, typename... _Args> 235 template <typename T, typename... _Args>
@@ -361,4 +246,5 @@ make_array_pointer_holder(size_t n) @@ -361,4 +246,5 @@ make_array_pointer_holder(size_t n)
361 return PointerHolder<T>(true, new T[n]); 246 return PointerHolder<T>(true, new T[n]);
362 } 247 }
363 248
  249 +#endif // POINTERHOLDER_TRANSITION < 4
364 #endif // POINTERHOLDER_HH 250 #endif // POINTERHOLDER_HH
libtests/pointer_holder.cc
@@ -65,8 +65,8 @@ callHelloWithGet(ObjectHolder const&amp; oh) @@ -65,8 +65,8 @@ callHelloWithGet(ObjectHolder const&amp; oh)
65 (*oh).hello(); 65 (*oh).hello();
66 } 66 }
67 67
68 -int  
69 -main(int argc, char* argv[]) 68 +void
  69 +test_ph()
70 { 70 {
71 std::list<ObjectHolder> ol1; 71 std::list<ObjectHolder> ol1;
72 72
@@ -111,5 +111,135 @@ main(int argc, char* argv[]) @@ -111,5 +111,135 @@ main(int argc, char* argv[])
111 std::cout << "array" << std::endl; 111 std::cout << "array" << std::endl;
112 PointerHolder<Object> o_arr1_ph(true, new Object[2]); 112 PointerHolder<Object> o_arr1_ph(true, new Object[2]);
113 std::cout << "goodbye" << std::endl; 113 std::cout << "goodbye" << std::endl;
  114 +}
  115 +
  116 +PointerHolder<Object>
  117 +make_object_ph()
  118 +{
  119 + return new Object;
  120 +}
  121 +
  122 +std::shared_ptr<Object>
  123 +make_object_sp()
  124 +{
  125 + return std::make_shared<Object>();
  126 +}
  127 +
  128 +PointerHolder<Object const>
  129 +make_object_const_ph()
  130 +{
  131 + return new Object;
  132 +}
  133 +
  134 +std::shared_ptr<Object const>
  135 +make_object_const_sp()
  136 +{
  137 + return std::make_shared<Object const>();
  138 +}
  139 +
  140 +void
  141 +hello_ph(PointerHolder<Object> o)
  142 +{
  143 + o->hello();
  144 +}
  145 +
  146 +void
  147 +hello_sp(std::shared_ptr<Object> o)
  148 +{
  149 + o->hello();
  150 +}
  151 +
  152 +void
  153 +hello_ph_const(PointerHolder<Object const> o)
  154 +{
  155 + o->hello();
  156 +}
  157 +
  158 +void
  159 +hello_sp_const(std::shared_ptr<Object const> o)
  160 +{
  161 + o->hello();
  162 +}
  163 +
  164 +void
  165 +ph_sp_compat()
  166 +{
  167 + // Ensure bidirectional compatibility between PointerHolder and
  168 + // shared_ptr.
  169 + std::cout << "compat" << std::endl;
  170 + PointerHolder<Object> ph_from_ph = make_object_ph();
  171 + std::shared_ptr<Object> sp_from_ph = make_object_ph();
  172 + PointerHolder<Object> ph_from_sp = make_object_sp();
  173 + std::shared_ptr<Object> sp_from_sp = make_object_sp();
  174 + hello_sp(ph_from_ph);
  175 + hello_ph(sp_from_ph);
  176 + hello_sp(ph_from_sp);
  177 + hello_ph(sp_from_sp);
  178 + PointerHolder<Object const> ph_const_from_ph = make_object_const_ph();
  179 + std::shared_ptr<Object const> sp_const_from_ph = make_object_const_ph();
  180 + PointerHolder<Object const> ph_const_from_sp = make_object_const_sp();
  181 + std::shared_ptr<Object const> sp_const_from_sp = make_object_const_sp();
  182 + hello_sp_const(ph_const_from_ph);
  183 + hello_ph_const(sp_const_from_ph);
  184 + hello_sp_const(ph_const_from_sp);
  185 + hello_ph_const(sp_const_from_sp);
  186 + PointerHolder<Object> arr1_ph;
  187 + {
  188 + std::cout << "initialize ph array from shared_ptr" << std::endl;
  189 + std::shared_ptr<Object> arr1(
  190 + new Object[2], std::default_delete<Object[]>());
  191 + arr1_ph = arr1;
  192 + }
  193 + std::cout << "delete ph array" << std::endl;
  194 + arr1_ph = nullptr;
  195 + std::shared_ptr<Object> arr2_sp;
  196 + {
  197 + std::cout << "initialize sp array from PointerHolder" << std::endl;
  198 + PointerHolder<Object> arr2(true, new Object[2]);
  199 + arr2_sp = arr2;
  200 + }
  201 + std::cout << "delete sp array" << std::endl;
  202 + arr2_sp = nullptr;
  203 + std::cout << "end compat" << std::endl;
  204 +}
  205 +
  206 +std::list<PointerHolder<Object>>
  207 +get_ph_list()
  208 +{
  209 + std::list<PointerHolder<Object>> l = {
  210 + make_object_sp(),
  211 + make_object_ph(),
  212 + };
  213 + return l;
  214 +}
  215 +
  216 +std::list<std::shared_ptr<Object>>
  217 +get_sp_list()
  218 +{
  219 + std::list<std::shared_ptr<Object>> l = {
  220 + make_object_sp(),
  221 + make_object_ph(),
  222 + };
  223 + return l;
  224 +}
  225 +
  226 +void
  227 +ph_sp_containers()
  228 +{
  229 + std::cout << "containers" << std::endl;
  230 + // Demonstrate that using auto makes it easy to switch interfaces
  231 + // from using a container of one shared pointer type to a
  232 + // container of the other.
  233 + auto phl1 = get_ph_list();
  234 + auto phl2 = get_sp_list();
  235 + std::cout << "end containers" << std::endl;
  236 +}
  237 +
  238 +int
  239 +main(int argc, char* argv[])
  240 +{
  241 + test_ph();
  242 + ph_sp_compat();
  243 + ph_sp_containers();
114 return 0; 244 return 0;
115 } 245 }
libtests/qtest/ph/ph.out
@@ -30,3 +30,51 @@ destroyed Object, id 6 @@ -30,3 +30,51 @@ destroyed Object, id 6
30 destroyed Object, id 5 30 destroyed Object, id 5
31 destroyed Object, id 3 31 destroyed Object, id 3
32 destroyed Object, id 1 32 destroyed Object, id 1
  33 +compat
  34 +created Object, id 7
  35 +created Object, id 8
  36 +created Object, id 9
  37 +created Object, id 10
  38 +calling Object::hello for 7
  39 +calling Object::hello for 8
  40 +calling Object::hello for 9
  41 +calling Object::hello for 10
  42 +created Object, id 11
  43 +created Object, id 12
  44 +created Object, id 13
  45 +created Object, id 14
  46 +calling Object::hello const for 11
  47 +calling Object::hello const for 12
  48 +calling Object::hello const for 13
  49 +calling Object::hello const for 14
  50 +initialize ph array from shared_ptr
  51 +created Object, id 15
  52 +created Object, id 16
  53 +delete ph array
  54 +destroyed Object, id 16
  55 +destroyed Object, id 15
  56 +initialize sp array from PointerHolder
  57 +created Object, id 17
  58 +created Object, id 18
  59 +delete sp array
  60 +destroyed Object, id 18
  61 +destroyed Object, id 17
  62 +end compat
  63 +destroyed Object, id 14
  64 +destroyed Object, id 13
  65 +destroyed Object, id 12
  66 +destroyed Object, id 11
  67 +destroyed Object, id 10
  68 +destroyed Object, id 9
  69 +destroyed Object, id 8
  70 +destroyed Object, id 7
  71 +containers
  72 +created Object, id 19
  73 +created Object, id 20
  74 +created Object, id 21
  75 +created Object, id 22
  76 +end containers
  77 +destroyed Object, id 21
  78 +destroyed Object, id 22
  79 +destroyed Object, id 19
  80 +destroyed Object, id 20