/* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef SHARED_PTR_HH_ #define SHARED_PTR_HH_ #include "shared_ptr_debug_helper.hh" #include #include #include #include #include "util/is_smart_ptr.hh" // 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<>(). // #ifndef DEBUG_SHARED_PTR using shared_ptr_counter_type = long; #else using shared_ptr_counter_type = debug_shared_ptr_counter_type; #endif 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 make_shared(T&& 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 { shared_ptr_counter_type _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() noexcept {} enable_lw_shared_from_this(enable_lw_shared_from_this&&) noexcept {} enable_lw_shared_from_this(const enable_lw_shared_from_this&) noexcept {} enable_lw_shared_from_this& operator=(const enable_lw_shared_from_this&) noexcept { return *this; } enable_lw_shared_from_this& operator=(enable_lw_shared_from_this&&) noexcept { return *this; } public: lw_shared_ptr shared_from_this(); lw_shared_ptr shared_from_this() const; template friend class lw_shared_ptr; }; template struct shared_ptr_no_esft { shared_ptr_counter_type _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(std::nullptr_t) noexcept : lw_shared_ptr() {} 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=(std::nullptr_t) noexcept { return *this = lw_shared_ptr(); } 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() const 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; } bool operator==(const lw_shared_ptr& x) const { return _p == x._p; } bool operator!=(const lw_shared_ptr& x) const { return !operator==(x); } bool operator==(const lw_shared_ptr>& x) const { return _p == x._p; } bool operator!=(const lw_shared_ptr>& x) const { return !operator==(x); } template friend class lw_shared_ptr; 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); } template inline lw_shared_ptr enable_lw_shared_from_this::shared_from_this() const { return lw_shared_ptr(const_cast(this)); } template static inline std::ostream& operator<<(std::ostream& out, const lw_shared_ptr& p) { if (!p) { return out << "null"; } return out << *p; } // Polymorphic shared pointer class struct shared_ptr_count_base { // destructor is responsible for fully-typed deletion virtual ~shared_ptr_count_base() {} shared_ptr_counter_type 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(); shared_ptr shared_from_this() const; template friend class shared_ptr; template friend struct shared_ptr_make_helper; }; 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) { if (_b) { ++_b->count; } } explicit shared_ptr(enable_shared_from_this>* p) noexcept : _b(p), _p(static_cast(p)) { if (_b) { ++_b->count; } } public: using element_type = T; shared_ptr() noexcept = default; shared_ptr(std::nullptr_t) noexcept : shared_ptr() {} 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; } shared_ptr& operator=(std::nullptr_t) noexcept { return *this = shared_ptr(); } 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; } long use_count() const noexcept { if (_b) { return _b->count; } else { return 0; } } template struct make_helper; template friend shared_ptr make_shared(A&&... a); template friend shared_ptr make_shared(U&& 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) { auto p = new T(std::forward(a)...); return shared_ptr(p, p); } }; template inline shared_ptr make_shared(A&&... a) { using helper = shared_ptr_make_helper::value>; return helper::make(std::forward(a)...); } template inline shared_ptr make_shared(T&& a) { using helper = shared_ptr_make_helper::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) { auto q = dynamic_cast(p._p); return shared_ptr(q ? p._b : nullptr, q); } 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() { auto unconst = reinterpret_cast>*>(this); return shared_ptr(unconst); } template inline shared_ptr enable_shared_from_this::shared_from_this() const { auto esft = const_cast(this); auto unconst = reinterpret_cast>*>(esft); return shared_ptr(unconst); } 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(); } template static inline std::ostream& operator<<(std::ostream& out, const shared_ptr& p) { if (!p) { return out << "null"; } return out << *p; } template struct shared_ptr_equal_by_value { bool operator()(const shared_ptr& i1, const shared_ptr& i2) const { if (bool(i1) ^ bool(i2)) { return false; } return !i1 || *i1 == *i2; } }; template struct shared_ptr_value_hash { size_t operator()(const shared_ptr& p) const { if (p) { return std::hash()(*p); } return 0; } }; namespace std { template struct hash> : private hash { size_t operator()(const lw_shared_ptr& p) const { return hash::operator()(p.get()); } }; template struct hash<::shared_ptr> : private hash { size_t operator()(const ::shared_ptr& p) const { return hash::operator()(p.get()); } }; } template struct is_smart_ptr<::shared_ptr> : std::true_type {}; template struct is_smart_ptr<::lw_shared_ptr> : std::true_type {}; #endif /* SHARED_PTR_HH_ */