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

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

cpp: added shared_ptr from weak_ptr constructor

  • Property mode set to 100644
File size: 16.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 <internal/functional/arithmetic_operations.hpp>
34#include <internal/functional/hash.hpp>
35#include <internal/memory/allocator_arg.hpp>
36#include <internal/memory/shared_payload.hpp>
37#include <internal/trycatch.hpp>
38#include <type_traits>
39
40namespace std
41{
42 template<class T>
43 class weak_ptr;
44
45 /**
46 * 20.8.2.1, class bad_weak_ptr:
47 */
48
49 class bad_weak_ptr: public exception
50 {
51 public:
52 bad_weak_ptr() noexcept = default;
53
54 const char* what() const noexcept override
55 {
56 return "std::bad_weak_ptr";
57 }
58 };
59
60 /**
61 * 20.8.2.2, class template shared_ptr:
62 */
63
64 template<class T>
65 class shared_ptr
66 {
67 public:
68 using element_type = T;
69
70 /**
71 * 20.8.2.2.1, constructors:
72 */
73
74 constexpr shared_ptr() noexcept
75 : payload_{}, data_{}
76 { /* DUMMY BODY */ }
77
78 template<class U>
79 explicit shared_ptr(
80 U* ptr,
81 enable_if_t<is_convertible_v<U*, element_type*>>* = nullptr
82 )
83 : payload_{}, data_{ptr}
84 {
85 try
86 {
87 payload_ = new aux::shared_payload<T>{ptr};
88 }
89 catch (const bad_alloc&)
90 {
91 delete ptr;
92
93 throw;
94 }
95 }
96
97 template<class U, class D>
98 shared_ptr(
99 U* ptr, D deleter,
100 enable_if_t<is_convertible_v<U*, element_type*>>* = nullptr
101 )
102 : shared_ptr{}
103 {
104 try
105 {
106 payload_ = new aux::shared_payload<T, D>{ptr, deleter};
107 }
108 catch (const bad_alloc&)
109 {
110 deleter(ptr);
111
112 throw;
113 }
114 }
115
116 template<class U, class D, class A>
117 shared_ptr(
118 U* ptr, D deleter, A,
119 enable_if_t<is_convertible_v<U*, element_type*>>* = nullptr
120 )
121 : shared_ptr{}
122 {
123 try
124 {
125 payload_ = new aux::shared_payload<T, D>{ptr, deleter};
126 }
127 catch (const bad_alloc&)
128 {
129 deleter(ptr);
130
131 throw;
132 }
133 }
134
135 template<class D>
136 shared_ptr(nullptr_t ptr, D deleter)
137 : shared_ptr{}
138 { /* DUMMY BODY */ }
139
140 template<class D, class A>
141 shared_ptr(nullptr_t, D deleter, A)
142 : shared_ptr{}
143 { /* DUMMY BODY */ }
144
145 template<class U>
146 shared_ptr(
147 const shared_ptr<U>& other, element_type* ptr,
148 enable_if_t<is_convertible_v<U*, element_type*>>* = nullptr
149 )
150 : payload_{other.payload_}, data_{ptr}
151 {
152 if (payload_)
153 payload_->increment();
154 }
155
156 shared_ptr(const shared_ptr& other)
157 : payload_{other.payload_}, data_{other.data_}
158 {
159 if (payload_)
160 payload_->increment();
161 }
162
163 template<class U>
164 shared_ptr(
165 const shared_ptr<U>& other,
166 enable_if_t<is_convertible_v<U*, element_type*>>* = nullptr
167 )
168 : payload_{other.payload_}, data_{other.data_}
169 {
170 if (payload_)
171 payload_->increment();
172 }
173
174 shared_ptr(shared_ptr&& other)
175 : payload_{move(other.payload_)}, data_{move(other.data_)}
176 {
177 other.payload_ = nullptr;
178 other.data_ = nullptr;
179 }
180
181 template<class U>
182 shared_ptr(
183 shared_ptr<U>&& other,
184 enable_if_t<is_convertible_v<U*, element_type*>>* = nullptr
185 )
186 : payload_{move(other.payload_)}, data_{move(other.data_)}
187 {
188 other.payload_ = nullptr;
189 other.data_ = nullptr;
190 }
191
192 template<class U>
193 explicit shared_ptr(
194 const weak_ptr<U>& other,
195 enable_if_t<is_convertible_v<U*, element_type*>>* = nullptr
196 )
197 : payload_{}, data_{}
198 {
199 if (other.expired())
200 throw bad_weak_ptr{};
201
202 if (other.payload_)
203 {
204 payload_ = other.payload_->lock();
205 data_ = payload_->get();
206 }
207 }
208
209 template<class U, class D>
210 shared_ptr(
211 unique_ptr<U, D>&& other,
212 enable_if_t<
213 is_convertible_v<
214 typename unique_ptr<U, D>::pointer,
215 element_type*
216 >
217 >* = nullptr
218 ) // TODO: if D is a reference type, it should be ref(other.get_deleter())
219 : shared_ptr{other.release(), other.get_deleter()}
220 { /* DUMMY BODY */ }
221
222 constexpr shared_ptr(nullptr_t) noexcept
223 : shared_ptr{}
224 { /* DUMMY BODY */ }
225
226 /**
227 * 20.8.2.2.2, destructor:
228 */
229
230 ~shared_ptr()
231 {
232 remove_payload_();
233 }
234
235 /**
236 * 20.8.2.2.3, assignment:
237 */
238
239 shared_ptr& operator=(const shared_ptr& rhs) noexcept
240 {
241 if (rhs.payload_)
242 rhs.payload_->increment();
243
244 remove_payload_();
245
246 payload_ = rhs.payload_;
247 data_ = rhs.data_;
248
249 return *this;
250 }
251
252 template<class U>
253 enable_if_t<is_convertible_v<U*, element_type*>, shared_ptr>&
254 operator=(const shared_ptr<U>& rhs) noexcept
255 {
256 if (rhs.payload_)
257 rhs.payload_->increment();
258
259 remove_payload_();
260
261 payload_ = rhs.payload_;
262 data_ = rhs.data_;
263
264 return *this;
265 }
266
267 shared_ptr& operator=(shared_ptr&& rhs) noexcept
268 {
269 shared_ptr{move(rhs)}.swap(*this);
270
271 return *this;
272 }
273
274 template<class U>
275 shared_ptr& operator=(shared_ptr<U>&& rhs) noexcept
276 {
277 shared_ptr{move(rhs)}.swap(*this);
278
279 return *this;
280 }
281
282 template<class U, class D>
283 shared_ptr& operator=(unique_ptr<U, D>&& rhs)
284 {
285 shared_ptr{move(rhs)}.swap(*this);
286
287 return *this;
288 }
289
290 /**
291 * 20.8.2.2.4, modifiers:
292 */
293
294 void swap(shared_ptr& other) noexcept
295 {
296 std::swap(payload_, other.payload_);
297 std::swap(data_, other.data_);
298 }
299
300 void reset() noexcept
301 {
302 shared_ptr{}.swap(*this);
303 }
304
305 template<class U>
306 void reset(U* ptr)
307 {
308 shared_ptr{ptr}.swap(*this);
309 }
310
311 template<class U, class D>
312 void reset(U* ptr, D deleter)
313 {
314 shared_ptr{ptr, deleter}.swap(*this);
315 }
316
317 template<class U, class D, class A>
318 void reset(U* ptr, D deleter, A alloc)
319 {
320 shared_ptr{ptr, deleter, alloc}.swap(*this);
321 }
322
323 /**
324 * 20.8.2.2.5, observers:
325 */
326
327 element_type* get() const noexcept
328 {
329 return data_;
330 }
331
332 enable_if_t<!is_void_v<T>, T&> operator*() const noexcept
333 {
334 return *data_;
335 }
336
337 T* operator->() const noexcept
338 {
339 return get();
340 }
341
342 long use_count() const noexcept
343 {
344 if (payload_)
345 return payload_->refs();
346 else
347 return 0L;
348 }
349
350 bool unique() const noexcept
351 {
352 return use_count() == 1L;
353 }
354
355 explicit operator bool() const noexcept
356 {
357 return get() != nullptr;
358 }
359
360 template<class U>
361 bool owner_before(const shared_ptr<U>& ptr) const
362 {
363 return payload_ < ptr.payload_;
364 }
365
366 template<class U>
367 bool owner_before(const weak_ptr<U>& ptr) const
368 {
369 return payload_ < ptr.payload_;
370 }
371
372 private:
373 aux::shared_payload_base<element_type>* payload_;
374 element_type* data_;
375
376 shared_ptr(aux::payload_tag_t, aux::shared_payload_base<element_type>* payload)
377 : payload_{payload}, data_{payload->get()}
378 { /* DUMMY BODY */ }
379
380 void remove_payload_()
381 {
382 if (payload_)
383 {
384 auto res = payload_->decrement();
385 if (res)
386 payload_->destroy();
387
388 payload_ = nullptr;
389 }
390
391 if (data_)
392 data_ = nullptr;
393 }
394
395 template<class U, class... Args>
396 friend shared_ptr<U> make_shared(Args&&...);
397
398 template<class U, class A, class... Args>
399 friend shared_ptr<U> allocate_shared(const A&, Args&&...);
400
401 template<class D, class U>
402 D* get_deleter(const shared_ptr<U>&) noexcept;
403
404 template<class U>
405 friend class weak_ptr;
406 };
407
408 /**
409 * 20.8.2.2.6, shared_ptr creation:
410 * Note: According to the standard, these two functions
411 * should perform at most one memory allocation
412 * (should, don't have to :P). It might be better
413 * to create payloads that embed the type T to
414 * perform this optimization.
415 */
416
417 template<class T, class... Args>
418 shared_ptr<T> make_shared(Args&&... args)
419 {
420 return shared_ptr<T>{
421 aux::payload_tag,
422 new aux::shared_payload<T>{forward<Args>(args)...}
423 };
424 }
425
426 template<class T, class A, class... Args>
427 shared_ptr<T> allocate_shared(const A& alloc, Args&&... args)
428 {
429 return shared_ptr<T>{
430 aux::payload_tag,
431 new aux::shared_payload<T>{allocator_arg, A{alloc}, forward<Args>(args)...}
432 };
433 }
434
435 /**
436 * 20.8.2.2.7, shared_ptr comparisons:
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 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 !(lhs == rhs);
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 less<common_type_t<T*, U*>>{}(lhs.get(), rhs.get());
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 rhs < lhs;
461 }
462
463 template<class T, class U>
464 bool operator<=(const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) noexcept
465 {
466 return !(rhs < lhs);
467 }
468
469 template<class T, class U>
470 bool operator>=(const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) noexcept
471 {
472 return !(lhs < rhs);
473 }
474
475 template<class T>
476 bool operator==(const shared_ptr<T>& lhs, nullptr_t) noexcept
477 {
478 return !lhs;
479 }
480
481 template<class T>
482 bool operator==(nullptr_t, const shared_ptr<T>& rhs) noexcept
483 {
484 return !rhs;
485 }
486
487 template<class T>
488 bool operator!=(const shared_ptr<T>& lhs, nullptr_t) noexcept
489 {
490 return (bool)lhs;
491 }
492
493 template<class T>
494 bool operator!=(nullptr_t, const shared_ptr<T>& rhs) noexcept
495 {
496 return (bool)rhs;
497 }
498
499 template<class T>
500 bool operator<(const shared_ptr<T>& lhs, nullptr_t) noexcept
501 {
502 return less<T*>{}(lhs.get(), nullptr);
503 }
504
505 template<class T>
506 bool operator<(nullptr_t, const shared_ptr<T>& rhs) noexcept
507 {
508 return less<T*>{}(nullptr, rhs.get());
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 !(nullptr < lhs);
527 }
528
529 template<class T>
530 bool operator<=(nullptr_t, const shared_ptr<T>& rhs) noexcept
531 {
532 return !(rhs < nullptr);
533 }
534
535 template<class T>
536 bool operator>=(const shared_ptr<T>& lhs, nullptr_t) noexcept
537 {
538 return !(lhs < nullptr);
539 }
540
541 template<class T>
542 bool operator>=(nullptr_t, const shared_ptr<T>& rhs) noexcept
543 {
544 return !(nullptr < rhs);
545 }
546
547 /**
548 * 20.8.2.2.8, shared_ptr specialized algorithms:
549 */
550
551 template<class T>
552 void swap(shared_ptr<T>& lhs, shared_ptr<T>& rhs) noexcept
553 {
554 lhs.swap(rhs);
555 }
556
557 /**
558 * 20.8.2.2.9, shared_ptr casts:
559 */
560
561 template<class T, class U>
562 shared_ptr<T> static_pointer_cast(const shared_ptr<U>& ptr) noexcept
563 {
564 if (!ptr)
565 return shared_ptr<T>{};
566
567 return shared_ptr<T>{
568 ptr, static_cast<T*>(ptr.get())
569 };
570 }
571
572 template<class T, class U>
573 shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& ptr) noexcept
574 {
575 if (auto res = dynamic_cast<T*>(ptr.get()))
576 return shared_ptr<T>{ptr, res};
577 else
578 return shared_ptr<T>{};
579 }
580
581 template<class T, class U>
582 shared_ptr<T> const_pointer_cast(const shared_ptr<U>& ptr) noexcept
583 {
584 if (!ptr)
585 return shared_ptr<T>{};
586
587 return shared_ptr<T>{
588 ptr, const_cast<T*>(ptr.get())
589 };
590 }
591
592 /**
593 * 20.8.2.2.10, shared_ptr get_deleter:
594 */
595
596 template<class D, class T>
597 D* get_deleter(const shared_ptr<T>& ptr) noexcept
598 {
599 if (ptr.payload_)
600 return static_cast<D*>(ptr.payload_->deleter());
601 else
602 return nullptr;
603 }
604
605 /**
606 * 20.8.2.2.11, shared_ptr I/O:
607 */
608
609 template<class Char, class Traits, class T>
610 basic_ostream<Char, Traits>& operator<<(basic_ostream<Char, Traits>& os,
611 const shared_ptr<T>& ptr)
612 {
613 return os << ptr.get();
614 }
615
616 /**
617 * 20.8.2.5, class template enable_shared_from_this:
618 */
619
620 // TODO: implement
621
622 /**
623 * 20.8.2.6, shared_ptr atomic access
624 */
625
626 // TODO: implement
627
628 /**
629 * 20.8.2.7, smart pointer hash support:
630 */
631
632 template<class T>
633 struct hash<shared_ptr<T>>
634 {
635 size_t operator()(const shared_ptr<T>& ptr) const noexcept
636 {
637 return hash<T*>{}(ptr.get());
638 }
639
640 using argument_type = shared_ptr<T>;
641 using result_type = size_t;
642 };
643}
644
645#endif
Note: See TracBrowser for help on using the repository browser.