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

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

cpp: added hash support for smart pointers

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