/* * memory.hpp * * This file is part of Algorithms library toolkit. * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz) * Algorithms library toolkit is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * Algorithms library toolkit is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with Algorithms library toolkit. If not, see <http://www.gnu.org/licenses/>. * * Created on: May 1, 2015 * Author: Jan Travnicek */ #ifndef __MEMORY_HPP_ #define __MEMORY_HPP_ #include <memory> #include <utility> #include "compare.hpp" #include "type_traits.hpp" #include "clone.hpp" namespace ext { /** * \brief * Predeclaration of copy on write shared ptr class. */ template < typename T, typename Enable = void > class cow_shared_ptr; /** * \brief * Base class of classes used inside copy on write pointers. */ class cow_shared_ptr_base { public: /** * Constructor of the copy on write shared ptr base class. The use count is initialized to zero. */ cow_shared_ptr_base ( ) : m_UseCount ( 0 ) { } private: /** * Caries the number of uses of this instance as a managed pointer inside copy on write pointer. */ unsigned m_UseCount; /** * To make m_UseCount accessible from copy on write pointer class. */ template < typename T, typename Enable > friend class cow_shared_ptr; }; /** * \brief * Specialisation of copy on write pointer for classes based with copy on write pointer base. * * The class is essentially mimicking the behavior of shared_ptr. Additionaly the use of dereference (either through operator* or operator->) in non-constant context causes the managed pointer to ensure the referenced object is referenced from this class only. * * The class also uniques the referenced instances when they are equal. * * \tparam T the type of managed objects */ template < class T > class cow_shared_ptr < T, typename std::enable_if < std::is_base_of < cow_shared_ptr_base, T >::value >::type > { public: /** * \brief * Default initialization to null. */ explicit cow_shared_ptr ( ) { attach ( NULL ); } /** * \brief * Constructor which takes ownership of the provided pointer. */ explicit cow_shared_ptr ( T * data ) { if ( data ) data->m_UseCount = 0; attach ( data ); } /** * \brief * Copy constructor to create new instance of shared pointer reffering the same data. * * \param other the source instance */ cow_shared_ptr ( const cow_shared_ptr & other ) { attach ( other.m_Data ); } /** * \brief * Move constructor to create new instance of shared pointer reffering the same data. * * \param other the source instance */ cow_shared_ptr ( cow_shared_ptr && other ) noexcept { m_Data = other.m_Data; other.m_Data = NULL; } /** * \brief * The destructor of the shared pointer. */ ~cow_shared_ptr ( ) noexcept { detach ( ); } /** * \brief * Copy assignment operator to change reffered instace to source one. * * \param other the source instance */ cow_shared_ptr & operator =( const cow_shared_ptr & other ) { if ( this == & other ) return * this; detach ( ); attach ( other.m_Data ); return * this; } /** * \brief * Move assignment operator to change reffered instace to source one. * * \param other the source instance */ cow_shared_ptr & operator =( cow_shared_ptr && other ) noexcept { swap ( * this, other ); return * this; } /** * \brief * Operator arrow to chain dereferece to inner managed pointer. * * \return the managed pointer */ T * operator ->( ) { make_unique ( ); return m_Data; } /** * \brief * Operator arrow to chain dereferece to inner managed pointer. * * \return the managed pointer */ T const * operator ->( ) const { return m_Data; } /** * \brief * Operator dereference to access the inner managed pointer. * * \return reference to managed data */ T & operator *( ) { make_unique ( ); return * m_Data; } /** * \brief * Operator dereference to access the inner managed pointer. * * \return reference to managed data */ T const & operator *( ) const { return * m_Data; } /** * Getter of the raw managed pointer. * * \return the managed pointer */ T * get ( ) { make_unique ( ); return m_Data; } /** * Getter of the raw managed pointer. * * \return the managed pointer */ T const * get ( ) const { return m_Data; } /** * \brief * Tests whether the managed pointer is referenced from one location only (or the class stores null pointer). * * \return bool true if the managed pointer is referenced from one location only, false otherwise */ bool unique ( ) const { return m_Data == NULL || m_Data->m_UseCount == 1; } /** * \brief * Getter of the number how many times the managed pointer is referenced. * * \return the use count */ unsigned getUseCount ( ) const { if ( m_Data == NULL ) return 0; return m_Data->m_UseCount; } /** * Tests the instance whether the managed pointer is valid * * \return true if the managed pointer is not null and the use count is nonzero, false otherwise */ explicit operator bool( ) const { return getUseCount ( ) != 0; } private: /** * \brief * Changes the managed pointer and increases its use count * * \param data the new value of managed pointer */ void attach ( T * data ) { m_Data = data; if ( m_Data ) m_Data->m_UseCount++; } /** * \brief * Decreases the use count of managed pointer, if needed the pointer is freed. Managed pointer stored in this instance is set to null. */ void detach ( ) { if ( m_Data && ( --( m_Data->m_UseCount ) <= 0 ) ) delete m_Data; m_Data = NULL; } /** * \brief * Ensures the managed pointer is unique by copy constructing the managed instance if needed. */ void make_unique ( ) { if ( unique ( ) ) return; T * tmp = m_Data; detach ( ); tmp = ext::clone ( * tmp ); tmp->m_UseCount = 0; attach ( tmp ); } /** * \brief * The managed pointer */ T * m_Data; /** * \brief * Specialisation of swap method to copy on write shared pointers. * * \param first the first instance * \param second the second instance */ friend void swap ( cow_shared_ptr & first, cow_shared_ptr & second ) { T * tmp = first.m_Data; first.m_Data = second.m_Data; second.m_Data = tmp; } }; /** * \brief * Specialisation of copy on write pointer for classes not based with copy on write pointer base. * * The class is essentially mimicking the behavior of shared_ptr. Additionaly the use of dereference (either through operator* or operator->) in non-constant context causes the managed pointer to ensure the referenced object is referenced from this class only. * * The class also uniques the referenced instances when they are equal. * * \tparam T the type of managed objects */ template < class T > class cow_shared_ptr < T, typename std::enable_if < !std::is_base_of < cow_shared_ptr_base, T >::value >::type > { /** * \brief * The inner structure to hold managed pointer and its use count (the number how many times it is referenced). */ struct cow_shared_ptr_data { /** * \brief * The managed pointer. */ T * m_Data; /** * Caries the number of uses of this instance as a managed pointer inside copy on write pointer. */ unsigned m_UseCount; /** * \brief * Contructs the inner shared resource based on the managed pointer. * * The class takes ownership of the provided pointer */ cow_shared_ptr_data ( T * data ) : m_Data ( data ), m_UseCount ( 0 ) { } /** * \brief * Destructor to free the managed pointer. */ ~cow_shared_ptr_data ( ) { delete m_Data; } /** * Disable copy construction. */ cow_shared_ptr_data ( const cow_shared_ptr_data & ) = delete; /** * Disable copy operator of assignment. */ cow_shared_ptr_data & operator = ( const cow_shared_ptr_data & ) = delete; /** * Disable move construction. */ cow_shared_ptr_data ( cow_shared_ptr_data && ) = delete; /** * Disable move operator of assignment. */ cow_shared_ptr_data & operator = ( cow_shared_ptr_data && ) = delete; }; public: /** * \brief * Default initialization to null. */ explicit cow_shared_ptr ( ) { attach ( NULL ); } /** * \brief * Constructor which takes ownership of the provided pointer. */ explicit cow_shared_ptr ( T * data ) { attach ( data ? new cow_shared_ptr_data ( data ) : NULL ); } /** * \brief * Copy constructor to create new instance of shared pointer reffering the same data. * * \param other the source instance */ cow_shared_ptr ( const cow_shared_ptr & other ) { attach ( other.m_Data ); } /** * \brief * Move constructor to create new instance of shared pointer reffering the same data. * * \param other the source instance */ cow_shared_ptr ( cow_shared_ptr && other ) noexcept { m_Data = other.m_Data; other.m_Data = NULL; } /** * \brief * The destructor of the shared pointer. */ ~cow_shared_ptr ( ) noexcept { detach ( ); } /** * \brief * Copy assignment operator to change reffered instace to source one. * * \param other the source instance */ cow_shared_ptr & operator =( const cow_shared_ptr & other ) { if ( this == & other ) return * this; detach ( ); attach ( other.m_Data ); return * this; } /** * \brief * Move assignment operator to change reffered instace to source one. * * \param other the source instance */ cow_shared_ptr & operator =( cow_shared_ptr && other ) noexcept { swap ( * this, other ); return * this; } /** * \brief * Operator arrow to chain dereferece to inner managed pointer. * * \return the managed pointer */ T * operator ->( ) { make_unique ( ); return m_Data->m_Data; } /** * \brief * Operator arrow to chain dereferece to inner managed pointer. * * \return the managed pointer */ T const * operator ->( ) const { return m_Data->m_Data; } /** * \brief * Operator dereference to access the inner managed pointer. * * \return reference to managed data */ T & operator *( ) { make_unique ( ); return * ( m_Data->m_Data ); } /** * \brief * Operator dereference to access the inner managed pointer. * * \return reference to managed data */ T const & operator *( ) const { return * ( m_Data->m_Data ); } /** * \brief * Getter of the raw managed pointer. * * \return the managed pointer */ T * get ( ) { make_unique ( ); return m_Data->m_Data; } /** * \brief * Getter of the raw managed pointer. * * \return the managed pointer */ T const * get ( ) const { return m_Data->m_Data; } /** * \brief * Tests whether the managed pointer is referenced from one location only (or the class stores null pointer). * * \return bool true if the managed pointer is referenced from one location only, false otherwise */ bool unique ( ) const { return m_Data == NULL || m_Data->m_UseCount == 1; } /** * \brief * Getter of the number how many times the managed pointer is referenced. * * \return the use count */ unsigned getUseCount ( ) const { if ( m_Data == NULL ) return 0; return m_Data->m_UseCount; } /** * \brief * Tests the instance whether the managed pointer is valid * * \return true if the managed pointer is not null and the use count is nonzero, false otherwise */ explicit operator bool( ) const { return getUseCount ( ) != 0; } private: /** * \brief * Changes the managed pointer and increases its use count * * \param data the new value of managed pointer */ void attach ( typename cow_shared_ptr < T >::cow_shared_ptr_data * data ) { m_Data = data; if ( m_Data ) m_Data->m_UseCount++; } /** * \brief * Decreases the use count of managed pointer, if needed the pointer is freed. Managed pointer stored in this instance is set to null. */ void detach ( ) { if ( m_Data && ( --( m_Data->m_UseCount ) <= 0 ) ) delete m_Data; m_Data = NULL; } /** * \brief * Ensures the managed pointer is unique by copy constructing the managed instance if needed. */ void make_unique ( ) { if ( unique ( ) ) return; typename cow_shared_ptr < T >::cow_shared_ptr_data * tmp = m_Data; detach ( ); attach ( new cow_shared_ptr_data ( ext::clone ( * tmp->m_Data ) ) ); } /** * \brief * The instance of sturucture containing managed pointer and its use count. */ cow_shared_ptr_data * m_Data; /** * \brief * Specialisation of swap method to copy on write shared pointers. * * \param first the first instance * \param second the second instance */ friend void swap ( cow_shared_ptr & first, cow_shared_ptr & second ) { typename cow_shared_ptr < T >::cow_shared_ptr_data * tmp = first.m_Data; first.m_Data = second.m_Data; second.m_Data = tmp; } }; /** * \brief * Managed pointer simulating value like behavior. * * The class is supposed to be similar to unique_ptr but allows copying of managed instance by clone method or its copy constructor. * * \tparam T type of managed instance */ template < class T > class smart_ptr { /** * \brief * Pointer to managed object. */ T * m_Data; public: /** * \brief * Default initialization to null. */ explicit smart_ptr ( ) : m_Data ( NULL ) { } /** * \brief * Constructor which takes ownership of the provided pointer. */ explicit smart_ptr ( T * data ) : m_Data ( data ) { } /** * \brief * Conversion constructor to simplify type casting. * * \tparam R the type of the managed resource of the source smart pointer * * \param other the source instance */ template < class R > smart_ptr ( smart_ptr < R > other ) : m_Data ( other.release ( ) ) { } /** * \brief * Copy constructor of the smart pointer. Internally uses clone (if available) or copy constructor of the managed resource. * * \param other the source instance */ smart_ptr ( const smart_ptr & other ) : m_Data ( ext::clone ( * other.m_Data ) ) { } /** * \brief * Move constructor of the smart pointer. Passes ownership of the managed resource from source to constructed instance * * \param other the source instance */ smart_ptr ( smart_ptr && other ) noexcept : m_Data ( other.release ( ) ) { } /** * \brief * The destructor of the shared pointer, responsible for freeing the managed resource. */ ~smart_ptr ( ) noexcept { delete m_Data; } /** * \brief * Copy operator of assignment. Internally uses clone (if available) or copy constructor of the managed resource. * * \param other the source instance */ smart_ptr & operator =( const smart_ptr & other ) { if ( this == & other ) return * this; delete m_Data; m_Data = ext::clone ( * other.m_Data ); return * this; } /** * \brief * Copy operator of assignment. Passes ownership of the managed resource from source to this instance. * * \param other the source instance */ smart_ptr & operator =( smart_ptr && other ) noexcept { using std::swap; swap ( this->m_Data, other.m_Data ); return * this; } /** * \brief * Operator arrow to chain dereferece to inner managed pointer. * * \return the managed pointer */ T * operator ->( ) { return m_Data; } /** * \brief * Operator arrow to chain dereferece to inner managed pointer. * * \return the managed pointer */ T * operator ->( ) const { return m_Data; } /** * \brief * Operator dereference to access the inner managed pointer. * * \return reference to managed data */ T & operator *( ) { return * m_Data; } /** * \brief * Operator dereference to access the inner managed pointer. * * \return reference to managed data */ T & operator *( ) const { return * m_Data; } /** * \brief * Getter of the raw managed pointer. * * \return the managed pointer */ T * get ( ) { return m_Data; } /** * \brief * Getter of the raw managed pointer. * * \return the managed pointer */ T * get ( ) const { return m_Data; } /** * \brief * Releases the shared resource and returns it. * * \return the released shared resource */ T * release ( ) { T * res = m_Data; m_Data = nullptr; return res; } /** * \brief * Tests the instance whether the managed pointer is valid * * \return true if the managed pointer is not null, false otherwise */ explicit operator bool( ) const { return ( bool ) m_Data; } }; /** * \brief * Operator to print the copy on write shared pointer to the output stream. * * \param out the output stream * \param map the copy on write shared pointer to print * * \tparam T the type of managed instances inside the copy on write shared pointer * * \return the output stream from the \p out */ template < class T > std::ostream & operator <<( std::ostream & out, const ext::cow_shared_ptr < T > & ptr ) { out << * ptr; return out; } /** * \brief * Operator to print the smart pointer to the output stream. * * \param out the output stream * \param map the smart pointer to print * * \tparam T the type of managed instances inside the smart pointer * * \return the output stream from the \p out */ template < class T > std::ostream & operator <<( std::ostream & out, const ext::smart_ptr < T > & ptr ) { out << * ptr; return out; } /** * \brief * Specialisation of the compare structure implementing the three-way comparison * * \tparam T the type of managed resource inside the copy on write shared pointer */ template < class T > struct compare < ext::cow_shared_ptr < T > > { /** * \brief * Implementation of the three-way comparison * * \param first the left operand of the comparison * \param second the right operand of the comparison * * \return negative value of left < right, positive value if left > right, zero if left == right */ int operator ()( const ext::cow_shared_ptr < T > & first, const ext::cow_shared_ptr < T > & second ) const { if ( first.get ( ) == second.get ( ) ) return 0; if ( !first ) return -1; if ( !second ) return 1; static compare < typename std::decay < T >::type > comp; return comp ( * first, * second ); } }; /** * \brief * Specialisation of the compare structure implementing the three-way comparison * * \tparam T the type of managed resource inside the shared pointer */ template < class T > struct compare < std::shared_ptr < T > > { /** * \brief * Implementation of the three-way comparison * * \param first the left operand of the comparison * \param second the right operand of the comparison * * \return negative value of left < right, positive value if left > right, zero if left == right */ int operator ()( const std::shared_ptr < T > & first, const std::shared_ptr < T > & second ) const { if ( first.get ( ) == second.get ( ) ) return 0; if ( !first ) return -1; if ( !second ) return 1; static compare < typename std::decay < T >::type > comp; return comp ( * first, * second ); } }; /** * \brief * Specialisation of the compare structure implementing the three-way comparison * * \tparam T the type of managed resource inside the unique pointer */ template < class T > struct compare < std::unique_ptr < T > > { /** * \brief * Implementation of the three-way comparison * * \param first the left operand of the comparison * \param second the right operand of the comparison * * \return negative value of left < right, positive value if left > right, zero if left == right */ int operator ()( const std::unique_ptr < T > & first, const std::unique_ptr < T > & second ) const { if ( first.get ( ) == second.get ( ) ) return 0; if ( !first ) return -1; if ( !second ) return 1; static compare < typename std::decay < T >::type > comp; return comp ( * first, * second ); } }; /** * \brief * Specialisation of the compare structure implementing the three-way comparison * * \tparam T the type of managed resource inside the smart pointer */ template < class T > struct compare < ext::smart_ptr < T > > { /** * Implementation of the three-way comparison * * \param first the left operand of the comparison * \param second the right operand of the comparison * * \return negative value of left < right, positive value if left > right, zero if left == right */ int operator ()( const ext::smart_ptr < T > & first, const ext::smart_ptr < T > & second ) const { if ( first.get ( ) == second.get ( ) ) return 0; if ( !first ) return -1; if ( !second ) return 1; static compare < typename std::decay < T >::type > comp; return comp ( * first, * second ); } }; } /* namespace ext */ #endif /* __MEMORY_HPP_ */