473,830 Members | 2,114 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

copy_ptr: request for code review.

Hi folks,
in another thread there is some discussion about cloning smart pointers. So
I started thinking about it and stumbled upon a rather simple generic
implementation. However, it uses dynamic_cast<>, and I am not really
familiar with that cast. So, I am not sure whether the code is correct.
Please have a look and let me know how to improve this.

Sorry for the long post

Thanks

Kai-Uwe Bux


// copy_ptr.cc (C) Kai-Uwe Bux [2005]
// =============== =============== ====

/*
| - Upon construction, copy_ptr<T> takes pointee ownership
| a D* where D is a type derived from T.
| - In any copy construction or assignment a copy of the
| pointee is created. There should be no slicing.
| - Upon destruction the pointee is destroyed.
| - Intended use is within STL containers.
*/

// we swap:
#include <algorithm>

// The clone function:
template < typename T, typename D >
T * clone ( T * ptr ) {
return( new D ( *( dynamic_cast<D* >( ptr ) ) ) );
}

// forward declarations:
template < typename T >
class copy_ptr;

template < typename T >
void swap ( copy_ptr< T > &, copy_ptr< T > & );
// implementation:
template < typename T >
class copy_ptr {

friend void swap<> ( copy_ptr<T> &, copy_ptr<T> & );

/*
The idea is that in addition to a pointer, we also need
a pointer to the appropriate clone function.
*/
T * raw_ptr;
T * ( *clone_fct ) ( T * );

public:

copy_ptr ( void )
: raw_ptr ( new T )
, clone_fct( clone<T,T> )
{}

template < typename D >
copy_ptr ( D * ptr )
: raw_ptr ( ptr )
, clone_fct ( clone<T,D> )
{}

// copy construction clones:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( other.clone_fct ( other.raw_ptr ) )
, clone_fct ( other.clone_fct )
{}

// destruction frees the pointee
~copy_ptr ( void ) {
delete( raw_ptr );
}

// assignment reduces to copy construction:
copy_ptr & operator= ( copy_ptr const & other ) {
copy_ptr dummy ( other );
swap( *this, dummy );
return( *this );
}

T const * operator-> ( void ) const {
return( raw_ptr );
}

T * operator-> ( void ) {
return( raw_ptr );
}

T const & operator* ( void ) const {
return( *raw_ptr );
}

T & operator* ( void ) {
return( *raw_ptr );
}

}; // copy_ptr<T>

template < typename T >
void swap ( copy_ptr< T > & p, copy_ptr< T > & q ) {
std::swap( p.raw_ptr, q.raw_ptr );
std::swap( p.clone_fct, p.clone_fct );
}
// a sanity check:

#include <iostream>

struct Base {

Base ( void ) {
std::cout << "base is born.\n";
}

Base ( Base const & other ) {
std::cout << "base is copied.\n";
}

virtual ~Base () {
std::cout << "base dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "base" );
}

};

std::ostream & operator<< ( std::ostream & ostr,
Base const & obj ) {
return( obj.dump( ostr ) );
}

struct Derived : public Base {

Derived ( void ) {
std::cout << "derived is born.\n";
}

Derived ( Derived const & other )
: Base ( other )
{
std::cout << "derived is copied.\n";
}

virtual ~Derived () {
std::cout << "derived dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "derived" );
}

};

int main ( void ) {
copy_ptr< Base > a_ptr;
copy_ptr< Base > b_ptr ( new Derived() );

std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

a_ptr = b_ptr;
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

}

Aug 22 '05 #1
8 1694

Kai-Uwe Bux wrote:
Hi folks,
in another thread there is some discussion about cloning smart pointers. So
I started thinking about it and stumbled upon a rather simple generic
implementation. However, it uses dynamic_cast<>, and I am not really
familiar with that cast. So, I am not sure whether the code is correct.
Please have a look and let me know how to improve this.

The code appears to be correct and usable, but perhaps can be improved.


Sorry for the long post

Thanks

Kai-Uwe Bux


// copy_ptr.cc (C) Kai-Uwe Bux [2005]
// =============== =============== ====

/*
| - Upon construction, copy_ptr<T> takes pointee ownership
| a D* where D is a type derived from T.
| - In any copy construction or assignment a copy of the
| pointee is created. There should be no slicing.
| - Upon destruction the pointee is destroyed.
| - Intended use is within STL containers.
*/

// we swap:
#include <algorithm>

// The clone function:
template < typename T, typename D >
T * clone ( T * ptr ) {
return( new D ( *( dynamic_cast<D* >( ptr ) ) ) );
}
no check to see if dynamic_cast was successful?

// forward declarations:
template < typename T >
class copy_ptr;

template < typename T >
void swap ( copy_ptr< T > &, copy_ptr< T > & );
// implementation:
template < typename T >
class copy_ptr {

friend void swap<> ( copy_ptr<T> &, copy_ptr<T> & );

/*
The idea is that in addition to a pointer, we also need
a pointer to the appropriate clone function.
*/
T * raw_ptr;
T * ( *clone_fct ) ( T * );

public:

copy_ptr ( void )
: raw_ptr ( new T )
, clone_fct( clone<T,T> )
{}

template < typename D >
copy_ptr ( D * ptr )
: raw_ptr ( ptr )
, clone_fct ( clone<T,D> )
{}

// copy construction clones:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( other.clone_fct ( other.raw_ptr ) )
, clone_fct ( other.clone_fct )
{}

// destruction frees the pointee
~copy_ptr ( void ) {
delete( raw_ptr );
}

// assignment reduces to copy construction:
copy_ptr & operator= ( copy_ptr const & other ) {
copy_ptr dummy ( other );
swap( *this, dummy );
return( *this );
}

T const * operator-> ( void ) const {
return( raw_ptr );
}

T * operator-> ( void ) {
return( raw_ptr );
}

T const & operator* ( void ) const {
return( *raw_ptr );
}

T & operator* ( void ) {
return( *raw_ptr );
}

}; // copy_ptr<T>

template < typename T >
void swap ( copy_ptr< T > & p, copy_ptr< T > & q ) {
std::swap( p.raw_ptr, q.raw_ptr );
std::swap( p.clone_fct, p.clone_fct );
}
// a sanity check:

#include <iostream>

struct Base {

Base ( void ) {
std::cout << "base is born.\n";
}

Base ( Base const & other ) {
std::cout << "base is copied.\n";
}

virtual ~Base () {
std::cout << "base dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "base" );
}

};

std::ostream & operator<< ( std::ostream & ostr,
Base const & obj ) {
return( obj.dump( ostr ) );
}

struct Derived : public Base {

Derived ( void ) {
std::cout << "derived is born.\n";
}

Derived ( Derived const & other )
: Base ( other )
{
std::cout << "derived is copied.\n";
}

virtual ~Derived () {
std::cout << "derived dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "derived" );
}

};

struct foo { virtual ~foo() {} };
int main ( void ) {
copy_ptr< Base > a_ptr;
copy_ptr< Base > b_ptr ( new Derived() );

copy_ptr< Base > c_ptr ( new foo() );
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

a_ptr = b_ptr;
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

}


not tested, but i believe my additions will compile, but cause
undefined behavior at runtime. Checking the return of dynamic_cast is
one solution, but that only identifies the problem at runtime.
Compile-time type safety would probably be desired. Also, dynamic_cast
will barf on non-polymorphic types. While using copy_ptr<int>, etc
would be rather pointless, it would be nice to be allowed for
consistancy.

We can avoid the dynamic_cast by maintaining a D pointer in addition to
or instead of the T pointer. Probably easiest to show with code.

template<class T>
class copy_ptr {

class ptr_holder_inte rface {
public:
// ..
virtual ptr_holder_inte rface *clone() const = 0;
virtual T *get() const = 0;

/* Or, instead of the above get function (purely time-space trade-off
optimization):
T *get() const { return ptr; }

private:
T *ptr;
**/
};

template<class D>
class ptr_holder : public ptr_holder_inte rface {
public:
// ..
ptr_holder_inte rface *clone() const { return new
ptr_holder<D>(d _ptr); }

private:
D *d_ptr;
};

public:
template<class D>
copy_ptr(D *p) : holder(new ptr_holder<D>(p )) {}

// ...

private:
ptr_holder_inte rface *holder;
};

Aug 22 '05 #2
Many many thanks!

Alipha wrote:
[snip]
struct foo { virtual ~foo() {} };
int main ( void ) {
copy_ptr< Base > a_ptr;
copy_ptr< Base > b_ptr ( new Derived() );

copy_ptr< Base > c_ptr ( new foo() );
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

a_ptr = b_ptr;
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

}


not tested, but i believe my additions will compile, but cause
undefined behavior at runtime. Checking the return of dynamic_cast is
one solution, but that only identifies the problem at runtime.
Compile-time type safety would probably be desired.


Actually, one cool thing about dynamic_cast<> is that it does those checks
at compile time.

Also, dynamic_cast
will barf on non-polymorphic types. While using copy_ptr<int>, etc
would be rather pointless, it would be nice to be allowed for
consistancy.
Very good catch!
We can avoid the dynamic_cast by maintaining a D pointer in addition to
or instead of the T pointer. Probably easiest to show with code.

template<class T>
class copy_ptr {

class ptr_holder_inte rface {
public:
// ..
virtual ptr_holder_inte rface *clone() const = 0;
virtual T *get() const = 0;

/* Or, instead of the above get function (purely time-space trade-off
optimization):
T *get() const { return ptr; }

private:
T *ptr;
**/
};

template<class D>
class ptr_holder : public ptr_holder_inte rface {
public:
// ..
ptr_holder_inte rface *clone() const { return new
ptr_holder<D>(d _ptr); }

private:
D *d_ptr;
};

public:
template<class D>
copy_ptr(D *p) : holder(new ptr_holder<D>(p )) {}

// ...

private:
ptr_holder_inte rface *holder;
};


I had something like that in an earlier version. But, I thought, that way
you need to do some explicit concept checking at compile time to see
whether D is derived from T.
However, about the non-polymorphic types you are absolutely right. Here is
an idea how to deal with that: (just provide a special clone_function for
the case D=T.)

// copy_ptr.cc (C) Kai-Uwe Bux [2005]
// =============== =============== ====

/*
| - Upon construction, copy_ptr<T> takes pointee ownership
| a D* where D is a type derived from T. (compile type check)
| - In any copy construction or assignment a copy of the
| pointee is created. There should be no slicing.
| - Upon destruction the pointee is destroyed.
| - Intended use is within STL containers.
| - If T does not have a virtual destructor, copy_ptr<T>
| cannot be used polymorphically (compile time check).
*/

// we swap:
#include <algorithm>

// The clone_traits function:
template < typename T, typename D >
T * clone ( T * ptr ) {
return( new D ( *( dynamic_cast<D* >( ptr ) ) ) );
}

template < typename T >
T * simple_clone ( T * ptr ) {
return( new T ( *ptr ) );
}
// forward declarations:
template < typename T >
class copy_ptr;

template < typename T >
void swap ( copy_ptr< T > &, copy_ptr< T > & );
// implementation:
template < typename T >
class copy_ptr {

friend void swap<> ( copy_ptr<T> &, copy_ptr<T> & );

/*
The idea is that in addition to a pointer, we also need
a pointer to the appropriate clone_traits function.
*/
T * raw_ptr;
T * ( *clone__fct ) ( T * );

public:

copy_ptr ( void )
: raw_ptr ( new T )
, clone__fct( simple_clone<T> )
{}

copy_ptr ( T * ptr )
: raw_ptr ( ptr )
, clone__fct ( simple_clone<T> )
{}

template < typename D >
copy_ptr ( D * ptr )
: raw_ptr ( ptr )
, clone__fct ( clone<T,D> )
{}

// copy construction clone_traitss:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( other.clone__fc t( other.raw_ptr ) )
, clone__fct ( other.clone__fc t )
{}

// destruction frees the pointee
~copy_ptr ( void ) {
delete( raw_ptr );
}

// assignment reduces to copy construction:
copy_ptr & operator= ( copy_ptr const & other ) {
copy_ptr dummy ( other );
swap( *this, dummy );
return( *this );
}

T const * operator-> ( void ) const {
return( raw_ptr );
}

T * operator-> ( void ) {
return( raw_ptr );
}

T const & operator* ( void ) const {
return( *raw_ptr );
}

T & operator* ( void ) {
return( *raw_ptr );
}

}; // copy_ptr<T>

template < typename T >
void swap ( copy_ptr< T > & p, copy_ptr< T > & q ) {
std::swap( p.raw_ptr, q.raw_ptr );
std::swap( p.clone__fct, p.clone__fct );
}
// a sanity check:

#include <iostream>

struct Base {

Base ( void ) {
std::cout << "base is born.\n";
}

Base ( Base const & other ) {
std::cout << "base is copied.\n";
}

virtual ~Base () {
std::cout << "base dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "base" );
}

};

std::ostream & operator<< ( std::ostream & ostr,
Base const & obj ) {
return( obj.dump( ostr ) );
}

struct Derived : public Base {

Derived ( void ) {
std::cout << "derived is born.\n";
}

Derived ( Derived const & other )
: Base ( other )
{
std::cout << "derived is copied.\n";
}

virtual ~Derived () {
std::cout << "derived dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "derived" );
}

};

struct NotDerived {

NotDerived ( void ) {
std::cout << "not-derived is born.\n";
}

NotDerived ( NotDerived const & other )
{
std::cout << "not-derived is copied.\n";
}

virtual ~NotDerived () {
std::cout << "not-derived dies.\n";
}

};

struct BadBase {};
struct BadDerived : public BadBase {};

std::ostream & operator<< ( std::ostream & ostr, NotDerived const & obj ) {
return( ostr << "not-derived" );
}

int main ( void ) {
copy_ptr< Base > a_ptr;
copy_ptr< Base > b_ptr ( new Derived() );

std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

a_ptr = b_ptr;
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

copy_ptr< int > i_ptr;

// compile time errors:
// copy_ptr< Base > x_ptr ( new NotDerived() );
// copy_ptr< int > j_ptr ( new Base() );
// copy_ptr< BadBase > bad_ptr ( new BadDerived () );

}
Thanks again and
best regards

Kai-Uwe Bux

Aug 22 '05 #3

Kai-Uwe Bux wrote:
Hi folks,
in another thread there is some discussion about cloning smart pointers. So
I started thinking about it and stumbled upon a rather simple generic
implementation. However, it uses dynamic_cast<>, and I am not really
familiar with that cast. So, I am not sure whether the code is correct.
Please have a look and let me know how to improve this.

Sorry for the long post

Thanks

Kai-Uwe Bux


// copy_ptr.cc (C) Kai-Uwe Bux [2005]
// =============== =============== ====

/*
| - Upon construction, copy_ptr<T> takes pointee ownership
| a D* where D is a type derived from T.
| - In any copy construction or assignment a copy of the
| pointee is created. There should be no slicing.
| - Upon destruction the pointee is destroyed.
| - Intended use is within STL containers.
*/

// we swap:
#include <algorithm>

// The clone function:
template < typename T, typename D >
T * clone ( T * ptr ) {
return( new D ( *( dynamic_cast<D* >( ptr ) ) ) );
}

// forward declarations:
template < typename T >
class copy_ptr;

template < typename T >
void swap ( copy_ptr< T > &, copy_ptr< T > & );
// implementation:
template < typename T >
class copy_ptr {

friend void swap<> ( copy_ptr<T> &, copy_ptr<T> & );

/*
The idea is that in addition to a pointer, we also need
a pointer to the appropriate clone function.
*/
T * raw_ptr;
T * ( *clone_fct ) ( T * );

public:

copy_ptr ( void )
: raw_ptr ( new T )
, clone_fct( clone<T,T> )
{}

template < typename D >
copy_ptr ( D * ptr )
: raw_ptr ( ptr )
, clone_fct ( clone<T,D> )
{}

// copy construction clones:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( other.clone_fct ( other.raw_ptr ) )
, clone_fct ( other.clone_fct )
{}

// destruction frees the pointee
~copy_ptr ( void ) {
delete( raw_ptr );
}

// assignment reduces to copy construction:
copy_ptr & operator= ( copy_ptr const & other ) {
copy_ptr dummy ( other );
swap( *this, dummy );
return( *this );
}

T const * operator-> ( void ) const {
return( raw_ptr );
}

T * operator-> ( void ) {
return( raw_ptr );
}

T const & operator* ( void ) const {
return( *raw_ptr );
}

T & operator* ( void ) {
return( *raw_ptr );
}

}; // copy_ptr<T>

template < typename T >
void swap ( copy_ptr< T > & p, copy_ptr< T > & q ) {
std::swap( p.raw_ptr, q.raw_ptr );
std::swap( p.clone_fct, p.clone_fct );
}
// a sanity check:

#include <iostream>

struct Base {

Base ( void ) {
std::cout << "base is born.\n";
}

Base ( Base const & other ) {
std::cout << "base is copied.\n";
}

virtual ~Base () {
std::cout << "base dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "base" );
}

};

std::ostream & operator<< ( std::ostream & ostr,
Base const & obj ) {
return( obj.dump( ostr ) );
}

struct Derived : public Base {

Derived ( void ) {
std::cout << "derived is born.\n";
}

Derived ( Derived const & other )
: Base ( other )
{
std::cout << "derived is copied.\n";
}

virtual ~Derived () {
std::cout << "derived dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "derived" );
}

};

int main ( void ) {
copy_ptr< Base > a_ptr;
copy_ptr< Base > b_ptr ( new Derived() );

std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

a_ptr = b_ptr;
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";

}


That's a pretty slick method. I would modify it to use allocator
types, so the clone can be done via custom allocators if required.

Aug 22 '05 #4
Axter wrote:

Kai-Uwe Bux wrote:
// The clone function:
template < typename T, typename D >
T * clone ( T * ptr ) {
return( new D ( *( dynamic_cast<D* >( ptr ) ) ) );
}

[snip]
// implementation:
template < typename T >
class copy_ptr {

friend void swap<> ( copy_ptr<T> &, copy_ptr<T> & );

/*
The idea is that in addition to a pointer, we also need
a pointer to the appropriate clone function.
*/
T * raw_ptr;
T * ( *clone_fct ) ( T * );

public:

copy_ptr ( void )
: raw_ptr ( new T )
, clone_fct( clone<T,T> )
{}

template < typename D >
copy_ptr ( D * ptr )
: raw_ptr ( ptr )
, clone_fct ( clone<T,D> )
{}

// copy construction clones:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( other.clone_fct ( other.raw_ptr ) )
, clone_fct ( other.clone_fct )
{}

// destruction frees the pointee
~copy_ptr ( void ) {
delete( raw_ptr );
}

// assignment reduces to copy construction:
copy_ptr & operator= ( copy_ptr const & other ) {
copy_ptr dummy ( other );
swap( *this, dummy );
return( *this );
}

T const * operator-> ( void ) const {
return( raw_ptr );
}

T * operator-> ( void ) {
return( raw_ptr );
}

T const & operator* ( void ) const {
return( *raw_ptr );
}

T & operator* ( void ) {
return( *raw_ptr );
}

}; // copy_ptr<T>
That's a pretty slick method. I would modify it to use allocator
types, so the clone can be done via custom allocators if required.


Many thanks!

That is a very good suggestion, and I started thinking about it. One way to
go about it is pretty straight forward: turn clone<T,D>() into
clone<T,D,Alloc >(). However, I think in the destructor, the call to delete
would need to be replaced by the appropriate Alloc::dealloca te() call
(after rebinding the allocator to the right type. The most natural way I
see right now is to add a template

template < typename T, typename D, typename Alloc >
void kill ( T* ptr ) {
// let's think about syntax later:
/*
destruct the object
use Alloc<D>::deall ocate( dynamic_cast<D* >( ptr ) );
*/
}

and store a pointer to the appropriate kill() in the copy_ptr<T,Allo c>
object. But I might be stupidly trying to use the same trick twice. It
would be nice, though, if one could avoid this additional field.

In any case, thanks a lot for your comment. I will try that out and post
some version soon.
Best regards

Kai-Uwe Bux
Aug 23 '05 #5
In article <de*********@mu rdoch.acc.Virgi nia.EDU>,
Kai-Uwe Bux <jk********@gmx .net> wrote:
// copy_ptr.cc (C) Kai-Uwe Bux [2005]
// =============== =============== ====


Beware of this use case:

struct Derived2 : public Derived {

Derived2 ( void ) {
std::cout << "derived2 is born.\n";
}

Derived2 ( Derived2 const & other )
: Derived ( other )
{
std::cout << "derived2 is copied.\n";
}

virtual ~Derived2 () {
std::cout << "derived2 dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "derived2" );
}

};

....

Derived* d2p = new Derived;
copy_ptr< Base > d2_ptr ( d2p );
copy_ptr< Base > d2_ptr_also(d2_ ptr);

The copy slices your Derived2 down to a Derived.

A more bullet-proof approach (imho) is to have a copy_ptr which simply
does (untested):

copy_ptr ( copy_ptr const & other )
: raw_ptr (0)
{
if (other.raw_ptr)
raw_ptr = new T(*other);
}

And also a separate clone_ptr:

clone_ptr (clone_ptr const & other )
: raw_ptr (0)
{
if (other.raw_ptr)
raw_ptr = other->clone();
}

I.e. Two fundamental tools instead of trying to merge them under one
roof. Lower overhead, simpler, easier concept for clients to grasp.

I also note that null testing is missing. And you've implemented deep
const. Some people really like deep const. Other's really dislike it.
Standard and proposed standard smart pointers implement shallow const.
Document it loudly. :-)

-Howard
Aug 23 '05 #6
Howard Hinnant wrote:
In article <de*********@mu rdoch.acc.Virgi nia.EDU>,
Kai-Uwe Bux <jk********@gmx .net> wrote:
// copy_ptr.cc (C) Kai-Uwe Bux [2005]
// =============== =============== ====
Beware of this use case:

struct Derived2 : public Derived {

Derived2 ( void ) {
std::cout << "derived2 is born.\n";
}

Derived2 ( Derived2 const & other )
: Derived ( other )
{
std::cout << "derived2 is copied.\n";
}

virtual ~Derived2 () {
std::cout << "derived2 dies.\n";
}

virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "derived2" );
}

};

...

Derived* d2p = new Derived;
copy_ptr< Base > d2_ptr ( d2p );
copy_ptr< Base > d2_ptr_also(d2_ ptr);

The copy slices your Derived2 down to a Derived.


You are right, copy_ptr<> is dangerous:

As for slicing, the problem is not a 2-step derivation, already

Base * b_ptr = new Derived ( ... );
copy_ptr< Base > box ( b_ptr );

will happily slice the object.

The copy_ptr<> template should be handled by strictly conforming to certain
idioms. In particular, it should always be initialized like so:

copy_ptr< T > ptr ( new D ( ... ) );

This idiom is the intended use. Whenever people depart, BadThings(tm) will
hurt you. [Reminds me of std::auto_ptr<> .] This is somewhat by design (you
also noted the missing check for NULL in the constructor -- well, it is
intentional).

Now, if I really wanted to tie the clients into some kind of scheme, I would
provide different constructors:

copy_ptr<T> ( T ); // beware of slicing!

and

copy_ptr<T> ( copy_ptr<D> const & );
and then all copy pointers would be initialized by copy-construction from
pre-existing objects. This way, the slicing is well documented and
non-surprising and NULL initialization is impossible.

A more bullet-proof approach (imho) is to have a copy_ptr which simply
does (untested):

copy_ptr ( copy_ptr const & other )
: raw_ptr (0)
{
if (other.raw_ptr)
raw_ptr = new T(*other);
}

Actually such a copy_ptr<T> does not offer anything that T does not do
already.
And also a separate clone_ptr:

clone_ptr (clone_ptr const & other )
: raw_ptr (0)
{
if (other.raw_ptr)
raw_ptr = other->clone();
}

I.e. Two fundamental tools instead of trying to merge them under one
roof. Lower overhead, simpler, easier concept for clients to grasp.


I think, the overhead of a clone pointer is not lesser, just hidden in the
management of virtual function calls. As for being more simple and an
easier concept from a client perspective, I have no opinion since I lack
experience with clone pointers.

Best

Kai-Uwe Bux
ps.: What I *really* want is a polymorphic variable (realized via a smart
reference not a smart pointer). Unfortunately, one cannot overload the
dot-operator. Oh well.
Aug 24 '05 #7
In article <de**********@m urdoch.acc.Virg inia.EDU>,
Kai-Uwe Bux <jk********@gmx .net> wrote:
As for slicing, the problem is not a 2-step derivation, already

Base * b_ptr = new Derived ( ... );
copy_ptr< Base > box ( b_ptr );

will happily slice the object.
Yup.
The copy_ptr<> template should be handled by strictly conforming to certain
idioms. In particular, it should always be initialized like so:

copy_ptr< T > ptr ( new D ( ... ) );

This idiom is the intended use. Whenever people depart, BadThings(tm) will
hurt you. [Reminds me of std::auto_ptr<> .]
:-)
This is somewhat by design (you
also noted the missing check for NULL in the constructor -- well, it is
intentional).
Actually the NULL check I was talking about was for this kind of support:

copy_ptr<T> p;
....
if (p) ...

or

if (p == NULL)

It can safely be accomplished with a pointer to a private data member.
See:

http://home.twcny.rr.com/hinnant/cpp...nique_ptr.html

and search for "nat" and:

operator int nat::*() const {return ptr_.first() ? &nat::for_bo ol_ : 0;}
A more bullet-proof approach (imho) is to have a copy_ptr which simply
does (untested):

copy_ptr ( copy_ptr const & other )
: raw_ptr (0)
{
if (other.raw_ptr)
raw_ptr = new T(*other);
}


Actually such a copy_ptr<T> does not offer anything that T does not do
already.


<nod> You may be right (I've never needed one either). So why bother
with:

template < typename T >
T * simple_clone ( T * ptr ) {
return( new T ( *ptr ) );
}

Does that not turn your copy_ptr into my copy_ptr for a certain class of
types?
And also a separate clone_ptr:

clone_ptr (clone_ptr const & other )
: raw_ptr (0)
{
if (other.raw_ptr)
raw_ptr = other->clone();
}

I.e. Two fundamental tools instead of trying to merge them under one
roof. Lower overhead, simpler, easier concept for clients to grasp.


I think, the overhead of a clone pointer is not lesser, just hidden in the
management of virtual function calls. As for being more simple and an
easier concept from a client perspective, I have no opinion since I lack
experience with clone pointers.


I think the overhead is lesser. My suggested clone_ptr need only hold
the pointer. And the pointee need only have a virtual destructor and
virtual clone() function.

Your copy_ptr requires two pointers, and the pointee still needs a
virtual function table (else the dynamic_cast will fail).

Your copy_ptr has less restrictive requirements on the pointee (which is
a good thing). The pointee need not implement a virtual copy
constructor named clone().

My clone_ptr variant drops the internal function pointer by requiring
the clone() signature of the pointee. <shrug> it's a tradeoff. But
lots of generic code requires that the template parameter meet some
requirements (or concepts). E.g. std::for_each:

template<class InputIterator, class Function>
Function
for_each(InputI terator first, InputIterator last, Function f);

Requires InputIterator to meet all of the requirements of an input
iterator. Also requires of Function that the following be well defined:

f(*first);

Similarly clone_ptr<T> would require that:

T* p1 = ... // p1 non-null
T* p2 = p1->clone();

be well defined and that the dynamic type of *p1 and *p2 be the same,
and that *p2 be a copy of *p1.

Failure to have a clone() signature would of course be caught at compile
time. Having a clone() signature with other semantics would be
problematic and probably result in a run time failure. So major
questions are: Is clone() sufficiently universal in it's meaning (like
swap(x,y) has become)? Is requiring it of your pointee overly
burdensome?

I'm inclined to answer yes and no, but freely admit that this is a
subjective response.
ps.: What I *really* want is a polymorphic variable (realized via a smart
reference not a smart pointer). Unfortunately, one cannot overload the
dot-operator. Oh well.


This has been discussed in committee, but I'm not sure of the current
status. There are more language proposals than there are people to work
on them.

-Howard
Aug 25 '05 #8

Kai-Uwe Bux wrote:
Axter wrote:

Kai-Uwe Bux wrote:

// The clone function:
template < typename T, typename D >
T * clone ( T * ptr ) {
return( new D ( *( dynamic_cast<D* >( ptr ) ) ) );
}

[snip]
// implementation:
template < typename T >
class copy_ptr {

friend void swap<> ( copy_ptr<T> &, copy_ptr<T> & );

/*
The idea is that in addition to a pointer, we also need
a pointer to the appropriate clone function.
*/
T * raw_ptr;
T * ( *clone_fct ) ( T * );

public:

copy_ptr ( void )
: raw_ptr ( new T )
, clone_fct( clone<T,T> )
{}

template < typename D >
copy_ptr ( D * ptr )
: raw_ptr ( ptr )
, clone_fct ( clone<T,D> )
{}

// copy construction clones:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( other.clone_fct ( other.raw_ptr ) )
, clone_fct ( other.clone_fct )
{}

// destruction frees the pointee
~copy_ptr ( void ) {
delete( raw_ptr );
}

// assignment reduces to copy construction:
copy_ptr & operator= ( copy_ptr const & other ) {
copy_ptr dummy ( other );
swap( *this, dummy );
return( *this );
}

T const * operator-> ( void ) const {
return( raw_ptr );
}

T * operator-> ( void ) {
return( raw_ptr );
}

T const & operator* ( void ) const {
return( *raw_ptr );
}

T & operator* ( void ) {
return( *raw_ptr );
}

}; // copy_ptr<T>

That's a pretty slick method. I would modify it to use allocator
types, so the clone can be done via custom allocators if required.


Many thanks!

That is a very good suggestion, and I started thinking about it. One way to
go about it is pretty straight forward: turn clone<T,D>() into
clone<T,D,Alloc >(). However, I think in the destructor, the call to delete
would need to be replaced by the appropriate Alloc::dealloca te() call
(after rebinding the allocator to the right type. The most natural way I
see right now is to add a template

template < typename T, typename D, typename Alloc >
void kill ( T* ptr ) {
// let's think about syntax later:
/*
destruct the object
use Alloc<D>::deall ocate( dynamic_cast<D* >( ptr ) );
*/
}

and store a pointer to the appropriate kill() in the copy_ptr<T,Allo c>
object. But I might be stupidly trying to use the same trick twice. It
would be nice, though, if one could avoid this additional field.

In any case, thanks a lot for your comment. I will try that out and post
some version soon.
Best regards

Kai-Uwe Bux


I was playing around with this method, and one problem I had with it,
is that I couldn't get it to compile on VC++ 6.0 or Borland BCC55.
See following link for class and demo project.
http://code.axter.com/copy_ptr.h
http://code.axter.com/clone_ptr_demo.zip

It works just fine in VC++ 7.1 and GNU 3.x

Aug 27 '05 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

18
2157
by: Ben Hanson | last post by:
I have created an open source Notepad program for Windows in C++ that allows search and replace using regular expressions (and a few other extras). It is located at http://www.codeproject.com/cpp/notepadre.asp I'm trying to use best practice in my C++ programming and would appreciate any advice anyone can give. As code is far more than just a one page sample, just a review of one of the source files (or even just a function or two) is...
9
1717
by: Adam Monsen | last post by:
I kindly request a code review. If this is not an appropriate place for my request, where might be? Specific questions are in the QUESTIONS section of the code. ========================================================================== #include <stdio.h> #include <stdlib.h> #include <string.h>
6
3150
by: Daniel Rimmelzwaan | last post by:
I want to send a biztalk document to an aspx page, and I need to see some sample code, because I just can't make it work. I have a port with transport type HTTP, pointing to my aspx page, something like http://myserver/mypage.aspx. From there it gets blurry, because I just can't figure out how to do the rest. Does anybody have a sample page for me that I can take a look at? Just a simple one that takes whatever biztalk sends and saves it...
2
7870
by: William F. Robertson, Jr. | last post by:
Some of my users are receiving an error: Server Error in '/' Application. ---------------------------------------------------------------------------- ---- Request timed out. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
0
3843
by: Jack Wright | last post by:
Dear All, I have a web Application "http://localhost/Web/WebForm1.aspx" that calls a WebService from "http://localhost/webserviceapp/service1.asmx"...I have set the executionTimeout to 10 secs in Web.Config where my WebService is installed. <httpRuntime executionTimeout="10" appRequestQueueLimit="2" /> When I call a method "TestPlan" which takes more than 10 secs my web Page gets the following error:
2
1719
by: Terry Mulvany | last post by:
namespace CIBWeb { public class BasePage : System.Web.UI.Page { public BasePage() { } protected override void OnInit(EventArgs e) {
7
4148
by: Shapiro | last post by:
I have a scenario where I log a resquest to a database table and update the request with a corresponding response including the response time. I am using an HttpModule to do this. My challenge is how to corelate the response to a corresponding request. I am looking for a sure proof threadsafe way to corelate a response to a coresponding request message. Two things that concerns me:-
5
1941
by: Feniks | last post by:
Hello, I have some problems with smart pointers. I want to have smart pointer, copy_ptr in this case in my class like this: class Entity { protected: ..... copy_ptr<PropertyBagmProperties; // <- PropertyBag is a class here
3
17823
by: nms | last post by:
I've an ASP.net web site, Since last some days, Users are experiencing problem when they try to generate reports (for a large date range) it says, Request timed out. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information
0
9786
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9641
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10769
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10479
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
10199
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
5616
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5778
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4409
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3956
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.