aboutsummaryrefslogtreecommitdiffstats
path: root/include/qpdf/PointerHolder.hh
blob: 4523645679c29f8e462c4da5ecee49c929e83948 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
// Copyright (c) 2005-2022 Jay Berkenbilt
//
// This file is part of qpdf.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Versions of qpdf prior to version 7 were released under the terms
// of version 2.0 of the Artistic License. At your option, you may
// continue to consider qpdf to be licensed under those terms. Please
// see the manual for additional information.

#ifndef POINTERHOLDER_HH
#define POINTERHOLDER_HH

#ifndef POINTERHOLDER_TRANSITION

// In qpdf 11, #define POINTERHOLDER_IS_SHARED_POINTER

// In qpdf 11, issue a warning:
// #define POINTERHOLDER_TRANSITION 0 to suppress this warning, and see below.
// # warn "POINTERHOLDER_TRANSITION is not defined -- see qpdf/PointerHolder.hh"

// undefined = define as 0; will also issue a warning in qpdf 11
// 0 = no deprecation warnings
// 1 = make PointerHolder<T>(T*) explicit
// 2 = warn for use of getPointer() and getRefcount()
// 3 = warn for all use of PointerHolder
// 4 = don't define PointerHolder at all
# define POINTERHOLDER_TRANSITION 0
#endif // !defined(POINTERHOLDER_TRANSITION)

// *** WHAT IS HAPPENING ***

// In qpdf 11, PointerHolder will be replaced with std::shared_ptr
// wherever it appears in the qpdf API. The PointerHolder object will
// be derived from std::shared_ptr to provide a backward-compatible
// interface and will be mutually assignable with std::shared_ptr.
// Code that uses containers of PointerHolder will require adjustment.

// *** HOW TO TRANSITION ***

// The symbol POINTERHOLDER_TRANSITION can be defined to help you
// transition your code away from PointerHolder. You can define it
// before including any qpdf header files or including its definition
// in your build configuration. If not defined, it automatically gets
// defined to 0, which enables full backward compatibility. That way,
// you don't have to take action for your code to continue to work.

// If you want to work gradually to transition your code away from
// PointerHolder, you can define POINTERHOLDER_TRANSITION and fix the
// code so it compiles without warnings and works correctly. If you
// want to be able to continue to support old qpdf versions at the
// same time, you can write code like this:

// #ifndef POINTERHOLDER_TRANSITION
//  ... use PointerHolder as before 10.6
// #else
//  ... use PointerHolder or shared_ptr as needed
// #endif

// Each level of POINTERHOLDER_TRANSITION exposes differences between
// PointerHolder and std::shared_ptr. The easiest way to transition is
// to increase POINTERHOLDER_TRANSITION in steps of 1 so that you can
// test and handle changes incrementally.

// *** Transitions available starting at qpdf 10.6.0 ***

// POINTERHOLDER_TRANSITION = 1
//
// PointerHolder<T> has an implicit constructor that takes a T*, so
// you can replace a PointerHolder<T>'s pointer by directly assigning
// a T* to it or pass a T* to a function that expects a
// PointerHolder<T>. std::shared_ptr does not have this (risky)
// behavior. When POINTERHOLDER_TRANSITION = 1, PointerHolder<T>'s T*
// constructor is declared explicit. For compatibility with
// std::shared_ptr, you can still assign nullptr to a PointerHolder.
// Constructing all your PointerHolder<T> instances explicitly is
// backward compatible, so you can make this change without
// conditional compilation and still use the changes with older qpdf
// versions.
//
// Also defined is a make_pointer_holder method that acts like
// std::make_shared. You can use this as well, but it is not
// compatible with qpdf prior to 10.6. Like std::make_shared<T>,
// make_pointer_holder<T> can only be used when the constructor
// implied by its arguments is public. If you use this, you should be
// able to just replace it with std::make_shared when qpdf 11 is out.

// POINTERHOLDER_TRANSITION = 2
//
// std::shared_ptr as get() and use_count(). PointerHolder has
// getPointer() and getRefcount(). In 10.6.0, get() and use_count()
// were added as well. When POINTERHOLDER_TRANSITION = 2, getPointer()
// and getRefcount() are deprecated. Fix deprecation warnings by
// replacing with get() and use_count(). This breaks compatibility
// with qpdf older than 10.6. Search for CONST BEHAVIOR for an
// additional note.
//
// Once your code is clean at POINTERHOLDER_TRANSITION = 2, the only
// remaining issues that prevent simple replacement of PointerHolder
// with std::shared_ptr are shared arrays and containers, and neither
// of these are used in the qpdf API.

// *** Transitions available starting at qpdf 11.0.0 **

// NOTE: Until qpdf 11 is released, this is a plan and is subject to
// change. Be sure to check again after qpdf 11 is released.

// POINTERHOLDER_TRANSITION = 3
//
// Warn for all use of PointerHolder<T>. This helps you remove all use
// of PointerHolder from your code and use std::shared_ptr instead.
// You will also have to transition any containers of PointerHolder in
// your code.

// POINTERHOLDER_TRANSITION = 4
//
// Suppress definition of the PointerHolder<T> type entirely.

// CONST BEHAVIOR

// PointerHolder<T> has had a long-standing bug in its const behavior.
// const PointerHolder<T>'s getPointer() method returns a T const*.
// This is incorrect and is not how regular pointers or standard
// library smart pointers behave. Making a PointerHolder<T> const
// should prevent reassignment of its pointer but not affect the thing
// it points to. For that, use PointerHolder<T const>. The new get()
// method behaves correctly in this respect and is therefore slightly
// different from getPointer(). This shouldn't break any correctly
// written code. If you are relying on the incorrect behavior, use
// PointerHolder<T const> instead.

// OLD DOCUMENTATION

// This class is basically std::shared_ptr but predates that by
// several years.

// This class expects to be initialized with a dynamically allocated
// object pointer.  It keeps a reference count and deletes this once
// the reference count goes to zero.  PointerHolder objects are
// explicitly safe for use in STL containers.

// It is very important that a client who pulls the pointer out of
// this holder does not let the holder go out of scope until it is
// finished with the pointer.  It is also important that exactly one
// instance of this object ever gets initialized with a given pointer.
// Otherwise, the pointer will be deleted twice, and before that, some
// objects will be left with a pointer to a deleted object.  In other
// words, the only legitimate way for two PointerHolder objects to
// contain the same pointer is for one to be a copy of the other.
// Copy and assignment semantics are well-defined and essentially
// allow you to use PointerHolder as a means to get pass-by-reference
// semantics in a pass-by-value environment without having to worry
// about memory management details.

// Comparison (== and <) are defined and operate on the internally
// stored pointers, not on the data.  This makes it possible to store
// PointerHolder objects in sorted lists or to find them in STL
// containers just as one would be able to store pointers.  Comparing
// the underlying pointers provides a well-defined, if not
// particularly meaningful, ordering.

#include <cstddef>

template <class T>
class PointerHolder
{
  private:
    class Data
    {
      public:
        Data(T* pointer, bool array) :
            pointer(pointer),
            array(array),
            refcount(0)
        {
        }
        ~Data()
        {
            if (array)
            {
                delete [] this->pointer;
            }
            else
            {
                delete this->pointer;
            }
        }
        T* pointer;
        bool array;
        int refcount;
      private:
        Data(Data const&) = delete;
        Data& operator=(Data const&) = delete;
    };

  public:
#if POINTERHOLDER_TRANSITION >= 1
    explicit
#endif // POINTERHOLDER_TRANSITION >= 1
    PointerHolder(T* pointer = 0)
    {
        this->init(new Data(pointer, false));
    }
    // Special constructor indicating to free memory with delete []
    // instead of delete
    PointerHolder(bool, T* pointer)
    {
        this->init(new Data(pointer, true));
    }
    PointerHolder(PointerHolder const& rhs)
    {
        this->copy(rhs);
    }
    PointerHolder& operator=(PointerHolder const& rhs)
    {
        if (this != &rhs)
        {
            this->destroy();
            this->copy(rhs);
        }
        return *this;
    }
    PointerHolder& operator=(decltype(nullptr))
    {
        this->operator=(PointerHolder<T>());
        return *this;
    }
    ~PointerHolder()
    {
        this->destroy();
    }
    bool operator==(PointerHolder const& rhs) const
    {
        return this->data->pointer == rhs.data->pointer;
    }
    bool operator==(decltype(nullptr)) const
    {
        return this->data->pointer == nullptr;
    }
    bool operator<(PointerHolder const& rhs) const
    {
        return this->data->pointer < rhs.data->pointer;
    }

    // get() is for interface compatibility with std::shared_ptr
    T* get() const
    {
        return this->data->pointer;
    }

    // NOTE: The pointer returned by getPointer turns into a pumpkin
    // when the last PointerHolder that contains it disappears.
#if POINTERHOLDER_TRANSITION >= 2
    [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]]
#endif // POINTERHOLDER_TRANSITION >= 2
    T* getPointer()
    {
        return this->data->pointer;
    }
#if POINTERHOLDER_TRANSITION >= 2
    [[deprecated("use PointerHolder<T>::get() instead of getPointer()")]]
#endif // POINTERHOLDER_TRANSITION >= 2
    T const* getPointer() const
    {
        return this->data->pointer;
    }
#if POINTERHOLDER_TRANSITION >= 2
    [[deprecated("use use_count() instead of getRefcount()")]]
#endif // POINTERHOLDER_TRANSITION >= 2
    int getRefcount() const
    {
        return this->data->refcount;
    }

    // use_count() is for compatibility with std::shared_ptr
    long use_count()
    {
        return static_cast<long>(this->data->refcount);
    }

    T const& operator*() const
    {
        return *this->data->pointer;
    }
    T& operator*()
    {
        return *this->data->pointer;
    }

    T const* operator->() const
    {
        return this->data->pointer;
    }
    T* operator->()
    {
        return this->data->pointer;
    }

  private:
    void init(Data* data)
    {
        this->data = data;
        ++this->data->refcount;
    }
    void copy(PointerHolder const& rhs)
    {
        this->init(rhs.data);
    }
    void destroy()
    {
        bool gone = false;
        {
            if (--this->data->refcount == 0)
            {
                gone = true;
            }
        }
        if (gone)
        {
            delete this->data;
        }
    }

    Data* data;
};

template<typename T, typename... _Args>
inline PointerHolder<T>
make_pointer_holder(_Args&&... __args)
{
    return PointerHolder<T>(new T(__args...));
}

template <typename T>
PointerHolder<T>
make_array_pointer_holder(size_t n)
{
    return PointerHolder<T>(true, new T[n]);
}

#endif // POINTERHOLDER_HH