Topic: Proposal for a 'non-slicing' smart-pointer


Author: sebastian <sebastiangarth@gmail.com>
Date: Fri, 16 Oct 2009 23:30:54 CST
Raw View
Hi,

I'm not sure if something like this has already been suggested or even
perhaps already being considered, but at any rate, it seems that it
would be a useful addition to the standard library. Basically, the
idea is a smart pointer with value-based semantics that provides a
facility for deep-copying of polymorphic types.

The following example I've put together is obviously not complete.
Some of the shortcomings include:

1) It doesn't take advantage of any C++0x features.
2) It doesn't allow storage of a local copy of the allocator.
3) It could perhaps be abstracted to support non-pointer types.
4) It would nice to incorporate support for arrays.

So essentially it's just a rough draft to communicate the basic idea.

#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 that).
*/
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 >( );
 }

#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_;
 }

 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_ );
 }

 Type* orphan( void )
 {
  Type*
   tmp = m_ptr_;
  m_ptr_ = 0;
  return tmp;
 }

 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( );

/*
 Example usage
*/

#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( ),
 /*
  Create a non-sliced copy of mb1
 */
  mb2 = mb1;
 mb2->test( );
 return 0;
}


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: sebastian <sebastiangarth@gmail.com>
Date: Sun, 18 Oct 2009 10:26:53 CST
Raw View
Sorry, there was a minor bug in the code I posted. This is the updated
version:

/*
 A simple "non-slicing" memory manager.
 Note: NOT to be used to manage dynamically-allocated
 arrays (use std::vector or similar for that).
*/
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 )
 {
  return internal_reset( ptr, &internal_allocator< Derived
>::global );
 }

 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 )
 {
  return internal_reset( rhs.m_iab_->clone( rhs.m_ptr_ ),
rhs.m_iab_ );
 }

 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 >( );
 }

#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_;
 }

 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_ );
 }

 Type* orphan( void )
 {
  Type*
   tmp = m_ptr_;
  m_ptr_ = 0;
  return tmp;
 }

 virtual ~value_ptr( void )
 {
  reset( );
 }

 protected:

 void init( void )
 {
  m_ptr_ = 0;
  m_iab_ = &internal_allocator< Type >::global;
 }

 value_ptr& internal_reset( Type* ptr, internal_allocator_base* iab )
 {
 /*
  The swap ensures that if the 'current'
  data's destructor throws an exception,
  the 'new' memory will still be cleaned up.
 */
  std::swap( m_ptr_, ptr );
  std::swap( m_iab_, iab );
  iab->destroy( ptr );
  return *this;
 }

 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( );

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]