1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef API_BASE_CONTAINERS_SHARED_PTR_H
17 #define API_BASE_CONTAINERS_SHARED_PTR_H
18
19 #include <base/containers/shared_ptr_internals.h>
20 #include <base/containers/type_traits.h>
21 #include <base/containers/unique_ptr.h>
22 #include <base/namespace.h>
23
24 CORE_BEGIN_NAMESPACE()
25 class IInterface;
26 CORE_END_NAMESPACE()
27
28 BASE_BEGIN_NAMESPACE()
29 template<typename T>
30 class shared_ptr;
31 template<typename T>
32 class weak_ptr;
33
34 /**
35 * @brief C++ standard like weak_ptr.
36 */
37 template<typename T>
38 class weak_ptr final : public Internals::PtrCountedBase<T> {
39 public:
40 using element_type = BASE_NS::remove_extent_t<T>;
41 constexpr weak_ptr() noexcept = default;
~weak_ptr()42 ~weak_ptr()
43 {
44 if (this->control_) {
45 this->control_->ReleaseWeak();
46 }
47 }
48
weak_ptr(nullptr_t)49 weak_ptr(nullptr_t) {}
weak_ptr(const shared_ptr<T> & p)50 weak_ptr(const shared_ptr<T>& p) : Internals::PtrCountedBase<T>(p)
51 {
52 if (this->control_) {
53 this->control_->AddWeak();
54 }
55 }
weak_ptr(const weak_ptr & p)56 weak_ptr(const weak_ptr& p) noexcept : Internals::PtrCountedBase<T>(p)
57 {
58 if (this->control_) {
59 this->control_->AddWeak();
60 }
61 }
weak_ptr(weak_ptr && p)62 weak_ptr(weak_ptr&& p) noexcept : Internals::PtrCountedBase<T>(p)
63 {
64 p.InternalReset();
65 }
66
67 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
lock()68 shared_ptr<T> lock() const
69 {
70 return shared_ptr<T>(*this);
71 }
72 weak_ptr& operator=(weak_ptr&& p) noexcept
73 {
74 if (this != &p) {
75 reset();
76 this->control_ = p.control_;
77 this->pointer_ = p.pointer_;
78 p.InternalReset();
79 }
80 return *this;
81 }
82 weak_ptr& operator=(const weak_ptr& p) noexcept
83 {
84 if (this != &p) {
85 reset();
86 this->control_ = p.control_;
87 this->pointer_ = p.pointer_;
88 if (this->control_) {
89 this->control_->AddWeak();
90 }
91 }
92 return *this;
93 }
94 weak_ptr& operator=(const shared_ptr<T>& p)
95 {
96 reset();
97 this->control_ = p.control_;
98 this->pointer_ = p.pointer_;
99 if (this->control_) {
100 this->control_->AddWeak();
101 }
102 return *this;
103 }
104
105 weak_ptr& operator=(nullptr_t) noexcept
106 {
107 reset();
108 return *this;
109 }
110 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
reset()111 void reset()
112 {
113 if (this->control_) {
114 this->control_->ReleaseWeak();
115 this->InternalReset();
116 }
117 }
118
119 /*"implicit" casting constructors */
120 template<class U, class = Internals::EnableIfPointerConvertible<U, T>>
weak_ptr(const shared_ptr<U> & p)121 weak_ptr(const shared_ptr<U>& p)
122 // handle casting by using functionality in shared_ptr. (creates an aliased shared_ptr to original.)
123 : weak_ptr(shared_ptr<T>(p))
124 {}
125
126 template<class U, class = Internals::EnableIfPointerConvertible<U, T>>
weak_ptr(const weak_ptr<U> & p)127 weak_ptr(const weak_ptr<U>& p) : weak_ptr(shared_ptr<T>(p.lock()))
128 {}
129
130 /* "implicit" casting move */
131 template<class U, class = Internals::EnableIfPointerConvertible<U, T>>
weak_ptr(weak_ptr<U> && p)132 weak_ptr(weak_ptr<U>&& p) noexcept : weak_ptr(shared_ptr<T>(p.lock()))
133 {
134 p.reset();
135 }
136
137 /* "implicit" casting operators */
138 template<class U, class = Internals::EnableIfPointerConvertible<U, T>>
139 weak_ptr& operator=(const shared_ptr<U>& p)
140 {
141 // handle casting by using functionality in shared_ptr. (creates an aliased shared_ptr to original.)
142 *this = shared_ptr<T>(p);
143 return *this;
144 }
145
146 template<class U, class = Internals::EnableIfPointerConvertible<U, T>>
147 weak_ptr& operator=(const weak_ptr<U>& p)
148 {
149 // first lock the given weak ptr. (to see if it has expired, and to get a pointer that can be cast)
150 *this = shared_ptr<T>(p.lock());
151 return *this;
152 }
153
154 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
expired()155 bool expired() const noexcept
156 {
157 return this->use_count() == 0;
158 }
159
160 private:
161 friend class shared_ptr<T>;
162 template<typename>
163 friend class weak_ptr;
164 };
165
166 /**
167 * @brief C++ standard like shared_ptr with IInterface support for reference counting.
168 */
169 template<typename T>
170 class shared_ptr final : public Internals::PtrCountedBase<T> {
171 public:
172 using element_type = BASE_NS::remove_extent_t<T>;
173 using weak_type = weak_ptr<T>;
174
175 constexpr shared_ptr() noexcept = default;
shared_ptr(nullptr_t)176 constexpr shared_ptr(nullptr_t) noexcept {}
shared_ptr(const shared_ptr & p)177 shared_ptr(const shared_ptr& p) noexcept : Internals::PtrCountedBase<T>(p)
178 {
179 if (this->control_) {
180 this->control_->AddStrongCopy();
181 }
182 }
shared_ptr(shared_ptr && p)183 shared_ptr(shared_ptr&& p) noexcept : Internals::PtrCountedBase<T>(p)
184 {
185 p.InternalReset();
186 }
shared_ptr(T * ptr)187 explicit shared_ptr(T* ptr)
188 {
189 if (ptr) {
190 ConstructBlock(ptr);
191 }
192 }
193
194 template<typename Deleter>
shared_ptr(T * ptr,Deleter deleter)195 shared_ptr(T* ptr, Deleter deleter)
196 {
197 if (ptr) {
198 ConstructBlock(ptr, BASE_NS::move(deleter));
199 }
200 }
201
shared_ptr(const weak_type & p)202 explicit shared_ptr(const weak_type& p) noexcept : Internals::PtrCountedBase<T>(p)
203 {
204 if (this->control_) {
205 if (!this->control_->AddStrongLock()) {
206 this->InternalReset();
207 }
208 }
209 }
210 template<class Y>
shared_ptr(const shared_ptr<Y> & r,T * ptr)211 shared_ptr(const shared_ptr<Y>& r, T* ptr) noexcept : Internals::PtrCountedBase<T>(r.control_)
212 {
213 if (this->control_ && ptr) {
214 this->control_->AddStrongCopy();
215 this->pointer_ = const_cast<deletableType*>(ptr);
216 } else {
217 this->InternalReset();
218 }
219 }
220 template<class U, class = Internals::EnableIfPointerConvertible<U, T>>
shared_ptr(shared_ptr<U> && p)221 shared_ptr(shared_ptr<U>&& p) noexcept : Internals::PtrCountedBase<T>(p.control_)
222 {
223 if (this->control_) {
224 void* ptr = nullptr;
225 if constexpr (BASE_NS::is_same_v<T, const BASE_NS::remove_const_t<U>> ||
226 !BASE_NS::is_base_of_v<CORE_NS::IInterface, T>) {
227 ptr = p.get();
228 } else {
229 // make a proper interface cast here.
230 if constexpr (BASE_NS::is_const_v<T>) {
231 ptr = const_cast<void*>(static_cast<const void*>(p->GetInterface(T::UID)));
232 } else {
233 ptr = static_cast<void*>(p->GetInterface(T::UID));
234 }
235 }
236 if (ptr) {
237 this->pointer_ = static_cast<deletableType*>(ptr);
238 p.InternalReset();
239 } else {
240 this->InternalReset();
241 p.reset();
242 }
243 }
244 }
245 template<class U, class = Internals::EnableIfPointerConvertible<U, T>>
shared_ptr(const shared_ptr<U> & p)246 shared_ptr(const shared_ptr<U>& p) noexcept : shared_ptr(shared_ptr<U>(p)) // use the above move constructor
247 {}
248
249 template<class U, class D, class = Internals::EnableIfPointerConvertible<U, T>>
shared_ptr(unique_ptr<U,D> && p)250 shared_ptr(unique_ptr<U, D>&& p) noexcept
251 {
252 if (p) {
253 ConstructBlock(p.release(), BASE_NS::move(p.get_deleter()));
254 }
255 }
256
~shared_ptr()257 ~shared_ptr()
258 {
259 if (this->control_) {
260 this->control_->Release();
261 }
262 }
263 T* operator->() const noexcept
264 {
265 return get();
266 }
267 T& operator*() const noexcept
268 {
269 return *get();
270 }
271 explicit operator bool() const noexcept
272 {
273 return get();
274 }
275 bool operator==(const shared_ptr& other) const noexcept
276 {
277 return get() == other.get();
278 }
279 bool operator!=(const shared_ptr& other) const noexcept
280 {
281 return !(*this == other);
282 }
283 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
reset()284 void reset()
285 {
286 if (this->control_) {
287 this->control_->Release();
288 this->InternalReset();
289 }
290 }
291 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
reset(T * ptr)292 void reset(T* ptr)
293 {
294 if (ptr != this->pointer_) {
295 reset();
296 if (ptr) {
297 ConstructBlock(ptr);
298 }
299 }
300 }
301 template<typename Deleter>
302 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
reset(T * ptr,Deleter deleter)303 void reset(T* ptr, Deleter deleter)
304 {
305 if (ptr != this->pointer_) {
306 reset();
307 if (ptr) {
308 ConstructBlock(ptr, BASE_NS::move(deleter));
309 }
310 }
311 }
312
313 shared_ptr& operator=(nullptr_t) noexcept
314 {
315 reset();
316 return *this;
317 }
318 shared_ptr& operator=(const shared_ptr& o) noexcept
319 {
320 if (this != &o) {
321 reset();
322 this->control_ = o.control_;
323 this->pointer_ = o.pointer_;
324 if (this->control_) {
325 this->control_->AddStrongCopy();
326 }
327 }
328 return *this;
329 }
330 shared_ptr& operator=(shared_ptr&& o) noexcept
331 {
332 if (this != &o) {
333 reset();
334 this->control_ = o.control_;
335 this->pointer_ = o.pointer_;
336 o.InternalReset();
337 }
338 return *this;
339 }
340 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
swap(shared_ptr & p)341 void swap(shared_ptr& p) noexcept
342 {
343 auto tp = p.pointer_;
344 auto tc = p.control_;
345 p.pointer_ = this->pointer_;
346 p.control_ = this->control_;
347 this->pointer_ = tp;
348 this->control_ = tc;
349 }
350 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
get()351 element_type* get() const noexcept
352 {
353 return this->pointer_;
354 }
355
356 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
weak_count()357 int32_t weak_count() const noexcept
358 {
359 if (!this->control_) {
360 return 0;
361 }
362 int32_t c = this->control_->GetWeakCount();
363 if (c > 0) {
364 // strong references are counted as single weak reference for the internal bookkeeping.
365 --c;
366 }
367 return c;
368 }
369
370 private:
371 using deletableType = BASE_NS::remove_const_t<T>;
372
ConstructBlock(T * ptr)373 void ConstructBlock(T* ptr)
374 {
375 static_assert(sizeof(T), "type has to be complete when constructing control block");
376 if constexpr (BASE_NS::is_base_of_v<CORE_NS::IInterface, T>) {
377 this->control_ = new Internals::RefCountedObjectStorageBlock(ptr);
378 } else {
379 this->control_ = new Internals::StorageBlock(ptr);
380 }
381 this->pointer_ = ptr;
382 }
383 template<typename Deleter>
ConstructBlock(T * ptr,Deleter deleter)384 void ConstructBlock(T* ptr, Deleter deleter)
385 {
386 this->control_ = new Internals::StorageBlockWithDeleter(ptr, BASE_NS::move(deleter));
387 this->pointer_ = ptr;
388 }
389
390 template<typename>
391 friend class weak_ptr;
392 template<typename>
393 friend class shared_ptr;
394 };
395
396 template<typename T, typename... Args>
make_shared(Args &&...args)397 BASE_NS::shared_ptr<T> make_shared(Args&&... args)
398 {
399 return BASE_NS::shared_ptr<T>(new T(BASE_NS::forward<Args>(args)...));
400 }
BASE_END_NAMESPACE()401 BASE_END_NAMESPACE()
402
403 // NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
404 template<class U, class T>
405 BASE_NS::shared_ptr<U> static_pointer_cast(const BASE_NS::shared_ptr<T>& ptr)
406 {
407 if (ptr) {
408 return BASE_NS::shared_ptr<U>(ptr, static_cast<U*>(ptr.get()));
409 }
410 return {};
411 }
412
413 template<typename T, typename... Args>
CreateShared(Args &&...args)414 BASE_NS::shared_ptr<T> CreateShared(Args&&... args)
415 {
416 return BASE_NS::make_shared<T>(BASE_NS::forward<Args>(args)...);
417 }
418 #endif // API_BASE_CONTAINERS_SHARED_PTR_H
419