Commit dd4f30226f3d4738e198dfcb944ce4cdc7e50a86
1 parent
df2f5c6a
Rework PointerHolder transition to make it smoother
* Don't surprise people with deprecation warnings * Provide detailed instructions and support for the transition
Showing
6 changed files
with
575 additions
and
291 deletions
ChangeLog
| @@ -47,11 +47,6 @@ | @@ -47,11 +47,6 @@ | ||
| 47 | includes so you don't have to try to include a header that won't | 47 | includes so you don't have to try to include a header that won't |
| 48 | be there. | 48 | be there. |
| 49 | 49 | ||
| 50 | - * PointerHolder: deprecate getPointer() and getRefcount(). If you | ||
| 51 | - don't want to disable deprecation warnings in general but are not | ||
| 52 | - ready to tackle this change yet, you can define | ||
| 53 | - NO_POINTERHOLDER_DEPRECATION to suppress those. | ||
| 54 | - | ||
| 55 | * PointerHolder: add a get() method and a use_count() method for | 50 | * PointerHolder: add a get() method and a use_count() method for |
| 56 | compatibility with std::shared_ptr. In qpdf 11, qpdf's APIs will | 51 | compatibility with std::shared_ptr. In qpdf 11, qpdf's APIs will |
| 57 | switch to using std::shared_ptr instead of PointerHolder, though | 52 | switch to using std::shared_ptr instead of PointerHolder, though |
TODO
| @@ -388,35 +388,65 @@ Other notes: | @@ -388,35 +388,65 @@ Other notes: | ||
| 388 | PointerHolder to std::shared_ptr | 388 | PointerHolder to std::shared_ptr |
| 389 | ================================ | 389 | ================================ |
| 390 | 390 | ||
| 391 | -Remember to update the smart-pointers section of the manual in | ||
| 392 | -design.rst. | 391 | +To perform update: |
| 393 | 392 | ||
| 394 | -Once all deprecation warnings are cleared up (changing getPointer() to | ||
| 395 | -get() and getRefcount() to use_count()), the only real issues are that | ||
| 396 | -implicit assignment of a pointer to a shared_ptr doesn't work while it | ||
| 397 | -does for PointerHolder and containers are different. Using auto takes | ||
| 398 | -care of containers. | 393 | +Cherry-pick pointerholder branch commit |
| 399 | 394 | ||
| 400 | -PointerHolder<X> x = new X() --> | ||
| 401 | -auto x = std::make_shared<X>() | 395 | +Upgrade just the library. This is not necessary, but it's an added |
| 396 | +check that the compatibility code works since it will show that tests, | ||
| 397 | +examples, and CLI will work properly with the upgraded APIs, which | ||
| 398 | +provides some assurance that other people will have a smooth time with | ||
| 399 | +their code. | ||
| 402 | 400 | ||
| 403 | -PointerHolder<Base> x = new Derived() --> | ||
| 404 | -auto x = std::shared_ptr<Base>(new Derived()) | 401 | +patrepl s/PointerHolder/std::shared_ptr/g {include,libqpdf}/qpdf/*.hh |
| 402 | +patrepl s/PointerHolder/std::shared_ptr/g libqpdf/*.cc | ||
| 403 | +patrepl s/make_pointer_holder/std::make_shared/g libqpdf/*.cc | ||
| 404 | +patrepl s/make_array_pointer_holder/QUtil::make_shared_array/g libqpdf/*.cc | ||
| 405 | +patrepl s,qpdf/std::shared_ptr,qpdf/PointerHolder, **/*.cc **/*.hh | ||
| 406 | +git restore include/qpdf/PointerHolder.hh | ||
| 407 | +cleanpatch | ||
| 405 | 408 | ||
| 406 | -PointerHolder x(true, new T[5]) --> | ||
| 407 | -auto x = std::shared_ptr(new T[5], std::default_delete<T[]>()) | 409 | +Increase to POINTERHOLDER_TRANSITION=3 |
| 408 | 410 | ||
| 409 | -X* x = new X(); PointerHolder<X> x_ph(x) --> | ||
| 410 | -auto x_ph = std::make_shared<X>(); X* x = x_ph.get(); | 411 | +make build_libqpdf -- no errors |
| 411 | 412 | ||
| 412 | -Derived* x = new Derived(); PointerHolder<Base> x_ph(x) --> | ||
| 413 | -Derived* x = new Derived(); auto x_ph = std::shared_pointer<Base>(x); | 413 | +Drop back to POINTERHOLDER_TRANSITION=2 |
| 414 | 414 | ||
| 415 | -Also remember | 415 | +make check -- everything passes |
| 416 | 416 | ||
| 417 | -auto x = std::shared_ptr(new T[5], std::default_delete<T[]>()) | ||
| 418 | -vs. | ||
| 419 | -auto x = std::make_unique<T[]>(5) | 417 | +Then upgrade everything else. It would work to just start here. |
| 418 | + | ||
| 419 | +Increase to POINTERHOLDER_TRANSITION=3 | ||
| 420 | + | ||
| 421 | +patrepl s/PointerHolder/std::shared_ptr/g **/*.cc **/*.hh | ||
| 422 | +patrepl s/make_pointer_holder/std::make_shared/g **/*.cc | ||
| 423 | +patrepl s/make_array_pointer_holder/QUtil::make_shared_array/g **/*.cc | ||
| 424 | +patrepl s,qpdf/std::shared_ptr,qpdf/PointerHolder, **/*.cc **/*.hh | ||
| 425 | +git restore include/qpdf/PointerHolder.hh | ||
| 426 | +git restore libtests/pointer_holder.cc | ||
| 427 | +cleanpatch | ||
| 428 | + | ||
| 429 | +Remove all references to PointerHolder.hh from everything except | ||
| 430 | +public headers and pointer_holder.cc. | ||
| 431 | + | ||
| 432 | +make check -- everything passes | ||
| 433 | + | ||
| 434 | +Increase to POINTERHOLDER_TRANSITION=4 | ||
| 435 | + | ||
| 436 | +Do a clean build and make check -- everything passes | ||
| 437 | + | ||
| 438 | +Final steps: | ||
| 439 | + | ||
| 440 | +* Change to POINTERHOLDER_TRANSITION=4 in autoconf.mk.in. | ||
| 441 | +* Check code formatting | ||
| 442 | +* std::shared_ptr<Members> m can be replaced with | ||
| 443 | + std::shared_ptr<Members> m_ph and Members* m if performance is critical | ||
| 444 | +* Could try Members indirection with Members* for QPDFObjectHandle | ||
| 445 | + | ||
| 446 | +When done: | ||
| 447 | + | ||
| 448 | +* Update the smart-pointers section of the manual in design.rst | ||
| 449 | +* Update comments in PointerHolder.hh | ||
| 420 | 450 | ||
| 421 | PointerHolder in public API: | 451 | PointerHolder in public API: |
| 422 | 452 | ||
| @@ -453,20 +483,6 @@ PointerHolder in public API: | @@ -453,20 +483,6 @@ PointerHolder in public API: | ||
| 453 | QPDFPageObjectHelper::addContentTokenFilter( | 483 | QPDFPageObjectHelper::addContentTokenFilter( |
| 454 | PointerHolder<QPDFObjectHandle::TokenFilter>) | 484 | PointerHolder<QPDFObjectHandle::TokenFilter>) |
| 455 | 485 | ||
| 456 | -Strategy: | ||
| 457 | -* Start with pointerholder branch | ||
| 458 | -* Replace each public API one at a time | ||
| 459 | -* Replace remaining internal uses; sometimes unique_ptr may be good, | ||
| 460 | - particularly for temporary strings that are deleted in the same | ||
| 461 | - scope and Members for non-copyable classes | ||
| 462 | -* std::shared_ptr<Members> m can be replaced with | ||
| 463 | - std::shared_ptr<Members> m_ph and Members* m if performance is critical | ||
| 464 | -* Remove #include <PointerHolder.hh> from all headers | ||
| 465 | - | ||
| 466 | -At that point, we're in a good state to make that compatibility | ||
| 467 | -basically works. Then we can proceed to remove PointerHolder from | ||
| 468 | -everything else. | ||
| 469 | - | ||
| 470 | ABI Changes | 486 | ABI Changes |
| 471 | =========== | 487 | =========== |
| 472 | 488 |
include/qpdf/PointerHolder.hh
| @@ -22,13 +22,125 @@ | @@ -22,13 +22,125 @@ | ||
| 22 | #ifndef POINTERHOLDER_HH | 22 | #ifndef POINTERHOLDER_HH |
| 23 | #define POINTERHOLDER_HH | 23 | #define POINTERHOLDER_HH |
| 24 | 24 | ||
| 25 | -// In qpdf 11, PointerHolder will be derived from std::shared_ptr and | ||
| 26 | -// will also include a fix to incorrect semantics of const | ||
| 27 | -// PointerHolder objects. PointerHolder only allows a const | ||
| 28 | -// PointerHolder to return a const pointer. This is wrong. Use a | ||
| 29 | -// PointerHolder<const T> for that. A const PointerHolder should just | ||
| 30 | -// not allow you to change what it points to. This is consistent with | ||
| 31 | -// how regular pointers and standard library shared pointers work. | 25 | +#ifndef POINTERHOLDER_TRANSITION |
| 26 | + | ||
| 27 | +// In qpdf 11, #define POINTERHOLDER_IS_SHARED_POINTER | ||
| 28 | + | ||
| 29 | +// In qpdf 11, issue a warning: | ||
| 30 | +// #define POINTERHOLDER_TRANSITION 0 to suppress this warning, and see below. | ||
| 31 | +// # warn "POINTERHOLDER_TRANSITION is not defined -- see qpdf/PointerHolder.hh" | ||
| 32 | + | ||
| 33 | +// undefined = define as 0; will also issue a warning in qpdf 11 | ||
| 34 | +// 0 = no deprecation warnings | ||
| 35 | +// 1 = make PointerHolder<T>(T*) explicit | ||
| 36 | +// 2 = warn for use of getPointer() and getRefcount() | ||
| 37 | +// 3 = warn for all use of PointerHolder | ||
| 38 | +// 4 = don't define PointerHolder at all | ||
| 39 | +# define POINTERHOLDER_TRANSITION 0 | ||
| 40 | +#endif // !defined(POINTERHOLDER_TRANSITION) | ||
| 41 | + | ||
| 42 | +// *** WHAT IS HAPPENING *** | ||
| 43 | + | ||
| 44 | +// In qpdf 11, PointerHolder will be replaced with std::shared_ptr | ||
| 45 | +// wherever it appears in the qpdf API. The PointerHolder object will | ||
| 46 | +// be derived from std::shared_ptr to provide a backward-compatible | ||
| 47 | +// interface and will be mutually assignable with std::shared_ptr. | ||
| 48 | +// Code that uses containers of PointerHolder will require adjustment. | ||
| 49 | + | ||
| 50 | +// *** HOW TO TRANSITION *** | ||
| 51 | + | ||
| 52 | +// The symbol POINTERHOLDER_TRANSITION can be defined to help you | ||
| 53 | +// transition your code away from PointerHolder. You can define it | ||
| 54 | +// before including any qpdf header files or including its definition | ||
| 55 | +// in your build configuration. If not defined, it automatically gets | ||
| 56 | +// defined to 0, which enables full backward compatibility. That way, | ||
| 57 | +// you don't have to take action for your code to continue to work. | ||
| 58 | + | ||
| 59 | +// If you want to work gradually to transition your code away from | ||
| 60 | +// PointerHolder, you can define POINTERHOLDER_TRANSITION and fix the | ||
| 61 | +// code so it compiles without warnings and works correctly. If you | ||
| 62 | +// want to be able to continue to support old qpdf versions at the | ||
| 63 | +// same time, you can write code like this: | ||
| 64 | + | ||
| 65 | +// #ifndef POINTERHOLDER_TRANSITION | ||
| 66 | +// ... use PointerHolder as before 10.6 | ||
| 67 | +// #else | ||
| 68 | +// ... use PointerHolder or shared_ptr as needed | ||
| 69 | +// #endif | ||
| 70 | + | ||
| 71 | +// Each level of POINTERHOLDER_TRANSITION exposes differences between | ||
| 72 | +// PointerHolder and std::shared_ptr. The easiest way to transition is | ||
| 73 | +// to increase POINTERHOLDER_TRANSITION in steps of 1 so that you can | ||
| 74 | +// test and handle changes incrementally. | ||
| 75 | + | ||
| 76 | +// *** Transitions available starting at qpdf 10.6.0 *** | ||
| 77 | + | ||
| 78 | +// POINTERHOLDER_TRANSITION = 1 | ||
| 79 | +// | ||
| 80 | +// PointerHolder<T> has an implicit constructor that takes a T*, so | ||
| 81 | +// you can replace a PointerHolder<T>'s pointer by directly assigning | ||
| 82 | +// a T* to it or pass a T* to a function that expects a | ||
| 83 | +// PointerHolder<T>. std::shared_ptr does not have this (risky) | ||
| 84 | +// behavior. When POINTERHOLDER_TRANSITION = 1, PointerHolder<T>'s T* | ||
| 85 | +// constructor is declared explicit. For compatibility with | ||
| 86 | +// std::shared_ptr, you can still assign nullptr to a PointerHolder. | ||
| 87 | +// Constructing all your PointerHolder<T> instances explicitly is | ||
| 88 | +// backward compatible, so you can make this change without | ||
| 89 | +// conditional compilation and still use the changes with older qpdf | ||
| 90 | +// versions. | ||
| 91 | +// | ||
| 92 | +// Also defined is a make_pointer_holder method that acts like | ||
| 93 | +// std::make_shared. You can use this as well, but it is not | ||
| 94 | +// compatible with qpdf prior to 10.6. Like std::make_shared<T>, | ||
| 95 | +// make_pointer_holder<T> can only be used when the constructor | ||
| 96 | +// implied by its arguments is public. If you use this, you should be | ||
| 97 | +// able to just replace it with std::make_shared when qpdf 11 is out. | ||
| 98 | + | ||
| 99 | +// POINTERHOLDER_TRANSITION = 2 | ||
| 100 | +// | ||
| 101 | +// std::shared_ptr as get() and use_count(). PointerHolder has | ||
| 102 | +// getPointer() and getRefcount(). In 10.6.0, get() and use_count() | ||
| 103 | +// were added as well. When POINTERHOLDER_TRANSITION = 2, getPointer() | ||
| 104 | +// and getRefcount() are deprecated. Fix deprecation warnings by | ||
| 105 | +// replacing with get() and use_count(). This breaks compatibility | ||
| 106 | +// with qpdf older than 10.6. Search for CONST BEHAVIOR for an | ||
| 107 | +// additional note. | ||
| 108 | +// | ||
| 109 | +// Once your code is clean at POINTERHOLDER_TRANSITION = 2, the only | ||
| 110 | +// remaining issues that prevent simple replacement of PointerHolder | ||
| 111 | +// with std::shared_ptr are shared arrays and containers, and neither | ||
| 112 | +// of these are used in the qpdf API. | ||
| 113 | + | ||
| 114 | +// *** Transitions available starting at qpdf 11.0.0 ** | ||
| 115 | + | ||
| 116 | +// NOTE: Until qpdf 11 is released, this is a plan and is subject to | ||
| 117 | +// change. Be sure to check again after qpdf 11 is released. | ||
| 118 | + | ||
| 119 | +// POINTERHOLDER_TRANSITION = 3 | ||
| 120 | +// | ||
| 121 | +// Warn for all use of PointerHolder<T>. This help you remove all use | ||
| 122 | +// of PointerHolder from your code and use std::shared_ptr instead. | ||
| 123 | +// You will also have to transition any containers of PointerHolder in | ||
| 124 | +// your code. | ||
| 125 | + | ||
| 126 | +// POINTERHOLDER_TRANSITION = 4 | ||
| 127 | +// | ||
| 128 | +// Suppress definition of the PointerHolder<T> type entirely. | ||
| 129 | + | ||
| 130 | +// CONST BEHAVIOR | ||
| 131 | + | ||
| 132 | +// PointerHolder<T> has had a long-standing bug in its const behavior. | ||
| 133 | +// const PointerHolder<T>'s getPointer() method returns a T const*. | ||
| 134 | +// This is incorrect and is not how regular pointers or standard | ||
| 135 | +// library smart pointers behave. Making a PointerHolder<T> const | ||
| 136 | +// should prevent reassignment of its pointer but not affect the thing | ||
| 137 | +// it points to. For that, use PointerHolder<T const>. The new get() | ||
| 138 | +// method behaves correctly in this respect and is therefore slightly | ||
| 139 | +// different from getPointer(). This shouldn't break any correctly | ||
| 140 | +// written code. If you are relying on the incorrect behavior, use | ||
| 141 | +// PointerHolder<T const> instead. | ||
| 142 | + | ||
| 143 | +// OLD DOCUMENTATION | ||
| 32 | 144 | ||
| 33 | // This class is basically std::shared_ptr but predates that by | 145 | // This class is basically std::shared_ptr but predates that by |
| 34 | // several years. | 146 | // several years. |
| @@ -58,6 +170,8 @@ | @@ -58,6 +170,8 @@ | ||
| 58 | // the underlying pointers provides a well-defined, if not | 170 | // the underlying pointers provides a well-defined, if not |
| 59 | // particularly meaningful, ordering. | 171 | // particularly meaningful, ordering. |
| 60 | 172 | ||
| 173 | +#include <cstddef> | ||
| 174 | + | ||
| 61 | template <class T> | 175 | template <class T> |
| 62 | class PointerHolder | 176 | class PointerHolder |
| 63 | { | 177 | { |
| @@ -65,66 +179,78 @@ class PointerHolder | @@ -65,66 +179,78 @@ class PointerHolder | ||
| 65 | class Data | 179 | class Data |
| 66 | { | 180 | { |
| 67 | public: | 181 | public: |
| 68 | - Data(T* pointer, bool array) : | ||
| 69 | - pointer(pointer), | ||
| 70 | - array(array), | ||
| 71 | - refcount(0) | ||
| 72 | - { | ||
| 73 | - } | ||
| 74 | - ~Data() | ||
| 75 | - { | ||
| 76 | - if (array) | ||
| 77 | - { | ||
| 78 | - delete [] this->pointer; | ||
| 79 | - } | ||
| 80 | - else | ||
| 81 | - { | ||
| 82 | - delete this->pointer; | ||
| 83 | - } | ||
| 84 | - } | ||
| 85 | - T* pointer; | ||
| 86 | - bool array; | ||
| 87 | - int refcount; | 182 | + Data(T* pointer, bool array) : |
| 183 | + pointer(pointer), | ||
| 184 | + array(array), | ||
| 185 | + refcount(0) | ||
| 186 | + { | ||
| 187 | + } | ||
| 188 | + ~Data() | ||
| 189 | + { | ||
| 190 | + if (array) | ||
| 191 | + { | ||
| 192 | + delete [] this->pointer; | ||
| 193 | + } | ||
| 194 | + else | ||
| 195 | + { | ||
| 196 | + delete this->pointer; | ||
| 197 | + } | ||
| 198 | + } | ||
| 199 | + T* pointer; | ||
| 200 | + bool array; | ||
| 201 | + int refcount; | ||
| 88 | private: | 202 | private: |
| 89 | - Data(Data const&) = delete; | ||
| 90 | - Data& operator=(Data const&) = delete; | 203 | + Data(Data const&) = delete; |
| 204 | + Data& operator=(Data const&) = delete; | ||
| 91 | }; | 205 | }; |
| 92 | 206 | ||
| 93 | public: | 207 | public: |
| 208 | +#if POINTERHOLDER_TRANSITION >= 1 | ||
| 209 | + explicit | ||
| 210 | +#endif // POINTERHOLDER_TRANSITION >= 1 | ||
| 94 | PointerHolder(T* pointer = 0) | 211 | PointerHolder(T* pointer = 0) |
| 95 | - { | ||
| 96 | - this->init(new Data(pointer, false)); | ||
| 97 | - } | 212 | + { |
| 213 | + this->init(new Data(pointer, false)); | ||
| 214 | + } | ||
| 98 | // Special constructor indicating to free memory with delete [] | 215 | // Special constructor indicating to free memory with delete [] |
| 99 | // instead of delete | 216 | // instead of delete |
| 100 | PointerHolder(bool, T* pointer) | 217 | PointerHolder(bool, T* pointer) |
| 101 | - { | ||
| 102 | - this->init(new Data(pointer, true)); | ||
| 103 | - } | 218 | + { |
| 219 | + this->init(new Data(pointer, true)); | ||
| 220 | + } | ||
| 104 | PointerHolder(PointerHolder const& rhs) | 221 | PointerHolder(PointerHolder const& rhs) |
| 105 | - { | ||
| 106 | - this->copy(rhs); | ||
| 107 | - } | 222 | + { |
| 223 | + this->copy(rhs); | ||
| 224 | + } | ||
| 108 | PointerHolder& operator=(PointerHolder const& rhs) | 225 | PointerHolder& operator=(PointerHolder const& rhs) |
| 109 | - { | ||
| 110 | - if (this != &rhs) | ||
| 111 | - { | ||
| 112 | - this->destroy(); | ||
| 113 | - this->copy(rhs); | ||
| 114 | - } | ||
| 115 | - return *this; | ||
| 116 | - } | 226 | + { |
| 227 | + if (this != &rhs) | ||
| 228 | + { | ||
| 229 | + this->destroy(); | ||
| 230 | + this->copy(rhs); | ||
| 231 | + } | ||
| 232 | + return *this; | ||
| 233 | + } | ||
| 234 | + PointerHolder& operator=(decltype(nullptr)) | ||
| 235 | + { | ||
| 236 | + this->operator=(PointerHolder<T>()); | ||
| 237 | + return *this; | ||
| 238 | + } | ||
| 117 | ~PointerHolder() | 239 | ~PointerHolder() |
| 118 | - { | ||
| 119 | - this->destroy(); | ||
| 120 | - } | 240 | + { |
| 241 | + this->destroy(); | ||
| 242 | + } | ||
| 121 | bool operator==(PointerHolder const& rhs) const | 243 | bool operator==(PointerHolder const& rhs) const |
| 122 | { | 244 | { |
| 123 | - return this->data->pointer == rhs.data->pointer; | 245 | + return this->data->pointer == rhs.data->pointer; |
| 246 | + } | ||
| 247 | + bool operator==(decltype(nullptr)) const | ||
| 248 | + { | ||
| 249 | + return this->data->pointer == nullptr; | ||
| 124 | } | 250 | } |
| 125 | bool operator<(PointerHolder const& rhs) const | 251 | bool operator<(PointerHolder const& rhs) const |
| 126 | { | 252 | { |
| 127 | - return this->data->pointer < rhs.data->pointer; | 253 | + return this->data->pointer < rhs.data->pointer; |
| 128 | } | 254 | } |
| 129 | 255 | ||
| 130 | // get() is for interface compatibility with std::shared_ptr | 256 | // get() is for interface compatibility with std::shared_ptr |
| @@ -135,33 +261,33 @@ class PointerHolder | @@ -135,33 +261,33 @@ class PointerHolder | ||
| 135 | 261 | ||
| 136 | // NOTE: The pointer returned by getPointer turns into a pumpkin | 262 | // NOTE: The pointer returned by getPointer turns into a pumpkin |
| 137 | // when the last PointerHolder that contains it disappears. | 263 | // when the last PointerHolder that contains it disappears. |
| 138 | -#ifndef NO_POINTERHOLDER_DEPRECATION | 264 | +#if POINTERHOLDER_TRANSITION >= 2 |
| 139 | [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]] | 265 | [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]] |
| 140 | -#endif | 266 | +#endif // POINTERHOLDER_TRANSITION >= 2 |
| 141 | T* getPointer() | 267 | T* getPointer() |
| 142 | - { | ||
| 143 | - return this->data->pointer; | ||
| 144 | - } | ||
| 145 | -#ifndef NO_POINTERHOLDER_DEPRECATION | 268 | + { |
| 269 | + return this->data->pointer; | ||
| 270 | + } | ||
| 271 | +#if POINTERHOLDER_TRANSITION >= 2 | ||
| 146 | [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]] | 272 | [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]] |
| 147 | -#endif | 273 | +#endif // POINTERHOLDER_TRANSITION >= 2 |
| 148 | T const* getPointer() const | 274 | T const* getPointer() const |
| 149 | - { | ||
| 150 | - return this->data->pointer; | ||
| 151 | - } | ||
| 152 | -#ifndef NO_POINTERHOLDER_DEPRECATION | 275 | + { |
| 276 | + return this->data->pointer; | ||
| 277 | + } | ||
| 278 | +#if POINTERHOLDER_TRANSITION >= 2 | ||
| 153 | [[deprecated("use use_count() instead of getRefcount()")]] | 279 | [[deprecated("use use_count() instead of getRefcount()")]] |
| 154 | -#endif | 280 | +#endif // POINTERHOLDER_TRANSITION >= 2 |
| 155 | int getRefcount() const | 281 | int getRefcount() const |
| 156 | - { | ||
| 157 | - return this->data->refcount; | ||
| 158 | - } | 282 | + { |
| 283 | + return this->data->refcount; | ||
| 284 | + } | ||
| 159 | 285 | ||
| 160 | // use_count() is for compatibility with std::shared_ptr | 286 | // use_count() is for compatibility with std::shared_ptr |
| 161 | long use_count() | 287 | long use_count() |
| 162 | - { | ||
| 163 | - return static_cast<long>(this->data->refcount); | ||
| 164 | - } | 288 | + { |
| 289 | + return static_cast<long>(this->data->refcount); | ||
| 290 | + } | ||
| 165 | 291 | ||
| 166 | T const& operator*() const | 292 | T const& operator*() const |
| 167 | { | 293 | { |
| @@ -183,30 +309,44 @@ class PointerHolder | @@ -183,30 +309,44 @@ class PointerHolder | ||
| 183 | 309 | ||
| 184 | private: | 310 | private: |
| 185 | void init(Data* data) | 311 | void init(Data* data) |
| 186 | - { | ||
| 187 | - this->data = data; | ||
| 188 | - ++this->data->refcount; | ||
| 189 | - } | 312 | + { |
| 313 | + this->data = data; | ||
| 314 | + ++this->data->refcount; | ||
| 315 | + } | ||
| 190 | void copy(PointerHolder const& rhs) | 316 | void copy(PointerHolder const& rhs) |
| 191 | - { | ||
| 192 | - this->init(rhs.data); | ||
| 193 | - } | 317 | + { |
| 318 | + this->init(rhs.data); | ||
| 319 | + } | ||
| 194 | void destroy() | 320 | void destroy() |
| 195 | - { | ||
| 196 | - bool gone = false; | ||
| 197 | - { | ||
| 198 | - if (--this->data->refcount == 0) | ||
| 199 | - { | ||
| 200 | - gone = true; | ||
| 201 | - } | ||
| 202 | - } | ||
| 203 | - if (gone) | ||
| 204 | - { | ||
| 205 | - delete this->data; | ||
| 206 | - } | ||
| 207 | - } | 321 | + { |
| 322 | + bool gone = false; | ||
| 323 | + { | ||
| 324 | + if (--this->data->refcount == 0) | ||
| 325 | + { | ||
| 326 | + gone = true; | ||
| 327 | + } | ||
| 328 | + } | ||
| 329 | + if (gone) | ||
| 330 | + { | ||
| 331 | + delete this->data; | ||
| 332 | + } | ||
| 333 | + } | ||
| 208 | 334 | ||
| 209 | Data* data; | 335 | Data* data; |
| 210 | }; | 336 | }; |
| 211 | 337 | ||
| 338 | +template<typename T, typename... _Args> | ||
| 339 | +inline PointerHolder<T> | ||
| 340 | +make_pointer_holder(_Args&&... __args) | ||
| 341 | +{ | ||
| 342 | + return PointerHolder<T>(new T(__args...)); | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +template <typename T> | ||
| 346 | +PointerHolder<T> | ||
| 347 | +make_array_pointer_holder(size_t n) | ||
| 348 | +{ | ||
| 349 | + return PointerHolder<T>(true, new T[n]); | ||
| 350 | +} | ||
| 351 | + | ||
| 212 | #endif // POINTERHOLDER_HH | 352 | #endif // POINTERHOLDER_HH |
libtests/pointer_holder.cc
| 1 | -#define NO_POINTERHOLDER_DEPRECATION // we need to test the deprecated API | 1 | +// We need to test the deprecated API |
| 2 | +#ifdef POINTERHOLDER_TRANSITION | ||
| 3 | +# undef POINTERHOLDER_TRANSITION | ||
| 4 | +#endif | ||
| 5 | +#define POINTERHOLDER_TRANSITION 0 | ||
| 2 | #include <qpdf/PointerHolder.hh> | 6 | #include <qpdf/PointerHolder.hh> |
| 3 | 7 | ||
| 4 | #include <iostream> | 8 | #include <iostream> |
manual/design.rst
| @@ -751,16 +751,26 @@ actually quite rare and largely avoidable. | @@ -751,16 +751,26 @@ actually quite rare and largely avoidable. | ||
| 751 | Smart Pointers | 751 | Smart Pointers |
| 752 | -------------- | 752 | -------------- |
| 753 | 753 | ||
| 754 | -This section describes changes to the use of smart pointers in qpdf in | ||
| 755 | -versions 10.6.0 and 11.0.0. | 754 | +This section describes changes to the use of smart pointers there were |
| 755 | +made in qpdf 10.6.0 as well as some planned for 11.0.0. | ||
| 756 | 756 | ||
| 757 | Starting in qpdf 11, ``PointerHolder`` will be replaced with | 757 | Starting in qpdf 11, ``PointerHolder`` will be replaced with |
| 758 | ``std::shared_ptr`` in qpdf's public API. A backward-compatible | 758 | ``std::shared_ptr`` in qpdf's public API. A backward-compatible |
| 759 | -``PointerHolder`` will be provided that should make it possible for | ||
| 760 | -most code to remain unchanged. This new ``PointerHolder`` will be | ||
| 761 | -marked deprecated but will provide a way to suppress the deprecation | ||
| 762 | -warnings. Code that works with containers of ``PointerHolder`` may | ||
| 763 | -have to be modified, though no qpdf interfaces do this. | 759 | +``PointerHolder`` class will be provided that should make it possible |
| 760 | +for most code to remain unchanged. ``PointerHolder`` may eventually be | ||
| 761 | +removed from qpdf entirely, but this will not happen for a while to | ||
| 762 | +make it easier for people who need to support multiple versions of | ||
| 763 | +qpdf. | ||
| 764 | + | ||
| 765 | +The ``POINTERHOLDER_TRANSITION`` preprocessor symbol has been | ||
| 766 | +introduced to help people transition from ``PointerHolder`` to | ||
| 767 | +``std::shared_ptr``. After qpdf 11 is released, to prepare for a | ||
| 768 | +future qpdf without ``PointerHolder`` and to let them know that it is | ||
| 769 | +no longer needed, a warning will be issued if | ||
| 770 | +``<qpdf/PointerHolder.hh>`` is included, though it will be possible to | ||
| 771 | +suppress the warning by defining ``POINTERHOLDER_TRANSITION``. In | ||
| 772 | +10.6.0, there are some steps you can perform to prepare, but no action | ||
| 773 | +is required. | ||
| 764 | 774 | ||
| 765 | The remainder of this section describes how to prepare if you want to | 775 | The remainder of this section describes how to prepare if you want to |
| 766 | eliminate ``PointerHolder`` from your code or what to do if you want | 776 | eliminate ``PointerHolder`` from your code or what to do if you want |
| @@ -769,25 +779,31 @@ to stick with the old interfaces. | @@ -769,25 +779,31 @@ to stick with the old interfaces. | ||
| 769 | Changes in 10.6.0 | 779 | Changes in 10.6.0 |
| 770 | ~~~~~~~~~~~~~~~~~ | 780 | ~~~~~~~~~~~~~~~~~ |
| 771 | 781 | ||
| 772 | -In qpdf 10.6.0, two ``PointerHolder`` methods have been deprecated and | ||
| 773 | -replaced with methods that are compatible with ``std::shared_ptr``: | 782 | +In qpdf 10.6.0, the following changes have been made to |
| 783 | +``PointerHolder`` to make its behavior closer to that of | ||
| 784 | +``std::shared_ptr``: | ||
| 774 | 785 | ||
| 775 | -- ``getPointer()`` -- use ``get()`` instead | 786 | +- ``get()`` has been added as an alternative to ``getPointer()`` |
| 776 | 787 | ||
| 777 | -- ``getRefcount()`` -- use ``use_count()`` instead | 788 | +- ``use_count()`` has been added as an alternative to ``getRefcount()`` |
| 778 | 789 | ||
| 779 | -If you build your code with deprecation warnings enabled and you want | ||
| 780 | -to suppress these deprecation warnings for now, you can ``#define | ||
| 781 | -NO_POINTERHOLDER_DEPRECATION`` before including any qpdf header files. | ||
| 782 | -It may be possible to leave it this way long-term to facilitate | ||
| 783 | -supporting older versions of qpdf without conditional compilation. | 790 | +- A new global helper function ``make_pointer_holder`` behaves |
| 791 | + similarly to ``std::make_shared``, so you can use | ||
| 792 | + ``make_pointer_holder<T>(args...)`` to create a ``PointerHolder<T>`` | ||
| 793 | + with ``new T(args...)`` as the pointer. | ||
| 794 | + | ||
| 795 | +- A new global helper function ``make_array_pointer_holder`` takes a | ||
| 796 | + size and creates a ``PointerHolder`` to an array. It is a | ||
| 797 | + counterpart to the newly added ``QUtil::make_shared_array`` method, | ||
| 798 | + which does the same thing with a ``std::shared_ptr``. | ||
| 784 | 799 | ||
| 785 | ``PointerHolder`` has had a long-standing bug: a ``const | 800 | ``PointerHolder`` has had a long-standing bug: a ``const |
| 786 | PointerHolder<T>`` would only provide a ``T const*`` with its | 801 | PointerHolder<T>`` would only provide a ``T const*`` with its |
| 787 | -``getPointer`` method. This is incorrect and is not how standard C++ | ||
| 788 | -smart pointers or regular pointers behave. The correct semantics | ||
| 789 | -would be that a ``const PointerHolder<T>`` would not accept a new | ||
| 790 | -pointer after being created but would still allow you to modify the | 802 | +``getPointer`` method. This is incorrect and is not how standard |
| 803 | +library C++ smart pointers or regular pointers behave. The correct | ||
| 804 | +semantics would be that a ``const PointerHolder<T>`` would not accept | ||
| 805 | +a new pointer after being created (``PointerHolder`` has always | ||
| 806 | +behaved correctly in this way) but would still allow you to modify the | ||
| 791 | item being pointed to. If you don't want to mutate the thing it points | 807 | item being pointed to. If you don't want to mutate the thing it points |
| 792 | to, use ``PointerHolder<T const>`` instead. The new ``get()`` method | 808 | to, use ``PointerHolder<T const>`` instead. The new ``get()`` method |
| 793 | behaves correctly. It is therefore not exactly the same as | 809 | behaves correctly. It is therefore not exactly the same as |
| @@ -795,157 +811,290 @@ behaves correctly. It is therefore not exactly the same as | @@ -795,157 +811,290 @@ behaves correctly. It is therefore not exactly the same as | ||
| 795 | ``std::shared_ptr``. This shouldn't make any difference to any | 811 | ``std::shared_ptr``. This shouldn't make any difference to any |
| 796 | correctly written code. | 812 | correctly written code. |
| 797 | 813 | ||
| 814 | +Differences between ``PointerHolder`` and ``std::shared_ptr`` | ||
| 815 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 816 | + | ||
| 817 | +Here is a list of things you need to think about when migrating from | ||
| 818 | +``PointerHolder`` to ``std::shared_ptr``. After the list, we will | ||
| 819 | +discuss how to address each one using the ``POINTERHOLDER_TRANSITION`` | ||
| 820 | +preprocessor symbol or other C++ coding techniques. | ||
| 821 | + | ||
| 822 | +- ``PointerHolder<T>`` has an *implicit* constructor that takes a | ||
| 823 | + ``T*``, which means you can assign a ``T*`` directly to a | ||
| 824 | + ``PointerHolder<T>`` or pass a ``T*`` to a function that expects a | ||
| 825 | + ``PointerHolder<T>`` as a parameter. ``std::shared_ptr<T>`` does not | ||
| 826 | + have this behavior, though you can still assign ``nullptr`` to a | ||
| 827 | + ``std::shared_ptr<T>`` and compare ``nullptr`` with a | ||
| 828 | + ``std::shared_ptr<T>``. Here are some examples of how you might need | ||
| 829 | + to change your code: | ||
| 830 | + | ||
| 831 | + Old code: | ||
| 832 | + .. code-block:: c++ | ||
| 833 | + | ||
| 834 | + PointerHolder<X> x_p; | ||
| 835 | + X* x = new X(); | ||
| 836 | + x_p = x; | ||
| 837 | + | ||
| 838 | + New code: | ||
| 839 | + .. code-block:: c++ | ||
| 840 | + | ||
| 841 | + auto x_p = std::make_shared<X>(); | ||
| 842 | + X* x = x_p.get(); | ||
| 843 | + // or, less safe, but closer: | ||
| 844 | + std::shared_ptr<X> x_p; | ||
| 845 | + X* x = new X(); | ||
| 846 | + x_p = std::shared_ptr<X>(x); | ||
| 847 | + | ||
| 848 | + Old code: | ||
| 849 | + .. code-block:: c++ | ||
| 850 | + | ||
| 851 | + PointerHolder<Base> base_p; | ||
| 852 | + Derived* derived = new Derived(); | ||
| 853 | + base_p = derived; | ||
| 854 | + | ||
| 855 | + New code: | ||
| 856 | + .. code-block:: c++ | ||
| 857 | + | ||
| 858 | + std::shared_ptr<Base> base_p; | ||
| 859 | + Derived* derived = new Derived(); | ||
| 860 | + base_p = std::shared_ptr<Base>(derived); | ||
| 861 | + | ||
| 862 | +- ``PointerHolder<T>`` has ``getPointer()`` to get the underlying | ||
| 863 | + pointer. It also has the seldom-used ``getRefcount()`` method to get | ||
| 864 | + the reference count. ``std::shared_ptr<T>`` has ``get()`` and | ||
| 865 | + ``use_count()``. In qpdf 10.6, ``PointerHolder<T>`` also has | ||
| 866 | + would not be an issue unless you did this in your own code. | ||
| 867 | + | ||
| 868 | +Addressing the Differences | ||
| 869 | +~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 870 | + | ||
| 871 | +If you need to support versions of qpdf prior to qpdf 10.6, you don't | ||
| 872 | +*need* to take any action at this time, but it is recommended that you | ||
| 873 | +at least address the implicit constructor issue since this can be done | ||
| 874 | +without breaking backward compatibility. (Explicit construction of | ||
| 875 | +``PointerHolder<T>`` is and always has been allowed.) | ||
| 876 | + | ||
| 877 | +There are two significant things you can do to minimize the impact of | ||
| 878 | +switching from ``PointerHolder`` to ``std::shared_ptr``: | ||
| 879 | + | ||
| 880 | +- Use ``auto`` and ``decltype`` whenever possible when working with | ||
| 881 | + ``PointerHolder`` variables that are exchanged with the qpdf API. | ||
| 882 | + | ||
| 883 | +- Use the ``POINTERHOLDER_TRANSITION`` preprocessor symbol to identify | ||
| 884 | + and resolve the differences described above. | ||
| 885 | + | ||
| 886 | +To use ``POINTERHOLDER_TRANSITION``, you will need to ``#define`` it | ||
| 887 | +before including any qpdf header files or specify its value as part of | ||
| 888 | +your build. The table below describes the values of | ||
| 889 | +``POINTERHOLDER_TRANSITION``. This informatoin is also summarized in | ||
| 890 | +:file:`include/qpdf/PointerHolder.hh`, so you will have it handy | ||
| 891 | +without consulting this manual. | ||
| 892 | + | ||
| 893 | +.. list-table:: POINTERHOLDER_TRANSITION values | ||
| 894 | + :widths: 5 80 | ||
| 895 | + :header-rows: 1 | ||
| 896 | + | ||
| 897 | + - - value | ||
| 898 | + - meaning | ||
| 899 | + | ||
| 900 | + - - undefined | ||
| 901 | + - same as ``0``, but start with qpdf 11.0, issues a warning | ||
| 902 | + | ||
| 903 | + - - ``0`` | ||
| 904 | + - provide a backward compatible ``PointerHolder`` and suppress | ||
| 905 | + all deprecation warnings | ||
| 906 | + | ||
| 907 | + - - ``1`` | ||
| 908 | + - Make the ``PointerHolder<T>(T*)`` constructor explicit | ||
| 909 | + | ||
| 910 | + - - ``2`` | ||
| 911 | + - Deprecate ``getPointer()`` and ``getRefcount()`` | ||
| 912 | + | ||
| 913 | + - - ``3`` | ||
| 914 | + - Starting in qpdf 11, deprecate all uses of ``PointerHolder`` | ||
| 915 | + | ||
| 916 | + - - ``4`` | ||
| 917 | + - Starting in qpdf 11, disable all functionality from | ||
| 918 | + ``qpdf/PointerHolder.hh`` so that ``#include``-ing it has no | ||
| 919 | + effect. | ||
| 920 | + | ||
| 921 | +Based on the above, here is a procedure for preparing your code. This | ||
| 922 | +is the procedure that was used for the qpdf code itself. | ||
| 923 | + | ||
| 924 | +If you need to support versions of qpdf prior to 10.6, you can still | ||
| 925 | +do these steps: | ||
| 926 | + | ||
| 927 | +- Find all occurrences of ``PointerHolder`` in the code. See whether | ||
| 928 | + any of them can just be outright replaced with ``std::shared_ptr`` | ||
| 929 | + or ``std::unique_ptr``. If you have been using qpdf prior to | ||
| 930 | + adopting C++11 and were using ``PointerHolder`` as a general-purpose | ||
| 931 | + smart pointer, you may have cases that can be replaced in this way. | ||
| 932 | + | ||
| 933 | + For example: | ||
| 934 | + | ||
| 935 | + - Simple ``PointerHolder<T>`` construction can be replaced with | ||
| 936 | + either the equivalent ``std::shared_ptr<T>`` construction or, if | ||
| 937 | + the constructor is public, with ``std::make_shared<T>(args...)``. | ||
| 938 | + If you are creating a smart pointer that is never copied, you may | ||
| 939 | + be able to use ``std::unique_ptr<T>`` instead. | ||
| 940 | + | ||
| 941 | + - Array allocations will have to be rewritten. | ||
| 798 | 942 | ||
| 799 | -How to Prepare | ||
| 800 | -~~~~~~~~~~~~~~ | 943 | + Allocating a ``PointerHolder`` to an array looked like this: |
| 801 | 944 | ||
| 802 | -If you don't need to support versions of qpdf prior to 10.6, you can | ||
| 803 | -just replace all occurrences of ``getPointer()`` with ``get()`` and | ||
| 804 | -all occurrences of ``getRefcount()`` with ``use_count()``. That's | ||
| 805 | -about all you will be able to do prior to qpdf 11. | 945 | + .. code-block:: c++ |
| 806 | 946 | ||
| 807 | -If you need to support older versions, you have two choices: | 947 | + PointerHolder<X> p(true, new X[n]); |
| 808 | 948 | ||
| 809 | -- ``#define NO_POINTERHOLDER_DEPRECATION`` and leave everything the | ||
| 810 | - way it was. You can just wait until qpdf 11. | 949 | + To allocate a ``std::shared_ptr`` to an array: |
| 811 | 950 | ||
| 812 | -- Write code that uses ``get()`` but falls back to ``getPointer()`` if | ||
| 813 | - ``QPDF_MAJOR_VERSION`` is not defined. The symbols | ||
| 814 | - ``QPDF_MAJOR_VERSION``, ``QPDF_MINOR_VERSION``, and | ||
| 815 | - ``QPDF_PATCH_VERSION`` were introduced with 10.6.0, so just checking | ||
| 816 | - for whether ``QPDF_MAJOR_VERSION`` is defined is sufficient for | ||
| 817 | - telling if you're running a version before 10.6.0. If you do this, | ||
| 818 | - once qpdf 11 comes out, you will already know all the places that | ||
| 819 | - have to be handled specially. | 951 | + .. code-block:: c++ |
| 820 | 952 | ||
| 821 | -If you are somehow relying on the fact that a ``const | ||
| 822 | -PointerHolder<T>`` always gave back a ``T const*`` and are | ||
| 823 | -dereferencing a ``const PointerHolder<T>`` to call methods that only | ||
| 824 | -have ``const`` versions in ``T``, you may have to change from | ||
| 825 | -``const PointerHolder<T>`` to ``PointerHolder<T const>``. This won't | ||
| 826 | -be an issue for anything in the qpdf API, and if you are using qpdf | ||
| 827 | -``PointerHolder`` objects for any other reason, you should just | ||
| 828 | -replace them with ``std::shared_ptr``. | 953 | + auto p = std::shared_ptr<X>(new X[n], std::default_delete<X[]>()); |
| 954 | + // If you don't mind using QUtil, there's QUtil::make_shared_array<X>(n). | ||
| 955 | + // If you are using c++20, you can use std::make_shared<X[]>(n) | ||
| 956 | + // to get a std::shared_ptr<X[]> instead of a std::shared_ptr<X>. | ||
| 829 | 957 | ||
| 830 | -What to Expect | ||
| 831 | -~~~~~~~~~~~~~~ | 958 | + To allocate a ``std::unique_ptr`` to an array: |
| 832 | 959 | ||
| 833 | -Note: if you are reading this in the 10.6 manual and 11 is out, you | ||
| 834 | -should read it in the manual for qpdf 11 instead. Some early tests | ||
| 835 | -have been done to try to ensure the accuracy of this information, but | ||
| 836 | -it may change once the work is actually completed. | 960 | + .. code-block:: c++ |
| 837 | 961 | ||
| 838 | -When ``PointerHolder`` disappears from qpdf's API in qpdf 11, you will | ||
| 839 | -have a few options: | 962 | + auto p = std::make_unique<X[]>(n); |
| 963 | + // or, if X has a private constructor: | ||
| 964 | + auto p = std::unique_ptr<X[]>(new X[n]); | ||
| 840 | 965 | ||
| 841 | -- Use the new ``PointerHolder``, which is derived from | ||
| 842 | - ``std::shared_ptr`` and which has methods to make it | ||
| 843 | - interchangeable. For things that use ``PointerHolder<T>`` directly, | ||
| 844 | - this should "just work," though you will have to ``#define | ||
| 845 | - NO_POINTERHOLDER_DEPRECATION`` if you don't want deprecation | ||
| 846 | - warnings. | 966 | +- If a ``PointerHolder<T>`` can't be replaced with a standard library |
| 967 | + smart pointer, perhaps it can be declared using ``auto`` or | ||
| 968 | + ``decltype`` so that, when the qpdf API changes, your code will just | ||
| 969 | + need to be recompiled. | ||
| 847 | 970 | ||
| 848 | -- Replace all uses of ``PointerHolder<T>`` with ``std::shared_ptr<T>`` | ||
| 849 | - and deal with the required changes, outlined below. This is the | ||
| 850 | - recommended course of action. You will need conditional compilation | ||
| 851 | - if you want to simultaneously support order code. Stay tuned for the | ||
| 852 | - qpdf 11 documentation for specifics. | 971 | +- ``#define POINTERHOLDER_TRANSITION 1`` to enable deprecation |
| 972 | + warnings for all implicit constructions of ``PointerHolder<T>`` from | ||
| 973 | + a plain ``T*``. When you find one, explicitly construct the | ||
| 974 | + ``PointerHolder<T>``. | ||
| 853 | 975 | ||
| 854 | -While ``PointerHolder<T>`` and ``std::shared_ptr<T>`` will be mutually | ||
| 855 | -assignable and convertible, this does not apply to containers of those | ||
| 856 | -objects. The qpdf API doesn't have any containers of | ||
| 857 | -``PointerHolder``, so this would have to be in your own code. You can | ||
| 858 | -prepare yourself for the change by using ``auto`` and ``decltype`` | ||
| 859 | -whenever possible so that a change to the underlying type of something | ||
| 860 | -won't require source changes. | 976 | + - Old code: |
| 861 | 977 | ||
| 862 | -Required Changes in qpdf 11 | ||
| 863 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 978 | + .. code-block:: c++ |
| 864 | 979 | ||
| 865 | -This section describes unavoidable changes when replacing | ||
| 866 | -``PointerHolder`` with ``std::shared_ptr`` rather than continuing to | ||
| 867 | -use the backward compatible API. Nothing here is needed (or can be | ||
| 868 | -done) prior to qpdf 11, so consider this to be a preview. | 980 | + PointerHolder<X> x = new X(); |
| 869 | 981 | ||
| 870 | -- Change ``getPointer`` to ``get`` and ``getRefcount`` to | ||
| 871 | - ``use_count`` as above. If your starting point is no deprecation | ||
| 872 | - warnings with qpdf 10.6, this will already be true. | 982 | + - New code: |
| 873 | 983 | ||
| 874 | -- Array allocations will have to be rewritten. | 984 | + .. code-block:: c++ |
| 875 | 985 | ||
| 876 | - To allocate a ``PointerHolder`` to an array: | 986 | + auto x = PointerHolder<X>(new X(...)); // all versions of qpdf |
| 987 | + // or, if X(...) is public: | ||
| 988 | + auto x = make_pointer_holder<X>(...); // only 10.6 and above | ||
| 877 | 989 | ||
| 878 | - .. code-block:: c++ | 990 | + Other examples appear above. |
| 879 | 991 | ||
| 880 | - PointerHolder<X> p(true, new X[n]); | 992 | +If you need to support older versions of qpdf than 10.6, this is as |
| 993 | +far as you can go until qpdf 11 comes out. | ||
| 881 | 994 | ||
| 882 | - To allocate a ``std::shared_ptr`` to an array: | 995 | +If you only need to support the latest version of qpdf, proceed as |
| 996 | +follows: | ||
| 883 | 997 | ||
| 884 | - .. code-block:: c++ | 998 | +- ``#define POINTERHOLDER_TRANSITION 2`` to enable deprecation of |
| 999 | + ``getPointer()`` and ``getRefcount()`` | ||
| 885 | 1000 | ||
| 886 | - auto p = std::shared_ptr<X>(new X[n], std::default_delete<X[]>()); | 1001 | +- Replace ``getPointer()`` with ``get()`` and ``getRefcount()`` with |
| 1002 | + ``use_count()``. These methods were not present prior to 10.6.0. | ||
| 887 | 1003 | ||
| 888 | - To allocate a ``std::unique_ptr`` to an array: | 1004 | +When you have gotten your code to compile cleanly with |
| 1005 | +``POINTERHOLDER_TRANSITION=2``, you are well on your way to being | ||
| 1006 | +ready for eliminating ``PointerHolder`` entirely after qpdf 11 is | ||
| 1007 | +released. | ||
| 889 | 1008 | ||
| 890 | - .. code-block:: c++ | 1009 | +After qpdf 11 is out |
| 1010 | +~~~~~~~~~~~~~~~~~~~~ | ||
| 891 | 1011 | ||
| 892 | - auto p = std::make_unique<X[]>(n); | ||
| 893 | - // or | ||
| 894 | - auto p = std::unique_ptr<X[]>(new X[n]); | 1012 | +In the 10.6 manual, this section represents a plan and is subject to |
| 1013 | +change. However, it has been tested in practice using a version of the | ||
| 1014 | +qpdf 11 ``PointerHolder`` on a branch, so it is likely to be accurate. | ||
| 1015 | +In the meantime, think of this is a preview. | ||
| 895 | 1016 | ||
| 896 | - The second form may be needed if ``X`` has a private constructor | ||
| 897 | - from this context. | 1017 | +First, make sure you have done the steps in the 10.6 section. (Note: |
| 1018 | +once qpdf 11 comes out, the goal is to not have to migrate to 10.6 | ||
| 1019 | +first, so it is likely that these sections will be combined.) | ||
| 898 | 1020 | ||
| 899 | - C++-17 has a better way to allocate ``std::shared_ptr`` to an array, | ||
| 900 | - but qpdf is still allowing C++-14 to be used. You can use whatever | ||
| 901 | - method to handle shared arrays that is supported in your | ||
| 902 | - environment. There are no shared arrays in qpdf's public API except | ||
| 903 | - for some ``QUtil`` helper methods that are not essential for use of | ||
| 904 | - qpdf features. | 1021 | +If you are explicitly choosing to stick with the backward compatible |
| 1022 | +``PointerHolder`` for now, you should define | ||
| 1023 | +``POINTERHOLDER_TRANSITION`` to ``0`` to suppress the warning from | ||
| 1024 | +including ``qpdf/PointerHolder.hh``. Be aware that you may eventually | ||
| 1025 | +have to deal with the transition, though the intention is to leave the | ||
| 1026 | +compatibility layer in place for a while. You should rebuild and test | ||
| 1027 | +your code. There may be compiler errors if you have containers of | ||
| 1028 | +``PointerHolder``, but most code should compile without any changes. | ||
| 1029 | +Even if you have errors, use of ``auto`` or ``decltype`` may enable | ||
| 1030 | +you to write code that works with the old and new API without having | ||
| 1031 | +to use conditional compilation. The | ||
| 1032 | +``POINTERHOLDER_IS_SHARED_POINTER`` is defined in qpdf 11 if you | ||
| 1033 | +``#include <qpdf/PointerHolder.hh>``. | ||
| 905 | 1034 | ||
| 906 | -- ``PointerHolder<T>`` can have plain pointers directly assigned to | ||
| 907 | - it, while ``std::shared_ptr<T>`` cannot. This makes code like this | ||
| 908 | - possible: | 1035 | +If you want to support older versions of qpdf and still transition so |
| 1036 | +that the backward-compatible ``PointerHolder`` is not in use, you can | ||
| 1037 | +separate old code and new code by testing with the | ||
| 1038 | +``POINTERHOLDER_IS_SHARED_POINTER`` preprocessor symbol, as in | ||
| 909 | 1039 | ||
| 910 | - .. code-block:: c++ | 1040 | +.. code-block:: c++ |
| 911 | 1041 | ||
| 912 | - PointerHolder<X> x_p; | ||
| 913 | - X* x = new X(); | ||
| 914 | - x_p = x; | 1042 | + #ifdef POINTERHOLDER_IS_SHARED_POINTER |
| 1043 | + std::shared_ptr<X> x; | ||
| 1044 | + #else | ||
| 1045 | + PointerHolder<X> x; | ||
| 1046 | + #endif // POINTERHOLDER_IS_SHARED_POINTER | ||
| 1047 | + x = decltype(x)(new X()) | ||
| 915 | 1048 | ||
| 916 | - It also makes it possible to pass a plain pointer to a function | ||
| 917 | - expecting a ``PointerHolder``, thereby transferring "ownership" of | ||
| 918 | - the pointer into the function. | 1049 | +or |
| 919 | 1050 | ||
| 920 | - Code like that is a risky because you can leak memory if an | ||
| 921 | - exception is thrown between creation of the X and assignment of it | ||
| 922 | - into the ``PointerHolder``. In any case, ``std::shared_ptr`` does | ||
| 923 | - not allow that, so you need one of these instead: | 1051 | +.. code-block:: c++ |
| 924 | 1052 | ||
| 925 | - .. code-block:: c++ | 1053 | + #ifdef POINTERHOLDER_IS_SHARED_POINTER |
| 1054 | + auto x_p = std::make_shared<X>(); | ||
| 1055 | + X* x = x_p.get(); | ||
| 1056 | + #else | ||
| 1057 | + auto x_p = PointerHolder<X>(new X()); | ||
| 1058 | + X* x = x_p.getPointer(); | ||
| 1059 | + #endif // POINTERHOLDER_IS_SHARED_POINTER | ||
| 1060 | + x_p->doSomething(); | ||
| 1061 | + x->doSomethingElse(); | ||
| 926 | 1062 | ||
| 927 | - auto x_p = std::make_shared<X>(); | ||
| 928 | - X* x = x_p.get(); | ||
| 929 | - // or, less safe, but closer: | ||
| 930 | - std::shared_ptr<X> x_p; | ||
| 931 | - X* x = new X(); | ||
| 932 | - x_p = std::shared_ptr<X>(x); | 1063 | +If you don't need to support older versions of qpdf, you can proceed |
| 1064 | +with these steps without protecting changes with the preprocessor | ||
| 1065 | +symbol. Here are the remaining changes. | ||
| 933 | 1066 | ||
| 934 | - Also, code like this: | 1067 | +- Make sure you have a clean build with ``POINTERHOLDER_TRANSITION`` |
| 1068 | + set to ``2``. This means that you are using ``PointerHolder`` in a | ||
| 1069 | + manner that is API-compatible with ``std::shared_ptr`` in all cases | ||
| 1070 | + except for array pointers. | ||
| 935 | 1071 | ||
| 936 | - .. code-block:: c++ | 1072 | +- Replace all occurrences of ``PointerHolder`` with |
| 1073 | + ``std::shared_ptr`` except in ``#include <qpdf/PointerHolder.hh>`` | ||
| 937 | 1074 | ||
| 938 | - PointerHolder<Base> base_p; | ||
| 939 | - Derived* derived = new Derived(); | ||
| 940 | - base_p = derived; | 1075 | +- Replace all occurrences of ``make_pointer_holder`` with |
| 1076 | + ``std::make_shared`` | ||
| 941 | 1077 | ||
| 942 | - needs to be replaced with something like this instead: | 1078 | +- Replace all occurrences of ``make_array_pointer_holder`` with |
| 1079 | + ``QUtil::make_shared_array``. You will need to include | ||
| 1080 | + ``<qpdf/QUtil.hh>`` if you haven't already done so. | ||
| 943 | 1081 | ||
| 944 | - .. code-block:: c++ | 1082 | +- Make sure ``<memory>`` is included wherever you were including |
| 1083 | + ``<qpdf/PointerHolder.hh>``. | ||
| 945 | 1084 | ||
| 946 | - std::shared_ptr<Base> base_p; | ||
| 947 | - Derived* derived = new Derived(); | ||
| 948 | - base_p = std::shared_ptr<Base>(derived); | 1085 | +- If you were using any array ``PointerHolder<T>`` objects, replace |
| 1086 | + them as above. You can let the compiler find these for you. | ||
| 1087 | + | ||
| 1088 | +- ``#define POINTERHOLDER_TRANSITION 3`` to enable deprecation of | ||
| 1089 | + all ``PointerHolder<T>`` construction. | ||
| 1090 | + | ||
| 1091 | +- Build and test. Fix any remaining issues. | ||
| 1092 | + | ||
| 1093 | +- If not supporting older qpdf, remove all references to | ||
| 1094 | + ``<qpdf/PointerHolder.hh>``. Otherwise, you wil still need to | ||
| 1095 | + include it but can ``#define POINTERHOLDER_TRANSITION 4`` to prevent | ||
| 1096 | + ``PointerHolder`` from being defined. The | ||
| 1097 | + ``POINTERHOLDER_IS_SHARED_POINTER`` symbol will still be defined. | ||
| 949 | 1098 | ||
| 950 | Historical Background | 1099 | Historical Background |
| 951 | ~~~~~~~~~~~~~~~~~~~~~ | 1100 | ~~~~~~~~~~~~~~~~~~~~~ |
| @@ -953,13 +1102,6 @@ Historical Background | @@ -953,13 +1102,6 @@ Historical Background | ||
| 953 | Since its inception, the qpdf library used its own smart pointer | 1102 | Since its inception, the qpdf library used its own smart pointer |
| 954 | class, ``PointerHolder``. The ``PointerHolder`` class was originally | 1103 | class, ``PointerHolder``. The ``PointerHolder`` class was originally |
| 955 | created long before ``std::shared_ptr`` existed, and qpdf itself | 1104 | created long before ``std::shared_ptr`` existed, and qpdf itself |
| 956 | -didn't start requiring a C++-11 compiler version 9.1.0 released in | ||
| 957 | -late 2019. | ||
| 958 | - | ||
| 959 | -``PointerHolder`` is a reference-counted smart pointer with semantics | ||
| 960 | -almost identical to ``std::shared_ptr`` except that it is not | ||
| 961 | -thread-safe. It has a few interface differences that prevent | ||
| 962 | -``std::shared_ptr`` from being a drop-in replacement. However, given | ||
| 963 | -the value of using standard library smart pointers, qpdf is taking the | ||
| 964 | -plunge for version 11 and switching over to standard library smart | ||
| 965 | -pointers. | 1105 | +didn't start requiring a C++11 compiler version 9.1.0 released in |
| 1106 | +late 2019. With current C++ versions, is no longer desirable for qpdf | ||
| 1107 | +to have its own smart pointer class. |
manual/release-notes.rst
| @@ -7,28 +7,15 @@ For a detailed list of changes, please see the file | @@ -7,28 +7,15 @@ For a detailed list of changes, please see the file | ||
| 7 | :file:`ChangeLog` in the source distribution. | 7 | :file:`ChangeLog` in the source distribution. |
| 8 | 8 | ||
| 9 | 10.6.0: XXX | 9 | 10.6.0: XXX |
| 10 | - - Deprecations/future replacement of ``PointerHolder`` | 10 | + - Preparation for replacement of ``PointerHolder`` |
| 11 | 11 | ||
| 12 | The next major release of qpdf will replace ``PointerHolder`` with | 12 | The next major release of qpdf will replace ``PointerHolder`` with |
| 13 | - ``std::shared_ptr`` across all of qpdf's public API. In | ||
| 14 | - preparation for this change, the following ``PointerHolder`` | ||
| 15 | - methods have been deprecated in favor of interfaces that more | ||
| 16 | - closely match ``std::shared_ptr``: | ||
| 17 | - | ||
| 18 | - - ``getPointer()`` -- use ``get()`` instead; this also fixes | ||
| 19 | - ``const`` semantics as discussed in | ||
| 20 | - :file:`include/qpdf/PointerHolder.hh`. | ||
| 21 | - | ||
| 22 | - - ``getRefcount()`` -- use ``use_count()`` instead | ||
| 23 | - | ||
| 24 | - If you build your code with deprecation warnings enabled and you | ||
| 25 | - want to suppress these deprecation warnings for now, you can | ||
| 26 | - ``#define NO_POINTERHOLDER_DEPRECATION`` before including any qpdf | ||
| 27 | - header files. Code that does this will *require no changes* prior | ||
| 28 | - to qpdf 11 and may or may not require changes after qpdf 11. | ||
| 29 | - | ||
| 30 | - For a detailed discussion of this change and how to prepare for | ||
| 31 | - it, see :ref:`smart-pointers`. | 13 | + ``std::shared_ptr`` across all of qpdf's public API. No action is |
| 14 | + required at this time, but if you'd like to prepare, read the | ||
| 15 | + comments :file:`include/qpdf/PointerHolder.hh` and see | ||
| 16 | + :ref:`smart-pointers` for details on what you can do now to create | ||
| 17 | + code that will continue to work with older versions of qpdf and be | ||
| 18 | + easier to switch over to qpdf 11 when it comes out. | ||
| 32 | 19 | ||
| 33 | - Preparation for a new JSON output version | 20 | - Preparation for a new JSON output version |
| 34 | 21 |