[419] | 1 | /*! |
---|
| 2 | \file |
---|
[529] | 3 | \brief BDM's own smart pointers. |
---|
[419] | 4 | \author Vaclav Barta. |
---|
| 5 | |
---|
| 6 | ----------------------------------- |
---|
| 7 | BDM++ - C++ library for Bayesian Decision Making under Uncertainty |
---|
| 8 | |
---|
| 9 | Using IT++ for numerical operations |
---|
| 10 | ----------------------------------- |
---|
| 11 | */ |
---|
| 12 | |
---|
| 13 | #ifndef shared_ptr_h |
---|
| 14 | #define shared_ptr_h |
---|
| 15 | |
---|
| 16 | #include <limits.h> |
---|
| 17 | #include <algorithm> |
---|
| 18 | #include <stdexcept> |
---|
| 19 | #include <string> |
---|
[565] | 20 | #include "bdmerror.h" |
---|
[419] | 21 | |
---|
| 22 | namespace bdm { |
---|
| 23 | |
---|
[555] | 24 | /*! \brief A naive implementation of roughly a subset of the std::tr1::shared_ptr spec |
---|
[529] | 25 | |
---|
| 26 | Really just roughly - it ignores memory |
---|
| 27 | exceptions, for example; also note I didn't read the spec. |
---|
| 28 | |
---|
[555] | 29 | The standard template would naturally be preferable, \b if it was |
---|
[529] | 30 | included in the standard libraries of all supported compilers - but |
---|
| 31 | as of 2009, that's still a problem... |
---|
| 32 | */ |
---|
[419] | 33 | template <typename T> |
---|
| 34 | class shared_ptr { |
---|
[1064] | 35 | template<class U> friend class shared_ptr; |
---|
[461] | 36 | |
---|
[419] | 37 | private: |
---|
[1064] | 38 | T *payload; |
---|
| 39 | unsigned *refCnt; |
---|
[419] | 40 | |
---|
| 41 | public: |
---|
[1064] | 42 | /*! |
---|
| 43 | \brief Default constructor |
---|
[555] | 44 | |
---|
[1064] | 45 | Creates an empty shared_ptr - one that doesn't point anywhere. |
---|
| 46 | */ |
---|
| 47 | shared_ptr() : |
---|
| 48 | payload ( 0 ), |
---|
| 49 | refCnt ( 0 ) { |
---|
| 50 | } |
---|
[419] | 51 | |
---|
[1064] | 52 | /*! |
---|
| 53 | Constructs a shared_ptr that owns the pointer p (unless p |
---|
| 54 | is NULL, in which case this constructor creates an empty |
---|
| 55 | shared_ptr). When p isn't null, it must have been alllocated |
---|
| 56 | by new! |
---|
| 57 | */ |
---|
| 58 | shared_ptr ( T *p ) : |
---|
| 59 | payload ( p ), |
---|
| 60 | refCnt ( p ? new unsigned ( 1 ) : 0 ) { |
---|
| 61 | } |
---|
[419] | 62 | |
---|
[1064] | 63 | /*! |
---|
| 64 | \brief Copy constructor |
---|
[555] | 65 | |
---|
[1064] | 66 | If other is empty, constructs an empty shared_ptr; otherwise, |
---|
| 67 | constructs a shared_ptr that shares ownership with other. |
---|
| 68 | */ |
---|
| 69 | shared_ptr ( const shared_ptr<T> &other ) : |
---|
| 70 | payload ( other.payload ), |
---|
| 71 | refCnt ( other.refCnt ) { |
---|
| 72 | add_ref(); |
---|
| 73 | } |
---|
[419] | 74 | |
---|
[1064] | 75 | /*! |
---|
| 76 | \brief Generalized copy |
---|
[555] | 77 | |
---|
[1064] | 78 | Allows initialization of shared pointer of a base type from |
---|
| 79 | raw pointer to a derived type. |
---|
[555] | 80 | |
---|
[1064] | 81 | If other is empty, constructs an empty shared_ptr; otherwise, |
---|
| 82 | constructs a shared_ptr that shares ownership with other. |
---|
| 83 | */ |
---|
| 84 | template<typename U> |
---|
| 85 | shared_ptr ( const shared_ptr<U> &other ) : |
---|
| 86 | payload ( other.payload ), |
---|
| 87 | refCnt ( other.refCnt ) { |
---|
| 88 | add_ref(); |
---|
| 89 | } |
---|
[419] | 90 | |
---|
[1064] | 91 | /*! |
---|
| 92 | Destructor. |
---|
| 93 | */ |
---|
| 94 | ~shared_ptr() { |
---|
| 95 | del_ref(); |
---|
| 96 | } |
---|
[419] | 97 | |
---|
[1064] | 98 | /*! |
---|
| 99 | \brief Assignment operator |
---|
| 100 | */ |
---|
| 101 | shared_ptr<T> &operator= ( const shared_ptr<T> &other ) { |
---|
| 102 | other.add_ref(); |
---|
| 103 | del_ref(); |
---|
[419] | 104 | |
---|
[1064] | 105 | payload = other.payload; |
---|
| 106 | refCnt = other.refCnt; |
---|
[419] | 107 | |
---|
[1064] | 108 | return *this; |
---|
| 109 | } |
---|
[419] | 110 | |
---|
[1064] | 111 | /*! |
---|
| 112 | Returns the stored pointer (which remains owned by this |
---|
| 113 | instance). For empty instances, this method returns NULL. |
---|
| 114 | */ |
---|
| 115 | T *get() { |
---|
| 116 | return payload; |
---|
| 117 | } |
---|
[419] | 118 | |
---|
[1064] | 119 | /*! |
---|
| 120 | Dereferences the stored pointer (which remains owned by |
---|
| 121 | this instance). This method may only be called when the |
---|
| 122 | stored pointer isn't NULL. |
---|
| 123 | */ |
---|
| 124 | T *operator->() { |
---|
| 125 | bdm_assert_debug ( payload, "dereferencing NULL shared pointer" ); |
---|
| 126 | return payload; |
---|
| 127 | } |
---|
[419] | 128 | |
---|
[1064] | 129 | //! Returns a reference to the object pointed to by the stored |
---|
| 130 | //! pointer. This method may only be called when the stored pointer |
---|
| 131 | //! isn't NULL. |
---|
| 132 | T &operator*() { |
---|
| 133 | bdm_assert_debug ( payload, "dereferencing NULL shared pointer" ); |
---|
| 134 | return *payload; |
---|
| 135 | } |
---|
[419] | 136 | |
---|
[1064] | 137 | /*! |
---|
| 138 | Returns the stored pointer (which remains owned by this |
---|
| 139 | instance). For empty instances, this method returns NULL. |
---|
| 140 | */ |
---|
| 141 | const T* get() const { |
---|
| 142 | return payload; |
---|
| 143 | } |
---|
[419] | 144 | |
---|
[1064] | 145 | //! Returns the stored pointer (which remains owned by this |
---|
| 146 | //! instance). This method may only be called when the stored |
---|
| 147 | //! pointer isn't NULL. |
---|
| 148 | const T *operator->() const { |
---|
| 149 | bdm_assert_debug ( payload, "dereferencing NULL shared pointer" ); |
---|
| 150 | return payload; |
---|
| 151 | } |
---|
[419] | 152 | |
---|
[1064] | 153 | //! Returns a reference to the object pointed to by the stored |
---|
| 154 | //! pointer. This method may only be called when the stored pointer |
---|
| 155 | //! isn't NULL. |
---|
| 156 | const T &operator*() const { |
---|
| 157 | bdm_assert_debug ( payload, "dereferencing NULL shared pointer" ); |
---|
| 158 | return *payload; |
---|
| 159 | } |
---|
[419] | 160 | |
---|
[1064] | 161 | //! Returns use_count() == 1 |
---|
| 162 | bool unique() const { |
---|
| 163 | return refCnt && ( *refCnt == 1 ); |
---|
| 164 | } |
---|
[419] | 165 | |
---|
[1064] | 166 | /*! |
---|
| 167 | Returns the number of shared_ptr instances (including |
---|
| 168 | this instance) that share ownership with this instance. For |
---|
| 169 | empty instances, this method returns 0. |
---|
| 170 | */ |
---|
| 171 | long use_count() const { |
---|
| 172 | return refCnt ? *refCnt : 0; |
---|
| 173 | } |
---|
[419] | 174 | |
---|
[1064] | 175 | /*! |
---|
| 176 | \brief Boolean cast |
---|
[555] | 177 | |
---|
[1064] | 178 | This operator returns true if and only if the instance isn't empty. |
---|
| 179 | */ |
---|
| 180 | operator bool() const { |
---|
| 181 | return !!payload; |
---|
| 182 | } |
---|
[419] | 183 | |
---|
[1064] | 184 | /*! |
---|
| 185 | \brief const cast |
---|
[570] | 186 | |
---|
[1064] | 187 | Shared pointer to T can be converted to shared pointer to |
---|
| 188 | const T, just like T * can be converted to T const *. |
---|
| 189 | */ |
---|
| 190 | template<typename U> |
---|
| 191 | operator shared_ptr<const U>() const { |
---|
| 192 | shared_ptr<const U> cptr; |
---|
| 193 | cptr.refCnt = refCnt; |
---|
| 194 | cptr.payload = payload; |
---|
| 195 | add_ref(); |
---|
| 196 | return cptr; |
---|
| 197 | } |
---|
[570] | 198 | |
---|
[1064] | 199 | /*! |
---|
| 200 | \brief Efficient swap for shared_ptr. |
---|
| 201 | */ |
---|
| 202 | void swap ( shared_ptr &other ) { |
---|
| 203 | std::swap ( payload, other.payload ); |
---|
| 204 | std::swap ( refCnt, other.refCnt ); |
---|
| 205 | } |
---|
[477] | 206 | |
---|
[419] | 207 | private: |
---|
[1064] | 208 | void add_ref() const { |
---|
| 209 | if ( refCnt ) { |
---|
| 210 | if ( *refCnt == UINT_MAX ) { |
---|
| 211 | throw std::overflow_error ( |
---|
| 212 | std::string ( "Shared pointer has too many references." ) ); |
---|
| 213 | } |
---|
[419] | 214 | |
---|
[1064] | 215 | ++*refCnt; |
---|
| 216 | } |
---|
| 217 | } |
---|
[419] | 218 | |
---|
[1064] | 219 | void del_ref() { |
---|
| 220 | if ( refCnt ) { |
---|
| 221 | if ( ! ( --*refCnt ) ) { |
---|
| 222 | delete payload; |
---|
| 223 | delete refCnt; |
---|
| 224 | } |
---|
| 225 | } |
---|
| 226 | } |
---|
[419] | 227 | }; |
---|
| 228 | |
---|
[737] | 229 | //! Compare shared pointers |
---|
[419] | 230 | template<typename T, typename U> |
---|
[477] | 231 | bool operator== ( shared_ptr<T> const &a, shared_ptr<U> const &b ) { |
---|
[1064] | 232 | return a.get() == b.get(); |
---|
[419] | 233 | } |
---|
| 234 | |
---|
[737] | 235 | //! Compare shared pointers |
---|
[419] | 236 | template<typename T, typename U> |
---|
[477] | 237 | bool operator!= ( shared_ptr<T> const &a, shared_ptr<U> const &b ) { |
---|
[1064] | 238 | return a.get() != b.get(); |
---|
[419] | 239 | } |
---|
| 240 | |
---|
[737] | 241 | //! Compare shared pointers |
---|
[419] | 242 | template<typename T, typename U> |
---|
[477] | 243 | bool operator< ( shared_ptr<T> const &a, shared_ptr<U> const &b ) { |
---|
[1064] | 244 | return a.get() < b.get(); |
---|
[419] | 245 | } |
---|
| 246 | |
---|
[529] | 247 | /*! \brief A wrapper of shared_ptr which is never empty. |
---|
| 248 | |
---|
| 249 | T must have a default constructor. |
---|
| 250 | |
---|
| 251 | Note that shared_ptr's destructor isn't virtual - don't call delete |
---|
| 252 | on pointers to instances of this class. |
---|
| 253 | */ |
---|
| 254 | template <typename T> |
---|
[737] | 255 | class object_ptr : public shared_ptr<T> { |
---|
[529] | 256 | public: |
---|
[1064] | 257 | /*! |
---|
| 258 | \brief Default constructor |
---|
[529] | 259 | |
---|
[1064] | 260 | Calls T's default constructor. |
---|
| 261 | */ |
---|
| 262 | object_ptr() : shared_ptr<T> ( new T() ) { } |
---|
[529] | 263 | |
---|
[1064] | 264 | /*! |
---|
| 265 | \brief Upcast from shared_ptr<T> to object_ptr<T> |
---|
[529] | 266 | |
---|
[1064] | 267 | \param b The shared pointer, which must not be empty. |
---|
| 268 | */ |
---|
| 269 | object_ptr ( const shared_ptr<T> &b ) : shared_ptr<T> ( b ) { |
---|
| 270 | bdm_assert_debug ( this->get(), "object_ptr cannot be empty" ); |
---|
| 271 | } |
---|
[529] | 272 | |
---|
[1064] | 273 | /*! |
---|
| 274 | Constructs an object_ptr that owns the pointer p. p must |
---|
| 275 | have been alllocated by new! |
---|
| 276 | */ |
---|
| 277 | object_ptr ( T *p ) : shared_ptr<T> ( p ) { |
---|
| 278 | bdm_assert_debug ( p, "object_ptr cannot be empty" ); |
---|
| 279 | } |
---|
[529] | 280 | |
---|
[1064] | 281 | /*! |
---|
| 282 | \brief Assignment operator |
---|
| 283 | */ |
---|
| 284 | object_ptr<T> &operator= ( const object_ptr<T> &other ) { |
---|
| 285 | shared_ptr<T>::operator= ( other ); |
---|
| 286 | return *this; |
---|
| 287 | } |
---|
[529] | 288 | }; |
---|
| 289 | |
---|
| 290 | #define SHAREDPTR(class_name) typedef bdm::object_ptr< class_name > class_name##_ptr |
---|
| 291 | |
---|
| 292 | #define SHAREDPTR2(template_class_name, template_parameter_name) typedef bdm::object_ptr< template_class_name < template_parameter_name > > template_class_name##_##template_parameter_name##_ptr |
---|
| 293 | |
---|
[419] | 294 | } |
---|
| 295 | |
---|
| 296 | #endif |
---|