Re: deep/real copy of derived objects
Hicham Mouline wrote:
"Kai-Uwe Bux" <jkherciueh@gmx.net> wrote in message
news:i5bgel$16h$1@news.doubleSlash.org...
Hicham Mouline wrote:
That depends on the clone_ptr or copy_ptr implementation. The ones
inspired
by shared_ptr usually share its feature of _not_ requiring virtual
destructors.
Actually, how can I use it below?
#include "cloned_ptr.hpp"
#include "Base.hpp"
class Container {
public:
Container(const Base& b)
: base_ptr_( ) //// what to do here?
private:
clone_ptr<Base> base_ptr_;
};
I'd want to do the copy of the most-derived oject referred to by b
without
knowing its type.
The key is not to lose track of type information. When you new() the
object,
the compiler knows, which type it is. At that point, you have to wrap it
so
that this type information is not lost. E.g.:
class Container {
public:
template < typename Derived >
Container ( const Derived & other )
: base_ptr_ ( new Derived ( other ) )
{}
private:
clone_ptr<Base> base_ptr_;
};
All right.
Could I also do this:
class Container {
public:
Container ( const clone_ptr<Base>& other )
: base_ptr_ ( other )
{}
private:
clone_ptr<Base> base_ptr_;
};
granted I use clone_ptr when I use other.
Looks ok.
Would you going with your implementation from Dec 2006, or would you
advise a different one?
I am probably not the right person to give advice about this: the
implementation was a response to a challenge about clone pointers for
incomplete types. Admittedly, I did not find use for it later :-)
Anyway, here is the current version sitting (more or less untested) in my
library (cleaned up a little to remove dependencies on local stuff):
------------------------------------------------
// copy_ptr.cc (C) Kai-Uwe Bux [2007-2010]
// =======================================
/*
The template copy_ptr<T> defines smart pointer with copy
semantics: a pointer assignment copies the pointee. It
supports initialization as
copy_ptr<T> p ( new T ( some args ) );
as well as polymorphic initialization via
copy_ptr<T> p ( new D ( some args ) );
where D is derived from T. In this case, copy construction and
assignment are also supported:
copy_ptr<D> d_ptr ( new D ( some args ) );
copy_ptr<T> t_ptr ( d_ptr );
t_ptr = d_ptr;
No slicing will occur when used according to these idioms.
The template allows for specification of a custom clone method
and a custom deleter. The default cloner does not require T to
have a clone method: it uses copy construction to clone a
pointer.
Note: the type T does not need to be complete.
The interface is closely modelled upon tr1::shared_ptr<>.
// FIXME: [write more]
*/
#include <boost/utility.hpp> // enable_if
#include <tr1/type_traits> // is_base_of
#include <tr1/functional> // function
#include <algorithm> // swap
#include <functional> // less
namespace kubux {
template < typename T >
class copy_ptr {
public:
typedef T value_type;
typedef value_type * pointer;
typedef value_type const * const_pointer;
typedef value_type & reference;
typedef value_type const & const_reference;
// standard deletion and copy strategies
// ======================================
static
void default_delete ( T* p ) {
delete ( p );
}
static
T* default_copy ( T* p ) {
return ( new T (*p) );
}
static
T* default_null ( T* p ) {
return ( 0 );
}
static
void default_do_nothing ( T* p ) {
}
typedef std::tr1::function< void(pointer) > deleter;
typedef std::tr1::function< pointer(pointer) > copier;
private:
pointer the_ptr;
copier the_cln;
deleter the_del;
// support for conversion
// copy_ptr<Derived> to copy_ptr<Base>
// =====================================
template < typename D >
friend class copy_ptr;
/*
The following templates are used to support
initialization of copy_ptr<T> from copy_ptr<D>
where D is derived from T. The idea is that the copier
and deleter object will first upcast the T* member
to a D* and then call the copier/deleter from there.
We use static cast: the code will not compile anyway
unless the assignment D* to T* is valid. In that case,
we may safely assume that upcasting back to D* is ok.
*/
template < typename D >
struct conversion_deleter {
typename copy_ptr<D>::deleter the_deleter;
template < typename P >
conversion_deleter ( P d )
: the_deleter ( d )
{}
void operator() ( pointer ptr ) const {
the_deleter( static_cast<D*>( ptr ) );
}
}; // conversion_deleter
template < typename D >
struct conversion_copier {
typename copy_ptr<D>::copier the_copier;
template < typename P >
conversion_copier ( P c )
: the_copier ( c )
{}
pointer operator() ( pointer ptr ) const {
return( static_cast<pointer>
( the_copier
( static_cast<D*>( ptr ) ) ) );
}
}; // conversion_copier
// pointer casts need to be friends
// ================================
template < typename A, typename B >
friend
copy_ptr<A> static_pointer_cast ( copy_ptr<B> const & );
template < typename A, typename B >
friend
copy_ptr<A> dynamic_pointer_cast ( copy_ptr<B> const & );
template < typename A, typename B >
friend
copy_ptr<A> const_pointer_cast ( copy_ptr<B> const & );
public:
// swap
// ====
void swap ( copy_ptr & other ) {
std::swap( the_cln, other.the_cln );
std::swap( the_del, other.the_del );
std::swap( the_ptr, other.the_ptr );
}
// default constructor [0 pointer]
// ===============================
copy_ptr ( void )
: the_ptr ( 0 )
, the_cln ( &default_null )
, the_del ( &default_do_nothing )
{}
// construction from pointer
// =========================
explicit
copy_ptr ( pointer ptr )
: the_ptr ( ptr )
, the_cln ( ptr == 0 ? &default_null : &default_copy )
, the_del ( ptr == 0 ? &default_do_nothing : &default_delete )
{}
template < typename Cloner >
copy_ptr ( pointer ptr, Cloner c )
: the_ptr ( ptr )
, the_cln ( c )
, the_del ( ptr == 0 ? &default_do_nothing : &default_delete )
{}
template < typename Cloner, typename Deleter >
copy_ptr ( pointer ptr, Cloner c, Deleter d )
: the_ptr ( ptr )
, the_cln ( c )
, the_del ( d )
{}
// copy constructor
// ================
copy_ptr ( copy_ptr const & other )
: the_ptr ( other.the_cln( other.the_ptr ) )
, the_cln ( other.the_cln )
, the_del ( other.the_del )
{}
// constructor variants from derived types
// =======================================
template < typename D >
explicit
copy_ptr ( D* ptr )
: the_ptr ( ptr )
, the_cln ( conversion_copier<D>
( ptr == 0 ?
©_ptr<D>::default_null :
©_ptr<D>::default_copy ) )
, the_del ( conversion_deleter<D>
( ptr == 0 ?
©_ptr<D>::default_do_nothing :
©_ptr<D>::default_delete ) )
{}
template < typename D, typename Cloner >
explicit
copy_ptr ( D* ptr, Cloner c )
: the_ptr ( ptr )
, the_cln ( conversion_copier<D>( c ) )
, the_del ( conversion_deleter<D>
( ptr == 0 ?
©_ptr<D>::default_do_nothing :
©_ptr<D>::default_delete ) )
{}
template < typename D, typename Cloner, typename Deleter >
explicit
copy_ptr ( D* ptr, Cloner c, Deleter d )
: the_ptr ( ptr )
, the_cln ( conversion_copier<D>( c ) )
, the_del ( conversion_deleter<D>( d ) )
{}
/*
We use enable_if to prevent certain conversion
so that comparisions
copy_ptr<Base> == copy_ptr<Derived>
do not have ambigous overloads.
*/
template < typename D >
copy_ptr ( copy_ptr<D> const & other,
typename boost::enable_if<
std::tr1::is_base_of<value_type,D>,
void* >::type dummy = 0 )
: the_ptr ( other.the_cln( other.the_ptr ) )
, the_cln ( conversion_copier<D>( other.the_cln ) )
, the_del ( conversion_deleter<D>( other.the_del ) )
{}
// destructor
// ==========
~copy_ptr ( void ) {
the_del( the_ptr );
}
// copy-swap assignment operators
// ==============================
copy_ptr & operator= ( copy_ptr const & other ) {
copy_ptr dummy ( other );
swap( dummy );
return ( *this );
}
template < typename D >
copy_ptr & operator= ( copy_ptr<D> const & other ) {
copy_ptr dummy ( other );
swap( dummy );
return ( *this );
}
// dereferencing operators
// =======================
pointer operator-> ( void ) const {
return ( the_ptr );
}
reference operator* ( void ) const {
return ( *the_ptr );
}
// observers
// =========
operator bool ( void ) const {
return ( get() != 0 );
}
pointer get ( void ) {
return ( the_ptr );
}
const_pointer get ( void ) const {
return ( the_ptr );
}
template < typename Cloner >
friend
Cloner * get_copier ( copy_ptr & c_ptr ) {
return ( c_ptr.the_del.template target<Cloner>() );
}
template < typename Cloner >
friend
Cloner const * get_copier ( copy_ptr const & c_ptr ) {
return ( c_ptr.the_del.template target<Cloner>() );
}
template < typename Deleter >
friend
Deleter * get_deleter ( copy_ptr & c_ptr ) {
return ( c_ptr.the_del.template target<Deleter>() );
}
template < typename Deleter >
friend
Deleter const * get_deleter ( copy_ptr const & c_ptr ) {
return ( c_ptr.the_del.template target<Deleter>() );
}
// modifiers
// =========
void reset ( T* ptr ) {
shared_ptr ( ptr ).swap( *this );
}
template < typename D >
void reset ( D* ptr ) {
shared_ptr( ptr ).swap( *this );
}
template < typename D, typename Cloner >
void reset ( D* ptr, Cloner c ) {
shared_ptr( ptr, c ).swap( *this );
}
template < typename D, typename Cloner, typename Deleter >
void reset ( D* ptr, Cloner c, Deleter d ) {
shared_ptr( ptr, c, d ).swap( *this );
}
// null pointer constant
// =====================
static
copy_ptr const & nil ( void ) {
static copy_ptr const nil_ptr;
return nil_ptr;
}
}; // copy_ptr<>
// specialized algorithms
template < typename T >
void swap ( copy_ptr<T> & a, copy_ptr<T> & b ) {
a.swap( b );
}
// comparison operators
// ====================
template < typename A, typename B >
bool operator== ( copy_ptr<A> const & lhs,
copy_ptr<B> const & rhs ) {
return ( lhs.get() == rhs.get() );
}
template < typename A, typename B >
bool operator!= ( copy_ptr<A> const & lhs,
copy_ptr<B> const & rhs ) {
return ( lhs.get() != rhs.get() );
}
template < typename A, typename B >
bool operator< ( copy_ptr<A> const & lhs,
copy_ptr<B> const & rhs ) {
return ( std::less<void*>()
( static_cast<void*>(lhs.get()),
static_cast<void*>(rhs.get()) ) );
}
// pointer casts (friends)
// =======================
template < typename A, typename B >
copy_ptr<A> static_pointer_cast ( copy_ptr<B> const & b_ptr ) {
if ( b_ptr ) {
A* a_ptr = static_cast<A*>( b_ptr.the_cln( b_ptr.the_ptr ) );
return
copy_ptr<A>
( a_ptr,
typename copy_ptr<A>::template
conversion_copier<B>( b_ptr.the_cln ),
typename copy_ptr<A>::template
conversion_deleter<B>( b_ptr.the_del ) );
} else {
return ( copy_ptr<A>() );
}
}
template < typename A, typename B >
copy_ptr<A> dynamic_pointer_cast ( copy_ptr<B> const & b_ptr ) {
B* b_copy ( b_ptr.the_cln( b_ptr.the_ptr ) );
A* a_ptr = dynamic_cast<A*>( b_copy.get() );
if ( a_ptr == 0 ) {
b_ptr.the_del( b_copy );
return copy_ptr<A>();
} else {
return
copy_ptr<A>
( a_ptr,
typename copy_ptr<A>::template
conversion_copier<B>( b_ptr.the_cln ),
typename copy_ptr<A>::template
conversion_deleter<B>( b_ptr.the_del ) );
}
}
template < typename A, typename B >
copy_ptr<A> const_pointer_cast ( copy_ptr<B> const & b_ptr ) {
if ( b_ptr ) {
A* a_ptr = const_cast<A*>( b_ptr.the_cln( b_ptr.the_ptr ) );
return
copy_ptr<A>
( a_ptr,
typename copy_ptr<A>::template
conversion_copier<B>( b_ptr.the_cln ),
typename copy_ptr<A>::template
conversion_deleter<B>( b_ptr.the_del ) );
} else {
return ( copy_ptr<A>() );
}
}
} // namespace kubux
// end of file
------------------------------------------
If you decide to try it, I would be very interesting to hear about wether it
works well for you or what the problems are.
Best
Kai-Uwe Bux