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

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

cpp: remove impl/memory.hpp and moved its contents to internal/memory/

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