source: mainline/uspace/lib/cpp/include/internal/memory/shared_ptr.hpp@ 5735b111

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5735b111 was 5735b111, checked in by Dzejrou <dzejrou@…>, 7 years ago

cpp: added a basic shared_ptr implementation

  • Property mode set to 100644
File size: 15.9 KB
Line 
1/*
2 * Copyright (c) 2018 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_INTERNAL_MEMORY_SHARED_PTR
30#define LIBCPP_INTERNAL_MEMORY_SHARED_PTR
31
32#include <exception>
33#include <functional>
34#include <internal/memory/allocator_arg.hpp>
35#include <internal/memory/shared_payload.hpp>
36#include <type_traits>
37
38namespace std
39{
40 template<class T>
41 class weak_ptr;
42
43 /**
44 * 20.8.2.1, class bad_weak_ptr:
45 */
46
47 class bad_weak_ptr: public exception
48 {
49 public:
50 bad_weak_ptr() noexcept = default;
51
52 const char* what() const noexcept override
53 {
54 return "std::bad_weak_ptr";
55 }
56 };
57
58 /**
59 * 20.8.2.2, class template shared_ptr:
60 */
61
62 template<class T>
63 class shared_ptr
64 {
65 public:
66 using element_type = T;
67
68 /**
69 * 20.8.2.2.1, constructors:
70 */
71
72 constexpr shared_ptr() noexcept
73 : payload_{}, data_{}
74 { /* DUMMY BODY */ }
75
76 template<class U>
77 explicit shared_ptr(
78 enable_if_t<is_convertible_v<U*, element_type*>, U*> ptr
79 )
80 : payload_{}, data_{ptr}
81 {
82 try
83 {
84 payload_ = new aux::shared_payload<T>{ptr};
85 }
86 catch (const bad_alloc&)
87 {
88 delete ptr;
89
90 throw;
91 }
92 }
93
94 template<class U, class D>
95 shared_ptr(
96 enable_if_t<is_convertible_v<U*, element_type*>, U*> ptr, D deleter
97 )
98 : shared_ptr{}
99 {
100 try
101 {
102 payload_ = new aux::shared_payload<T, D>{ptr, deleter};
103 }
104 catch (const bad_alloc&)
105 {
106 deleter(ptr);
107
108 throw;
109 }
110 }
111
112 template<class U, class D, class A>
113 shared_ptr(
114 enable_if_t<is_convertible_v<U*, element_type*>, U*> ptr,
115 D deleter, A
116 )
117 : shared_ptr{}
118 {
119 try
120 {
121 payload_ = new aux::shared_payload<T, D>{ptr, deleter};
122 }
123 catch (const bad_alloc&)
124 {
125 deleter(ptr);
126
127 throw;
128 }
129 }
130
131 template<class D>
132 shared_ptr(nullptr_t ptr, D deleter)
133 : shared_ptr{}
134 { /* DUMMY BODY */ }
135
136 template<class D, class A>
137 shared_ptr(nullptr_t, D deleter, A)
138 : shared_ptr{}
139 { /* DUMMY BODY */ }
140
141 template<class U>
142 shared_ptr(
143 enable_if_t<is_convertible_v<U*, element_type*>, const shared_ptr<U>&> other,
144 element_type* ptr
145 )
146 : payload_{other.payload_}, data_{ptr}
147 {
148 if (payload_)
149 payload_->increment();
150 }
151
152 shared_ptr(const shared_ptr& other)
153 : payload_{other.payload_}, data_{other.data_}
154 {
155 if (payload_)
156 payload_->increment();
157 }
158
159 template<class U>
160 shared_ptr(
161 enable_if_t<is_convertible_v<U*, element_type*>, const shared_ptr<U>&> other
162 )
163 : payload_{other.payload_}, data_{other.data_}
164 {
165 if (payload_)
166 payload_->increment();
167 }
168
169 shared_ptr(shared_ptr&& other)
170 : payload_{move(other.payload_)}, data_{move(other.data_)}
171 {
172 other.payload_ = nullptr;
173 other.data_ = nullptr;
174 }
175
176 template<class U>
177 shared_ptr(
178 enable_if_t<is_convertible_v<U*, element_type*>, shared_ptr<U>&&> other
179 )
180 : payload_{move(other.payload_)}, data_{move(other.data_)}
181 {
182 other.payload_ = nullptr;
183 other.data_ = nullptr;
184 }
185
186 template<class U>
187 explicit shared_ptr(
188 enable_if_t<is_convertible_v<U*, element_type*>, const weak_ptr<U>&> other
189 )
190 {
191 if (other.expired())
192 throw bad_weak_ptr{};
193 // TODO:
194 }
195
196 template<class U, class D>
197 shared_ptr(
198 enable_if_t<
199 is_convertible_v<
200 typename unique_ptr<U, D>::pointer,
201 element_type*
202 >,
203 unique_ptr<U, D>&&
204 > other
205 ) // TODO: if D is a reference type, it should be ref(other.get_deleter())
206 : shared_ptr{other.release(), other.get_deleter()}
207 { /* DUMMY BODY */ }
208
209 constexpr shared_ptr(nullptr_t) noexcept
210 : shared_ptr{}
211 { /* DUMMY BODY */ }
212
213 /**
214 * 20.8.2.2.2, destructor:
215 */
216
217 ~shared_ptr()
218 {
219 remove_payload_();
220 }
221
222 /**
223 * 20.8.2.2.3, assignment:
224 */
225
226 shared_ptr& operator=(const shared_ptr& rhs) noexcept
227 {
228 if (rhs.payload_)
229 rhs.payload_->increment();
230
231 remove_payload_();
232
233 payload_ = rhs.payload_;
234 data_ = rhs.data_;
235
236 return *this;
237 }
238
239 template<class U>
240 shared_ptr& operator=(
241 enable_if_t<is_convertible_v<U*, element_type*>, const shared_ptr<U>&> rhs
242 ) noexcept
243 {
244 if (rhs.payload_)
245 rhs.payload_->increment();
246
247 remove_payload_();
248
249 payload_ = rhs.payload_;
250 data_ = rhs.data_;
251
252 return *this;
253 }
254
255 shared_ptr& operator=(shared_ptr&& rhs) noexcept
256 {
257 shared_ptr{move(rhs)}.swap(*this);
258
259 return *this;
260 }
261
262 template<class U>
263 shared_ptr& operator=(
264 enable_if_t<is_convertible_v<U*, element_type*>, shared_ptr<U>&&> rhs
265 ) noexcept
266 {
267 shared_ptr{move(rhs)}.swap(*this);
268
269 return *this;
270 }
271
272 template<class U, class D>
273 shared_ptr& operator=(unique_ptr<U, D>&& rhs)
274 {
275 shared_ptr{move(rhs)}.swap(*this);
276
277 return *this;
278 }
279
280 /**
281 * 20.8.2.2.4, modifiers:
282 */
283
284 void swap(shared_ptr& other) noexcept
285 {
286 std::swap(payload_, other.payload_);
287 std::swap(data_, other.data_);
288 }
289
290 void reset() noexcept
291 {
292 shared_ptr{}.swap(*this);
293 }
294
295 template<class U>
296 void reset(U* ptr)
297 {
298 shared_ptr{ptr}.swap(*this);
299 }
300
301 template<class U, class D>
302 void reset(U* ptr, D deleter)
303 {
304 shared_ptr{ptr, deleter}.swap(*this);
305 }
306
307 template<class U, class D, class A>
308 void reset(U* ptr, D deleter, A alloc)
309 {
310 shared_ptr{ptr, deleter, alloc}.swap(*this);
311 }
312
313 /**
314 * 20.8.2.2.5, observers:
315 */
316
317 element_type* get() const noexcept
318 {
319 return data_;
320 }
321
322 enable_if_t<!is_void_v<T>, T&> operator*() const noexcept
323 {
324 return *data_;
325 }
326
327 T* operator->() const noexcept
328 {
329 return get();
330 }
331
332 long use_count() const noexcept
333 {
334 if (payload_)
335 return payload_->refcount();
336 else
337 return 0L;
338 }
339
340 bool unique() const noexcept
341 {
342 return use_count() == 1L;
343 }
344
345 explicit operator bool() const noexcept
346 {
347 return get() != nullptr;
348 }
349
350 template<class U>
351 bool owner_before(const shared_ptr<U>& ptr) const
352 {
353 return payload_ < ptr.payload_;
354 }
355
356 template<class U>
357 bool owner_before(const weak_ptr<U>& ptr) const
358 {
359 return payload_ < ptr.payload_;
360 }
361
362 private:
363 aux::shared_payload_base<element_type>* payload_;
364 element_type* data_;
365
366 shared_ptr(aux::shared_payload_base<element_type>* payload)
367 : payload_{payload}, data_{payload->get()}
368 { /* DUMMY BODY */ }
369
370 void remove_payload_()
371 {
372 if (payload_)
373 {
374 auto res = payload_->decrement();
375 if (res)
376 payload_->destroy();
377
378 payload_ = nullptr;
379 }
380
381 if (data_)
382 data_ = nullptr;
383 }
384
385 template<class U, class... Args>
386 friend shared_ptr<U> make_shared(Args&&...);
387
388 template<class U, class A, class... Args>
389 friend shared_ptr<U> allocate_shared(const A&, Args&&...);
390
391 template<class D, class U>
392 D* get_deleter(const shared_ptr<U>&) noexcept;
393
394 template<class U>
395 friend class weak_ptr;
396 };
397
398 /**
399 * 20.8.2.2.6, shared_ptr creation:
400 * Note: According to the standard, these two functions
401 * should perform at most one memory allocation
402 * (should, don't have to :P). It might be better
403 * to create payloads that embed the type T to
404 * perform this optimization.
405 */
406
407 template<class T, class... Args>
408 shared_ptr<T> make_shared(Args&&... args)
409 {
410 return shared_ptr<T>{
411 new aux::shared_payload<T>{forward<Args>(args)...}
412 };
413 }
414
415 template<class T, class A, class... Args>
416 shared_ptr<T> allocate_shared(const A& alloc, Args&&... args)
417 {
418 return shared_ptr<T>{
419 new aux::shared_payload<T>{allocator_arg, A{alloc}, forward<Args>(args)...}
420 };
421 }
422
423 /**
424 * 20.8.2.2.7, shared_ptr comparisons:
425 */
426
427 template<class T, class U>
428 bool operator==(const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) noexcept
429 {
430 return lhs.get() == rhs.get();
431 }
432
433 template<class T, class U>
434 bool operator!=(const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) noexcept
435 {
436 return !(lhs == rhs);
437 }
438
439 template<class T, class U>
440 bool operator<(const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) noexcept
441 {
442 return less<common_type_t<T*, U*>>{}(lhs.get(), rhs.get());
443 }
444
445 template<class T, class U>
446 bool operator>(const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) noexcept
447 {
448 return rhs < lhs;
449 }
450
451 template<class T, class U>
452 bool operator<=(const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) noexcept
453 {
454 return !(rhs < lhs);
455 }
456
457 template<class T, class U>
458 bool operator>=(const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) noexcept
459 {
460 return !(lhs < rhs);
461 }
462
463 template<class T>
464 bool operator==(const shared_ptr<T>& lhs, nullptr_t) noexcept
465 {
466 return !lhs;
467 }
468
469 template<class T>
470 bool operator==(nullptr_t, const shared_ptr<T>& rhs) noexcept
471 {
472 return !rhs;
473 }
474
475 template<class T>
476 bool operator!=(const shared_ptr<T>& lhs, nullptr_t) noexcept
477 {
478 return (bool)lhs;
479 }
480
481 template<class T>
482 bool operator!=(nullptr_t, const shared_ptr<T>& rhs) noexcept
483 {
484 return (bool)rhs;
485 }
486
487 template<class T>
488 bool operator<(const shared_ptr<T>& lhs, nullptr_t) noexcept
489 {
490 return less<T*>{}(lhs.get(), nullptr);
491 }
492
493 template<class T>
494 bool operator<(nullptr_t, const shared_ptr<T>& rhs) noexcept
495 {
496 return less<T*>{}(nullptr, rhs.get());
497 }
498
499 template<class T>
500 bool operator>(const shared_ptr<T>& lhs, nullptr_t) noexcept
501 {
502 return nullptr < lhs;
503 }
504
505 template<class T>
506 bool operator>(nullptr_t, const shared_ptr<T>& rhs) noexcept
507 {
508 return rhs < nullptr;
509 }
510
511 template<class T>
512 bool operator<=(const shared_ptr<T>& lhs, nullptr_t) noexcept
513 {
514 return !(nullptr < lhs);
515 }
516
517 template<class T>
518 bool operator<=(nullptr_t, const shared_ptr<T>& rhs) noexcept
519 {
520 return !(rhs < nullptr);
521 }
522
523 template<class T>
524 bool operator>=(const shared_ptr<T>& lhs, nullptr_t) noexcept
525 {
526 return !(lhs < nullptr);
527 }
528
529 template<class T>
530 bool operator>=(nullptr_t, const shared_ptr<T>& rhs) noexcept
531 {
532 return !(nullptr < rhs);
533 }
534
535 /**
536 * 20.8.2.2.8, shared_ptr specialized algorithms:
537 */
538
539 template<class T>
540 void swap(shared_ptr<T>& lhs, shared_ptr<T>& rhs) noexcept
541 {
542 lhs.swap(rhs);
543 }
544
545 /**
546 * 20.8.2.2.9, shared_ptr casts:
547 */
548
549 template<class T, class U>
550 shared_ptr<T> static_pointer_cast(const shared_ptr<U>& ptr) noexcept
551 {
552 if (!ptr)
553 return shared_ptr<T>{};
554
555 return shared_ptr<T>{
556 ptr, static_cast<T*>(ptr.get())
557 };
558 }
559
560 template<class T, class U>
561 shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& ptr) noexcept
562 {
563 if (auto res = dynamic_cast<T*>(ptr.get()))
564 return shared_ptr<T>{ptr, res};
565 else
566 return shared_ptr<T>{};
567 }
568
569 template<class T, class U>
570 shared_ptr<T> const_pointer_cast(const shared_ptr<U>& ptr) noexcept
571 {
572 if (!ptr)
573 return shared_ptr<T>{};
574
575 return shared_ptr<T>{
576 ptr, const_cast<T*>(ptr.get())
577 };
578 }
579
580 /**
581 * 20.8.2.2.10, shared_ptr get_deleter:
582 */
583
584 template<class D, class T>
585 D* get_deleter(const shared_ptr<T>& ptr) noexcept
586 {
587 if (ptr.payload_)
588 return static_cast<D*>(ptr.payload_->deleter());
589 else
590 return nullptr;
591 }
592
593 /**
594 * 20.8.2.2.11, shared_ptr I/O:
595 */
596
597 template<class Char, class Traits, class T>
598 basic_ostream<Char, Traits>& operator<<(basic_ostream<Char, Traits>& os,
599 const shared_ptr<T>& ptr)
600 {
601 return os << ptr.get();
602 }
603}
604
605#endif
Note: See TracBrowser for help on using the repository browser.