1 // LAF Base Library
2 // Copyright (c) 2001-2016 David Capello
3 //
4 // This file is released under the terms of the MIT license.
5 // Read LICENSE.txt for more information.
6 
7 #ifndef BASE_SHARED_PTR_H_INCLUDED
8 #define BASE_SHARED_PTR_H_INCLUDED
9 #pragma once
10 
11 #include "base/debug.h"
12 
13 namespace base {
14 
15 // This class counts references for a SharedPtr.
16 class SharedPtrRefCounterBase {
17 public:
SharedPtrRefCounterBase()18   SharedPtrRefCounterBase() : m_count(0) { }
~SharedPtrRefCounterBase()19   virtual ~SharedPtrRefCounterBase() { }
20 
add_ref()21   void add_ref() {
22     ++m_count;
23   }
24 
release()25   void release() {
26     --m_count;
27     if (m_count == 0)
28       delete this;
29   }
30 
use_count()31   long use_count() const {
32     return m_count;
33   }
34 
35 private:
36   long m_count;         // Number of references.
37 };
38 
39 // Default deleter used by shared pointer (it calls "delete"
40 // operator).
41 template<class T>
42 class DefaultSharedPtrDeleter {
43 public:
operator()44   void operator()(T* ptr) {
45     delete ptr;
46   }
47 };
48 
49 // A reference counter with a custom deleter.
50 template<class T, class Deleter>
51 class SharedPtrRefCounterImpl : public SharedPtrRefCounterBase {
52 public:
SharedPtrRefCounterImpl(T * ptr,Deleter deleter)53   SharedPtrRefCounterImpl(T* ptr, Deleter deleter)
54     : m_ptr(ptr)
55     , m_deleter(deleter) {
56   }
57 
~SharedPtrRefCounterImpl()58   ~SharedPtrRefCounterImpl() {
59     if (m_ptr)
60       m_deleter(m_ptr);
61   }
62 
63 private:
64   T* m_ptr;
65   Deleter m_deleter;            // Used to destroy the pointer.
66 };
67 
68 // Wraps a pointer and keeps reference counting to automatically
69 // delete the pointed object when it is no longer used.
70 template<class T>
71 class SharedPtr {
72 public:
73   typedef T element_type;
74 
SharedPtr()75   SharedPtr()
76     : m_ptr(nullptr)
77     , m_refCount(nullptr)
78   {
79   }
80 
81   // Constructor with default deleter.
SharedPtr(T * ptr)82   explicit SharedPtr(T* ptr)
83   {
84     create_refcount(ptr, DefaultSharedPtrDeleter<T>());
85     m_ptr = ptr;
86     add_ref();
87   }
88 
89   // Constructor with customized deleter.
90   template<class Deleter>
SharedPtr(T * ptr,Deleter deleter)91   SharedPtr(T* ptr, Deleter deleter)
92   {
93     create_refcount(ptr, deleter);
94     m_ptr = ptr;
95     add_ref();
96   }
97 
98   // Copy other pointer
SharedPtr(const SharedPtr<T> & other)99   SharedPtr(const SharedPtr<T>& other)
100     : m_ptr(other.m_ptr)
101     , m_refCount(other.m_refCount)
102   {
103     add_ref();
104   }
105 
106   // Copy other pointer (of static_casteable type)
107   template<class Y>
SharedPtr(const SharedPtr<Y> & other)108   SharedPtr(const SharedPtr<Y>& other)
109     : m_ptr(static_cast<T*>(const_cast<Y*>(other.m_ptr)))
110     , m_refCount(const_cast<SharedPtrRefCounterBase*>(other.m_refCount))
111   {
112     add_ref();
113   }
114 
115   // Releases one reference from the pointee.
~SharedPtr()116   virtual ~SharedPtr()
117   {
118     release();
119   }
120 
121   void reset(T* ptr = nullptr)
122   {
123     if (m_ptr != ptr) {
124       release();
125       m_ptr = nullptr;
126       m_refCount = nullptr;
127 
128       if (ptr) {
129         create_refcount(ptr, DefaultSharedPtrDeleter<T>());
130         m_ptr = ptr;
131         add_ref();
132       }
133     }
134   }
135 
136   template<class Deleter>
reset(T * ptr,Deleter deleter)137   void reset(T* ptr, Deleter deleter)
138   {
139     if (m_ptr != ptr) {
140       release();
141       m_ptr = nullptr;
142       m_refCount = nullptr;
143 
144       if (ptr) {
145         create_refcount(ptr, deleter);
146         m_ptr = ptr;
147         add_ref();
148       }
149     }
150   }
151 
152   SharedPtr& operator=(const SharedPtr<T>& other)
153   {
154     if (m_ptr != other.m_ptr) {
155       release();
156       m_ptr = other.m_ptr;
157       m_refCount = other.m_refCount;
158       add_ref();
159     }
160     return *this;
161   }
162 
163   template<class Y>
164   SharedPtr& operator=(const SharedPtr<Y>& other)
165   {
166     if (m_ptr != static_cast<T*>(other.m_ptr)) {
167       release();
168       m_ptr = static_cast<T*>(const_cast<Y*>(other.m_ptr));
169       m_refCount = const_cast<SharedPtrRefCounterBase*>(other.m_refCount);
170       add_ref();
171     }
172     return *this;
173   }
174 
get()175   T* get() const { return m_ptr; }
176   T& operator*() const { return *m_ptr; }
177   T* operator->() const { return m_ptr; }
178   explicit operator bool() const { return (m_ptr != nullptr); }
179 
use_count()180   long use_count() const { return (m_refCount ? m_refCount->use_count(): 0); }
unique()181   bool unique() const { return use_count() == 1; }
182 
183 private:
184 
185   template<typename Deleter>
create_refcount(T * ptr,Deleter deleter)186   void create_refcount(T* ptr, Deleter deleter) {
187     if (ptr) {
188       try {
189         m_refCount = new SharedPtrRefCounterImpl<T, Deleter>(ptr, deleter);
190       }
191       catch (...) {
192         if (ptr)
193           deleter(ptr);
194         throw;
195       }
196     }
197     else
198       m_refCount = nullptr;
199   }
200 
201   // Adds a reference to the pointee.
add_ref()202   void add_ref()
203   {
204     if (m_refCount)
205       m_refCount->add_ref();
206 
207     ASSERT((m_refCount && m_ptr) || (!m_refCount && !m_ptr));
208   }
209 
210   // Removes the reference to the pointee.
release()211   void release()
212   {
213     if (m_refCount)
214       m_refCount->release();
215 
216     ASSERT((m_refCount && m_ptr) || (!m_refCount && !m_ptr));
217   }
218 
219   T* m_ptr;                            // The pointee object.
220   SharedPtrRefCounterBase* m_refCount; // Number of references.
221 
222   template<class> friend class SharedPtr;
223 };
224 
225 // Compares if two shared-pointers points to the same place (object,
226 // memory address).
227 template<class T>
228 bool operator==(const SharedPtr<T>& ptr1, const SharedPtr<T>& ptr2)
229 {
230   return ptr1.get() == ptr2.get();
231 }
232 
233 // Compares if two shared-pointers points to different places
234 // (objects, memory addresses).
235 template<class T>
236 bool operator!=(const SharedPtr<T>& ptr1, const SharedPtr<T>& ptr2)
237 {
238   return ptr1.get() != ptr2.get();
239 }
240 
241 } // namespace base
242 
243 #endif
244