/* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef SHARED_PTR_HH_ #define SHARED_PTR_HH_ #include #include // This header defines two shared pointer facilities, lw_shared_ptr<> and // shared_ptr<>, both modeled after std::shared_ptr<>. // // Unlike std::shared_ptr<>, neither of these implementations are thread // safe, and two pointers sharing the same object must not be used in // different threads. // // lw_shared_ptr<> is the more lightweight variant, with a lw_shared_ptr<> // occupying just one machine word, and adding just one word to the shared // object. However, it does not support polymorphism. // // shared_ptr<> is more expensive, with a pointer occupying two machine // words, and with two words of overhead in the shared object. In return, // it does support polymorphism. // // Both variants support shared_from_this() via enable_shared_from_this<> // and lw_enable_shared_from_this<>(). // template class lw_shared_ptr; template class shared_ptr; template class enable_lw_shared_from_this; template class enable_shared_from_this; template lw_shared_ptr make_lw_shared(A&&... a); template lw_shared_ptr make_lw_shared(T&& a); template lw_shared_ptr make_lw_shared(T& a); template shared_ptr make_shared(A&&... a); template shared_ptr static_pointer_cast(const shared_ptr& p); template shared_ptr dynamic_pointer_cast(const shared_ptr& p); template shared_ptr const_pointer_cast(const shared_ptr& p); // We want to support two use cases for shared_ptr: // // 1. T is any type (primitive or class type) // // 2. T is a class type that inherits from enable_shared_from_this. // // In the first case, we must wrap T in an object containing the counter, // since T may be a primitive type and cannot be a base class. // // In the second case, we want T to reach the counter through its // enable_shared_from_this<> base class, so that we can implement // shared_from_this(). // // To implement those two conflicting requirements (T alongside its counter; // T inherits from an object containing the counter) we use std::conditional<> // and some accessor functions to select between two implementations. // CRTP from this to enable shared_from_this: template class enable_lw_shared_from_this { long _count = 0; using ctor = T; T* to_value() { return static_cast(this); } T* to_internal_object() { return static_cast(this); } protected: enable_lw_shared_from_this& operator=(const enable_lw_shared_from_this&) { return *this; } enable_lw_shared_from_this& operator=(enable_lw_shared_from_this&&) { return *this; } public: lw_shared_ptr shared_from_this(); template friend class lw_shared_ptr; }; template struct shared_ptr_no_esft { long _count = 0; T _value; using ctor = shared_ptr_no_esft; T* to_value() { return &_value; } shared_ptr_no_esft* to_internal_object() { return this; } shared_ptr_no_esft() = default; shared_ptr_no_esft(const T& x) : _value(x) {} shared_ptr_no_esft(T&& x) : _value(std::move(x)) {} template shared_ptr_no_esft(A&&... a) : _value(std::forward(a)...) {} template friend class lw_shared_ptr; }; template using shared_ptr_impl = std::conditional_t< std::is_base_of, T>::value, enable_lw_shared_from_this, shared_ptr_no_esft >; template class lw_shared_ptr { mutable shared_ptr_impl* _p = nullptr; private: lw_shared_ptr(shared_ptr_impl* p) noexcept : _p(p) { if (_p) { ++_p->_count; } } template static lw_shared_ptr make(A&&... a) noexcept { return lw_shared_ptr(new typename shared_ptr_impl::ctor(std::forward(a)...)); } public: using element_type = T; lw_shared_ptr() noexcept = default; lw_shared_ptr(const lw_shared_ptr& x) noexcept : _p(x._p) { if (_p) { ++_p->_count; } } lw_shared_ptr(lw_shared_ptr&& x) noexcept : _p(x._p) { x._p = nullptr; } ~lw_shared_ptr() { if (_p && !--_p->_count) { delete _p->to_internal_object(); } } lw_shared_ptr& operator=(const lw_shared_ptr& x) noexcept { if (_p != x._p) { this->~lw_shared_ptr(); new (this) lw_shared_ptr(x); } return *this; } lw_shared_ptr& operator=(lw_shared_ptr&& x) noexcept { if (_p != x._p) { this->~lw_shared_ptr(); new (this) lw_shared_ptr(std::move(x)); } return *this; } lw_shared_ptr& operator=(T&& x) noexcept { this->~lw_shared_ptr(); new (this) lw_shared_ptr(make_lw_shared(std::move(x))); return *this; } T& operator*() const noexcept { return *_p->to_value(); } T* operator->() const noexcept { return _p->to_value(); } T* get() const noexcept { return _p->to_value(); } long int use_count() noexcept { if (_p) { return _p->_count; } else { return 0; } } operator lw_shared_ptr() const noexcept { return lw_shared_ptr(_p); } explicit operator bool() const noexcept { return _p; } bool owned() const noexcept { return _p->_count == 1; } template friend lw_shared_ptr make_lw_shared(A&&...); template friend lw_shared_ptr make_lw_shared(U&&); template friend lw_shared_ptr make_lw_shared(U&); template friend class enable_lw_shared_from_this; }; template inline lw_shared_ptr make_lw_shared(A&&... a) { return lw_shared_ptr::make(std::forward(a)...); } template inline lw_shared_ptr make_lw_shared(T&& a) { return lw_shared_ptr::make(std::move(a)); } template inline lw_shared_ptr make_lw_shared(T& a) { return lw_shared_ptr::make(a); } template inline lw_shared_ptr enable_lw_shared_from_this::shared_from_this() { return lw_shared_ptr(this); } // Polymorphic shared pointer class struct shared_ptr_count_base { // destructor is responsible for fully-typed deletion virtual ~shared_ptr_count_base() {} long count = 0; }; template struct shared_ptr_count_for : shared_ptr_count_base { T data; template shared_ptr_count_for(A&&... a) : data(std::forward(a)...) {} }; template class enable_shared_from_this : private shared_ptr_count_base { public: shared_ptr shared_from_this(); template friend class shared_ptr; }; template class shared_ptr { mutable shared_ptr_count_base* _b = nullptr; mutable T* _p = nullptr; private: explicit shared_ptr(shared_ptr_count_for* b) noexcept : _b(b), _p(&b->data) { ++_b->count; } shared_ptr(shared_ptr_count_base* b, T* p) noexcept : _b(b), _p(p) { // test _p, not _b, since dynamic_pointer_cast<>() can zero p but not b if (_p) { ++_b->count; } } explicit shared_ptr(enable_shared_from_this* p) noexcept : _b(p), _p(static_cast(p)) { if (_b) { ++_b->count; } } public: shared_ptr() noexcept = default; shared_ptr(const shared_ptr& x) noexcept : _b(x._b) , _p(x._p) { if (_b) { ++_b->count; } } shared_ptr(shared_ptr&& x) noexcept : _b(x._b) , _p(x._p) { x._b = nullptr; x._p = nullptr; } template ::value>> shared_ptr(const shared_ptr& x) noexcept : _b(x._b) , _p(x._p) { if (_b) { ++_b->count; } } template ::value>> shared_ptr(shared_ptr&& x) noexcept : _b(x._b) , _p(x._p) { x._b = nullptr; x._p = nullptr; } ~shared_ptr() { if (_b && !--_b->count) { delete _b; } } shared_ptr& operator=(const shared_ptr& x) noexcept { if (this != &x) { this->~shared_ptr(); new (this) shared_ptr(x); } return *this; } shared_ptr& operator=(shared_ptr&& x) noexcept { if (this != &x) { this->~shared_ptr(); new (this) shared_ptr(std::move(x)); } return *this; } template ::value>> shared_ptr& operator=(const shared_ptr& x) noexcept { if (this != &x) { this->~shared_ptr(); new (this) shared_ptr(x); } return *this; } template ::value>> shared_ptr& operator=(shared_ptr&& x) noexcept { if (this != &x) { this->~shared_ptr(); new (this) shared_ptr(std::move(x)); } return *this; } explicit operator bool() const noexcept { return _p; } T& operator*() const noexcept { return *_p; } T* operator->() const noexcept { return _p; } T* get() const noexcept { return _p; } template struct make_helper; template friend shared_ptr make_shared(A&&... a); template friend shared_ptr static_pointer_cast(const shared_ptr& p); template friend shared_ptr dynamic_pointer_cast(const shared_ptr& p); template friend shared_ptr const_pointer_cast(const shared_ptr& p); template static shared_ptr make(A&&... a); template friend class enable_shared_from_this; template friend struct shared_ptr_make_helper; template friend class shared_ptr; }; template struct shared_ptr_make_helper; template struct shared_ptr_make_helper { template static shared_ptr make(A&&... a) { return shared_ptr(new shared_ptr_count_for(std::forward(a)...)); } }; template struct shared_ptr_make_helper { template static shared_ptr make(A&&... a) { return shared_ptr(new T(std::forward(a)...)); } }; template inline shared_ptr make_shared(A&&... a) { using helper = shared_ptr_make_helper, T>::value>; return helper::make(std::forward(a)...); } template inline shared_ptr static_pointer_cast(const shared_ptr& p) { return shared_ptr(p._b, static_cast(p._p)); } template inline shared_ptr dynamic_pointer_cast(const shared_ptr& p) { return shared_ptr(p._b, dynamic_cast(p._p)); } template inline shared_ptr const_pointer_cast(const shared_ptr& p) { return shared_ptr(p._b, const_cast(p._p)); } template inline shared_ptr enable_shared_from_this::shared_from_this() { return shared_ptr(this); } template inline bool operator==(const shared_ptr& x, const shared_ptr& y) { return x.get() == y.get(); } template inline bool operator==(const shared_ptr& x, std::nullptr_t) { return x.get() == nullptr; } template inline bool operator==(std::nullptr_t, const shared_ptr& y) { return nullptr == y.get(); } template inline bool operator!=(const shared_ptr& x, const shared_ptr& y) { return x.get() != y.get(); } template inline bool operator!=(const shared_ptr& x, std::nullptr_t) { return x.get() != nullptr; } template inline bool operator!=(std::nullptr_t, const shared_ptr& y) { return nullptr != y.get(); } template inline bool operator<(const shared_ptr& x, const shared_ptr& y) { return x.get() < y.get(); } template inline bool operator<(const shared_ptr& x, std::nullptr_t) { return x.get() < nullptr; } template inline bool operator<(std::nullptr_t, const shared_ptr& y) { return nullptr < y.get(); } template inline bool operator<=(const shared_ptr& x, const shared_ptr& y) { return x.get() <= y.get(); } template inline bool operator<=(const shared_ptr& x, std::nullptr_t) { return x.get() <= nullptr; } template inline bool operator<=(std::nullptr_t, const shared_ptr& y) { return nullptr <= y.get(); } template inline bool operator>(const shared_ptr& x, const shared_ptr& y) { return x.get() > y.get(); } template inline bool operator>(const shared_ptr& x, std::nullptr_t) { return x.get() > nullptr; } template inline bool operator>(std::nullptr_t, const shared_ptr& y) { return nullptr > y.get(); } template inline bool operator>=(const shared_ptr& x, const shared_ptr& y) { return x.get() >= y.get(); } template inline bool operator>=(const shared_ptr& x, std::nullptr_t) { return x.get() >= nullptr; } template inline bool operator>=(std::nullptr_t, const shared_ptr& y) { return nullptr >= y.get(); } #endif /* SHARED_PTR_HH_ */