Thread: Comments on value_ptr class?

Threaded View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708

    Comments on a value_ptr class?

    I'm revisting an idea that was brought up a few days ago about a non-slicing smart-pointer.

    It occurred to me that this might be a pretty useful addition to the STL, and so I was hoping that some of you here might be interested in helping to develop it into something that could eventually be submitted as a recommendation to the C++ standards commitee?

    At any rate, all comments and suggestions are welcome. Here is the current implementation:

    Code:
    
    #include <memory > // for std::allocator
    #include <algorithm > // for std::swap
    
    /*
    	A simple "non-slicing" memory manager. 
    	Note: NOT to be used to manage dynamically-allocated 
    	arrays (use std::vector or similar for arrays)
    */
    template < typename Type, typename Allocator = std::allocator< Type > > 
    class value_ptr 
    {
    	protected:
    	
    	class internal_allocator_base
    	{
    		public:
    		
    		virtual Type* clone( Type const* ) const = 0;
    		
    		virtual void destroy( Type* ) const = 0;
    	};
    	
    	template < typename Derived >
    	class internal_allocator : public internal_allocator_base
    	{
    		public:	
    	
    		virtual Type* clone( Type const* ptr ) const
    		{
    			if( ptr == 0 )
    				return 0;
    			Derived* 
    				val = allocator.allocate( 1 );
    			try
    			{
    				allocator.construct( val, *static_cast< Derived const* >( ptr ) );
    			}
    			catch( ... )
    			{
    				allocator.deallocate( val, 1 );
    				throw;
    			}
    			return val;
    		}		
    		
    		virtual void destroy( Type* ptr ) const
    		{
    			if( ptr != 0 )
    			{
    				Derived* 
    					tmp = static_cast< Derived* >( ptr );
    				try
    				{
    					allocator.destroy( tmp );
    				}
    				catch( ... )
    				{
    					allocator.deallocate( tmp, 1 );
    					throw;
    				}				
    				allocator.deallocate( tmp, 1 );			
    			}	
    		}		
    		
    		static internal_allocator 
    			global;
    		static typename Allocator::template rebind< Derived >::other 
    			allocator;		
    	};
    
    	public:
    
    	template < typename Derived >
    	value_ptr( Derived* ptr )
    	{	
    		init( );
    		reset( ptr );
    	}
    	
    	value_ptr( Type* ptr = 0 )
    	{	
    		init( );
    		reset( ptr );
    	}	
    
     	value_ptr( value_ptr const& rhs )
    	{	
    		init( );
    		reset( rhs );
    	}
    	
    	template < typename Derived >
    	value_ptr& reset( Derived* ptr )
    	{
    		internal_reset( ptr );
    		m_iab_ = &internal_allocator< Derived >::global;
    		return *this;
    	}
    	
    	template < typename Derived >
    	inline value_ptr& operator = ( Derived* ptr )
    	{
    		return reset( ptr );
    	}
    	
    	inline value_ptr& reset( Type* ptr = 0 )
    	{
    		return reset< Type >( ptr );
    	}
    	
     	inline value_ptr& operator = ( Type* ptr )
    	{
    		return reset( ptr );
    	}		
    	
     	value_ptr& reset( value_ptr const& rhs )
    	{		
    		internal_reset( rhs.m_iab_->clone( rhs.m_ptr_ ) );		
    		m_iab_ = rhs.m_iab_;
    		return *this;
    	}
    
    	inline value_ptr& operator = ( value_ptr const& rhs )
    	{	
    		return reset( rhs );
    	}
    	
    	template < typename Derived >
    	value_ptr& allocate( Derived const& val = Derived( ) )
    	{
    		return reset
    		( 
    			static_cast< Derived* >
    			( 
    				internal_allocator< Derived >::global.clone( &val ) 
    			)
    		);
    	}
    
    	inline value_ptr& allocate( void )
    	{
    		return allocate< Type >( );
    	}	
    	
    	friend void swap( value_ptr& lhs, value_ptr& rhs )
    	{
    		std::swap( lhs.m_ptr_, rhs.m_ptr_ );
    		std::swap( lhs.m_iab_, rhs.m_iab_ );
    	}
    
    #ifdef VALUE_PTR_NO_IMPLICIT_CONVERSION
    	inline Type& operator * ( void )
    	{
    		return *m_ptr_;
    	}
    
    	inline Type const& operator * ( void ) const
    	{
    		return *m_ptr_;
    	}
    #else 	
    	inline operator Type* ( void )
    	{
    		return m_ptr_;
    	}
    
    	inline operator Type const* ( void ) const
    	{
    		return m_ptr_;
    	}
    #endif
    	
    	inline Type* operator -> ( void )
    	{
    		return m_ptr_;
    	}
    
    	inline Type const* operator -> ( void ) const
    	{
    		return m_ptr_;
    	}
    		
    	virtual ~value_ptr( void )
    	{
    		reset( );
    	}	
     
    	protected:
    	
    	void init( void )
    	{
    		m_ptr_ = 0;
    		m_iab_ = &internal_allocator< Type >::global;
    	}	
    	
    	void internal_reset( Type* ptr )
    	{
    	/*
    		The temporary ensures that if the 'current' 
    		data's destructor throws an exception, 
    		the 'new' memory will still be cleaned up.
    	*/	
    		Type* 
    			saved = m_ptr_;
    		m_ptr_ = ptr;
    		m_iab_->destroy( saved );
    	}
    
    	Type* 
    		m_ptr_;
    	internal_allocator_base* 
    		m_iab_;
    };
    
    template < typename Type, typename Allocator > 
    template < typename Derived > 
    typename value_ptr< Type, Allocator >::template internal_allocator< Derived >
    	value_ptr< Type, Allocator >::internal_allocator< Derived >::global 
    	= typename value_ptr< Type, Allocator >::template internal_allocator< Derived >( ); 	
    template < typename Type, typename Allocator > 
    template < typename Derived > 
    typename Allocator::template rebind< Derived >::other 
    	value_ptr< Type, Allocator >::internal_allocator< Derived >::allocator	
    	= typename Allocator::template rebind< Derived >::other( );
    And a simple test program:

    Code:
    #include <iostream>
    
    class base
    {
        public:
        
        void print( char const* msg )
        {
            std::cout 
            << reinterpret_cast< int* >( this ) 
            << " : " << msg << std::endl;
        }
        
        base( void )
        {
            print( "base::base( )" );
        }
        
        base( base const& )
        {
            print( "base::base( base const& )" );
        }    
        
        virtual void test( void )
        {
            print( "base::test( )" );
        }
        
        ~base( void )
        {
            print( "base::~base( )" );
        }
    };
    
    class derived : public base
    {
        public:
        
        derived( void )
        {
            print( "derived::derived( )" );
        }
    
        derived( derived const& rhs )
        : base( rhs )
        {
            print( "derived::derived( derived const& )" );
        }        
        
        virtual void test( void )
        {
            print( "derived::test( )" );
        }
        
        ~derived( void )
        {
            print( "derived::~derived( )" );
        }    
    };
    
    int main( void )
    {
        value_ptr< base >
            mb1 = new derived( ),      
            mb2 = mb1;       
        mb2->test( );            
        return 0;
    }
    One thing I couldn't decide on was whether or not to give each instance a copy of it's allocator. It would certainly be a little more flexible (and more in line with other STL objects), but it just seemed so wasteful for a single object, so I've left it out for now.
    Last edited by Sebastiani; 10-15-2009 at 05:29 PM. Reason: fixed value_ptr::allocate, corrected static definition

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  2. Creating a database
    By Shamino in forum Game Programming
    Replies: 19
    Last Post: 06-10-2007, 01:09 PM
  3. Need help to build network class
    By weeb0 in forum C++ Programming
    Replies: 0
    Last Post: 02-01-2006, 11:33 AM
  4. structure vs class
    By sana in forum C++ Programming
    Replies: 13
    Last Post: 12-02-2002, 07:18 AM