Hicham said:
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