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

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

cpp: fixed enable_if usage, added payload_tag that allows us to properly construct smart pointers from payloads

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