//======================================================================= // Copyright Baptiste Wicht 2013-2018. // Distributed under the terms of the MIT License. // (See accompanying file LICENSE or copy at // http://www.opensource.org/licenses/MIT) //======================================================================= #ifndef SHARED_PTR_H #define SHARED_PTR_H #include #include #include namespace std { /*! * \brief A shared_ptr ptr of type T. * * A shared pointer is a reference-counted pointer, with several pointers being * able to point to the same managed object. When the reference counter goes * down to zero, the object is automatically deleted. */ template struct shared_ptr { using pointer_type = T*; ///< The pointer type using reference_type = T&; ///< The reference type using element_type = T; ///< The element type struct control_block_t; /*! * \brief Construct an empty shared_ptr */ constexpr shared_ptr() : ptr(nullptr), control_block(nullptr) {} /*! * \brief Construct an empty shared_ptr */ constexpr explicit shared_ptr(decltype(nullptr)) : ptr(nullptr), control_block(nullptr) {} /*! * \brief Construct a new shared_ptr around the given pointer. * * The pointer will be deleted using the 'delete-expression' */ template explicit shared_ptr(U* ptr) : ptr(ptr) { control_block = new control_block_impl>(ptr); } /*! * \brief Construct a new shared_ptr around the given pointer. * * The pointer will be deleted using 'deleter(ptr)' */ template shared_ptr(U* ptr, Deleter deleter) : ptr(ptr) { control_block = new control_block_impl(ptr, deleter); } /*! * \brief Construct a new shared_ptr directly with a control block */ shared_ptr(T* ptr, control_block_t* control_block, int) : ptr(ptr), control_block(control_block) { //Nothing else to init } /*! * \brief Copy construct a shared_ptr, effectively incrementing the reference counter */ shared_ptr(const shared_ptr& rhs) : ptr(rhs.ptr), control_block(rhs.control_block) { increment(); } /*! * \brief Copy assign a shared_ptr, effectively incrementing the reference counter */ shared_ptr& operator=(const shared_ptr& rhs){ if(this != &rhs){ decrement(); this->ptr = rhs.ptr; this->control_block = rhs.control_block; increment(); } return *this; } /*! * \brief Move construct a shared_ptr. * * This does not change the reference counter since the shared pointer moved from does not point to the object anymore */ shared_ptr(shared_ptr&& rhs) : ptr(rhs.ptr), control_block(rhs.control_block) { rhs.ptr = nullptr; rhs.control_block = nullptr; } /*! * \brief Move assign a shared_ptr * * This does not change the reference counter since the shared pointer moved from does not point to the object anymore */ shared_ptr& operator=(shared_ptr&& rhs){ if(this != &rhs){ decrement(); this->ptr = rhs.ptr; this->control_block = rhs.control_block; rhs.ptr = nullptr; rhs.control_block = nullptr; } return *this; } /*! * \brief Assign a new pointer to the shared pointer. * * The pointer will be deleted using the 'delete-expression' */ template shared_ptr& operator=(U* ptr){ decrement(); this->ptr = ptr; control_block = new control_block_impl>(ptr); } /*! * \brief Resets the shared_ptr value */ shared_ptr& operator=(decltype(nullptr)) { decrement(); this->ptr = nullptr; this->control_block = nullptr; } /*! * \brief Destroy the shared_ptr. This effectively decrement the counter. If the counter reaches 0, * the object is deleted. */ ~shared_ptr(){ decrement(); } /*! * \brief Returns the managed pointer */ pointer_type get() const { return ptr; } /*! * \brief Returns the managed pointer */ pointer_type operator->() const { return get(); } /*! * \brief Returns a reference to the managed object */ reference_type operator*() const { return *get(); } /*! * \brief Converts the shared ptr to a boolean, indicating if it points to something or not */ explicit operator bool() const { return get(); } struct control_block_t { volatile size_t counter; control_block_t() : counter(1) {} control_block_t(const control_block_t& rhs) = delete; control_block_t& operator=(const control_block_t& rhs) = delete; control_block_t(control_block_t&& rhs) = delete; control_block_t& operator=(control_block_t&& rhs) = delete; virtual void destroy() = 0; virtual ~control_block_t(){} }; template struct control_block_impl : control_block_t { U* ptr; Deleter deleter; control_block_impl(U* ptr) : ptr(ptr) {} control_block_impl(U* ptr, Deleter d) : ptr(ptr), deleter(d){} virtual void destroy() override { if(ptr){ deleter(ptr); } } }; template struct inplace_control_block_impl : control_block_t { U ptr; template inplace_control_block_impl(Args&&... args) : ptr(std::forward(args)...){} virtual void destroy() override { // Nothing to do } }; private: /*! * \brief Helper function to decrement the reference counter if the pointer points to a * managed object. * * If the counter goes to zero, this also deallocates the managed object and the control block */ void decrement(){ if(control_block){ if(!__sync_sub_and_fetch(&control_block->counter, 1)){ control_block->destroy(); delete control_block; } } } /*! * \brief Helper function to increment the reference counter if the pointer points to a * managed object. */ void increment(){ if(control_block){ __sync_fetch_and_add(&control_block->counter, 1); } } pointer_type ptr; ///< The managed pointer control_block_t* control_block; ///< Pointer to the control block }; /*! * \brief Creates a new shared_ptr. * * This is effectively more efficient than shared_ptr(new T) since it allocates the object directly in the control * block, saving one allocation. */ template std::shared_ptr make_shared(Args&&... args){ auto cb = new typename std::shared_ptr::template inplace_control_block_impl(std::forward(args)...); return std::shared_ptr{&cb->ptr, cb, 0}; } } //end of namespace std #endif