containers/simple_ptr.hpp

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
#ifndef STLPLUS_SIMPLE_PTR
#define STLPLUS_SIMPLE_PTR
////////////////////////////////////////////////////////////////////////////////
 
//   Author:    Daniel Milton
//   Copyright: (c) Daniel Milton           2002 onwards
//   License:   BSD License, see ../docs/license.html
 
//   A smart pointer is a memory-managing pointer to an object. If you like, it
//   is a zero-dimensional container.
 
//   Assignment of smart pointers result in multiple aliases of the same object.
//   The term alias is used to differentiate from conventional pointers because
//   the semantics are different.
 
//   Aliases can be turned into copies if the pointed-to class supports copying.
 
//   These simple_ptr classes from DJDM have slightly different semantics than
//   the smart_ptr classes of AJR. There are no cross-pointer side effects
//   that occur when the pointer is cleared. The clear() function is effectively
//   equivalent to the clear_unique() function of the smart_ptr. The only way
//   that a "referenced" object will be deleted is if all simple_ptr's that
//   reference the object are cleared (by deletion, manual clearing or reassignment).
 
//   Also, the simple pointer cannot contain a reference to a shared null pointer
//   (which occurs as a side-effect of clearing a multiply referenced object in
//   the smart_ptr classes). Which means that if you have a null simple_ptr, then
//   the assignment of any other null simple_ptr will NOT reassign the reference of
//   any other simple_ptr. Hence, the simple_ptr class acts a little more like a
//   normal pointer (with fewer side effects), with the added bonus of containment.
 
//   Due to the way that the simple_ptr contains the data, it also allows the
//   addition of various casting functions, while still keeping the managed data
//   containment functionality of the underlying object. This means that you can
//   have two simple_ptr's of different template types, both pointing to the same
//   data (if the differing template types are derivatives of each other).
 
//   The base class is simple_ptr_base which defines the common interface. Then
//   there are three subclasses which have the same interface but different copy
//   semantics:
 
//   - simple_ptr        for simple types and classes which have copy constructors
//   - simple_ptr_clone  for polymorphic class hierarchies which are copied using a clone method
//   - simple_ptr_nocopy for any class that cannot or should not be copied
 
////////////////////////////////////////////////////////////////////////////////
#include "containers_fixes.hpp"
#include "exceptions.hpp"
#include "copy_functors.hpp"
#include <map>
#include <string>
 
namespace stlplus
{
 
  ////////////////////////////////////////////////////////////////////////////////
  // Base class
  ////////////////////////////////////////////////////////////////////////////////
 
  template<typename T, typename C>
  class simple_ptr_base
  {
  public:
    //////////////////////////////////////////////////////////////////////////////
    // member type definitions
 
    typedef T value_type;
    typedef T& reference;
    typedef const T& const_reference;
    typedef C value_copy;
 
    //////////////////////////////////////////////////////////////////////////////
    // constructors and destructors
 
    // create a null pointer
    simple_ptr_base(void);
 
    // create a pointer containing a dynamically created object
    // Note: the object must be allocated *by the user* with new
    // constructor form - must be called in the form smart_ptr_base<type> x(new type(args))
    explicit simple_ptr_base(T* data);
 
    // copy constructor implements aliasing so no copy is made
    // note that the copy constructor should NOT be explicit, as this breaks
    // the returning of pointer objects from functions (at least within GCC 4.4)
    simple_ptr_base(const simple_ptr_base<T,C>& r);
 
    // assignment operator - required, else the output of GCC suffers segmentation faults
    simple_ptr_base<T,C>& operator=(const simple_ptr_base<T,C>& r);
 
    // destructor decrements the reference count and delete only when the last reference is destroyed
    ~simple_ptr_base(void);
 
    //////////////////////////////////////////////////////////////////////////////
    // logical tests to see if there is anything contained in the pointer since it can be null
 
    // there are two forms:explicit and implicit
    // implicit: if(!r) or if(r)
    // explicit: if(r.null()) or if(r.present())
    operator bool(void) const;
    bool operator!(void) const;
    bool present(void) const;
    bool null(void) const;
 
    //////////////////////////////////////////////////////////////////////////////
    // dereference operators and functions
 
    // dereference the smart pointer to get the object - use in the form *p1
    // exceptions: null_dereference
    T& operator*(void) ;
    // exceptions: null_dereference
    const T& operator*(void) const ;
 
    // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print()
    // exceptions: null_dereference
    T* operator->(void) ;
    // exceptions: null_dereference
    const T* operator->(void) const ;
 
    //////////////////////////////////////////////////////////////////////////////
    // explicit function forms of the above assignment and dereference operators
 
    // get the value
    // exceptions: null_dereference
    T& value(void) ;
    // exceptions: null_dereference
    const T& value(void) const ;
 
    // set the pointer
    // deletes the previous pointer and adopts the passed pointer instead
    // Note: the object must be allocated *by the user* with new
    // Warning: it is very easy to break the memory management with this operation
    void set(T* data = 0);
    // get the pointer
    T* pointer(void);
    const T* pointer(void) const;
 
    //////////////////////////////////////////////////////////////////////////////
    // functions to manage aliases
 
    // make this an alias of the passed object
    void alias(const simple_ptr_base<T,C>&);
 
    // test whether two pointers point to the same object(known as aliasing the object)
    // used in the form if(a.aliases(b))
    bool aliases(const simple_ptr_base<T,C>&) const;
 
    // find the number of aliases - used when you need to know whether an
    // object is still referred to from elsewhere (rare!)
    unsigned alias_count(void) const;
 
    // clear the reference to the object, but only delete the object if there are no
    // other references to that object. Hence, this does not affect other pointers
    // that are pointing to the same object.
    void clear(void);
 
    // This is just an alias of the clear() function, provided for completeness of
    // the interface when acting as a replacement for the smart_ptr classes
    void clear_unique(void);
 
    //////////////////////////////////////////////////////////////////////////////
    // functions that involve copying
 
    // these functions use the copy functor passed as the template parameter C
    // to copy the object with the right copy semantics. If the copy functor
    // is no_copy, an exception will be thrown.
 
    // create a pointer containing a *copy* of the object using the template parameter C
    // this copy is taken because the pointer class maintains a dynamically allocated object
    // and the T& may not be (usually is not) dynamically allocated
    // exceptions: illegal_copy
    explicit simple_ptr_base(const T& data) ;
 
    // set the value - note that this does a copy using the C template parameter
    // exceptions: illegal_copy
    void set_value(const T& data) ;
 
    // make this pointer unique with respect to any other references to the same object
    // if this pointer is already unique, it does nothing - otherwise it copies the object
    // exceptions: illegal_copy
    void make_unique(void) ;
 
    // make this pointer a unique copy of the parameter
    // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2
    // exceptions: illegal_copy
    void copy(const simple_ptr_base<T,C>&) ;
 
    //////////////////////////////////////////////////////////////////////////////
 
  protected:
    T* m_pointer;
    unsigned* m_count;
 
  public:
    // internal use only - had to make them public because they need to be
    // accessed by routines that could not be made friends
    // can't have a handle due to the way the simple pointer stores it's data
    // in separate counter and pointer objects
    unsigned* _count(void) const;
    T* _pointer(void) const;
    void _make_alias(T* pointer, unsigned* const& count);
 
  private:
    inline void increment(void);
    inline bool decrement(void);
    inline void _delete_pointer(void);
  };
 
  ////////////////////////////////////////////////////////////////////////////////
  // simple_ptr        for simple types and classes which have copy constructors
 
  template <typename T>
  class simple_ptr : public simple_ptr_base<T, constructor_copy<T> >
  {
  public:
    simple_ptr(void) {}
    explicit simple_ptr(const T& data) : simple_ptr_base<T, constructor_copy<T> >(data) {}
    explicit simple_ptr(T* data) : simple_ptr_base<T, constructor_copy<T> >(data) {}
    simple_ptr<T>& operator=(const T& data) {this->set_value(data); return *this;}
    simple_ptr<T>& operator=(T* data) {this->set(data); return *this;}
    ~simple_ptr(void) {}
 
#ifdef STLPLUS_MEMBER_TEMPLATES
    // functions that involve casting
    // moved from base class for two main reasons, though the second is a feature of the first:
 
    // 1. GCC cannot cast the previous base result of simple_ptr_base<T2, constructor_copy<T> >
    //    as a simple_ptr<T2> even though it used to look like a duck and quack like a duck.
    //    I think it was really complaining that the copy class was not guaranteed to be the same.
 
    // 2. Within the cast routines, one pointer type tried accessing private data of the other
    //    pointer type and even though they are really the same type, was not allowed. Because
    //    of this, the "private" function _make_alias is utilised to get the same result.
 
    // By having the cast functions in each derived class, you are guaranteed to use the same
    // copy class - no question. GCC is ok with this.
 
    template<typename T2> simple_ptr<T2> dyn_cast(void) const;
    template<typename T2> simple_ptr<T2> stat_cast(void) const;
    template<typename T2> simple_ptr<T2> cast(void) const;
#endif
  };
 
  ////////////////////////////////////////////////////////////////////////////////
  // simple_ptr_clone  for polymorphic class hierarchies which have a clone method
 
  template <typename T>
  class simple_ptr_clone : public simple_ptr_base<T, clone_copy<T> >
  {
  public:
    simple_ptr_clone(void) {}
    explicit simple_ptr_clone(const T& data) : simple_ptr_base<T, clone_copy<T> >(data) {}
    explicit simple_ptr_clone(T* data) : simple_ptr_base<T, clone_copy<T> >(data) {}
    simple_ptr_clone<T>& operator=(const T& data) {this->set_value(data); return *this;}
    simple_ptr_clone<T>& operator=(T* data) {this->set(data); return *this;}
    ~simple_ptr_clone(void) {}
 
#ifdef STLPLUS_MEMBER_TEMPLATES
    // functions that involve casting
    // moved from base class - see simple_ptr above
    template<typename T2> simple_ptr_clone<T2> dyn_cast(void) const;
    template<typename T2> simple_ptr_clone<T2> stat_cast(void) const;
    template<typename T2> simple_ptr_clone<T2> cast(void) const;
#endif
};
 
  ////////////////////////////////////////////////////////////////////////////////
  // simple_ptr_nocopy for any class that cannot or should not be copied
 
  template <typename T>
  class simple_ptr_nocopy : public simple_ptr_base<T, no_copy<T> >
  {
  public:
    simple_ptr_nocopy(void) {}
    explicit simple_ptr_nocopy(T* data) : simple_ptr_base<T, no_copy<T> >(data) {}
    simple_ptr_nocopy<T>& operator=(T* data) {this->set(data); return *this;}
    ~simple_ptr_nocopy(void) {}
 
#ifdef STLPLUS_MEMBER_TEMPLATES
    // functions that involve casting
    // moved from base class - see simple_ptr above
    template<typename T2> simple_ptr_nocopy<T2> dyn_cast(void) const;
    template<typename T2> simple_ptr_nocopy<T2> stat_cast(void) const;
    template<typename T2> simple_ptr_nocopy<T2> cast(void) const;
#endif
  };
 
  ////////////////////////////////////////////////////////////////////////////////
  // internally, simple_ptr allocates lots of count values, which are just
  // unsigned reference counts. The allocation of these values benefit from
  // being allocated from a dedicated pool, as this increases the speed
  // therefore I have now provided the ability to override the memory management
  // of these values without needing to override the global new/delete
  // all you need to do is to define these functions BEFORE including this file
  // and set the override definition
#ifndef STLPLUS_SIMPLE_PTR_REFCOUNT_ALLOC_OVERRIDE
  inline unsigned* simple_ptr_refcount_new() {
      return new unsigned(1);
  }
  inline void simple_ptr_refcount_delete(unsigned* pCount) {
      delete pCount;
  }
#endif
 
  ////////////////////////////////////////////////////////////////////////////////
 
} // end namespace stlplus
 
#include "simple_ptr.tpp"
#endif