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 39 # define POINTERHOLDER_TRANSITION 0
40 40 #endif // !defined(POINTERHOLDER_TRANSITION)
41 41  
  42 +#if POINTERHOLDER_TRANSITION < 4
  43 +
42 44 // *** WHAT IS HAPPENING ***
43 45  
44 46 // In qpdf 11, PointerHolder will be replaced with std::shared_ptr
... ... @@ -140,211 +142,94 @@
140 142 // written code. If you are relying on the incorrect behavior, use
141 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 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 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 181 [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]]
269   -#endif // POINTERHOLDER_TRANSITION >= 2
  182 +# endif // POINTERHOLDER_TRANSITION >= 2
270 183 T*
271 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 189 [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]]
277   -#endif // POINTERHOLDER_TRANSITION >= 2
  190 +# endif // POINTERHOLDER_TRANSITION >= 2
278 191 T const*
279 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 200 int
287 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 212 T const&
300 213 operator*() const
301 214 {
302   - return *this->data->pointer;
  215 + return *(this->get());
303 216 }
304 217 T&
305 218 operator*()
306 219 {
307   - return *this->data->pointer;
  220 + return *(this->get());
308 221 }
309 222  
310 223 T const*
311 224 operator->() const
312 225 {
313   - return this->data->pointer;
  226 + return this->get();
314 227 }
315 228 T*
316 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 235 template <typename T, typename... _Args>
... ... @@ -361,4 +246,5 @@ make_array_pointer_holder(size_t n)
361 246 return PointerHolder<T>(true, new T[n]);
362 247 }
363 248  
  249 +#endif // POINTERHOLDER_TRANSITION < 4
364 250 #endif // POINTERHOLDER_HH
... ...
libtests/pointer_holder.cc
... ... @@ -65,8 +65,8 @@ callHelloWithGet(ObjectHolder const&amp; oh)
65 65 (*oh).hello();
66 66 }
67 67  
68   -int
69   -main(int argc, char* argv[])
  68 +void
  69 +test_ph()
70 70 {
71 71 std::list<ObjectHolder> ol1;
72 72  
... ... @@ -111,5 +111,135 @@ main(int argc, char* argv[])
111 111 std::cout << "array" << std::endl;
112 112 PointerHolder<Object> o_arr1_ph(true, new Object[2]);
113 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 244 return 0;
115 245 }
... ...
libtests/qtest/ph/ph.out
... ... @@ -30,3 +30,51 @@ destroyed Object, id 6
30 30 destroyed Object, id 5
31 31 destroyed Object, id 3
32 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
... ...