Deep Copy smart pointer not requiring virtual copy constructor

N

Nindi73

A few days a ago I posted my code for a deep copy pointer which
doesn't require the pointee object to have a virtual copy constructor.
I need help with checking that it was exception safe and exception
neutral/
I got a reply from Bux with his code for a smart pointer with far fewer
lines of code
and more cleaner design, not over engineered like mine.

http://groups.google.co.uk/group/co...9af25/89e5c18bc6bb5c7e?hl=en#89e5c18bc6bb5c7e


However there was one possible extension to that code :

'The trade-off is that copy_ptr<Base> and copy_ptr<Derived> are
unrelated '
Kai-Uwe Bux
I think I have managed to implement this extension, so I post my code
here along with a main.cpp that exhibists the extension.

Any comments about my abuse of the language would be more than welcome
--------------------------------------------------------------------------------------------------------------
<smart_ptr_policies.h>
#ifndef SMART_POINTER_POLICIES_HEADER
#define SMART_POINTER_POLICIES_HEADER


#include<algorithm>
#include<boost/utility.hpp>
#include<boost/type_traits.hpp>

//
http://groups.google.co.uk/group/comp.lang.c++/browse_thread/thread/b...



template<class DerivedClass>
void * Buxclone(const void *theOtherPtr){
return new DerivedClass(*static_cast<const
DerivedClass*>(theOtherPtr));
}


template<class BaseClass>
struct BuxWrappedPointer {

template<class T>
friend struct BuxWrappedPointer;

BuxWrappedPointer():raw_pointer(0),theCloner(0){}

template<class DerivedClass>
BuxWrappedPointer(DerivedClass * ptr):raw_pointer(ptr),
theCloner(&Buxclone<DerivedClass>){}


BuxWrappedPointer(const BuxWrappedPointer &theOther):
theCloner(theOther.theCloner),
raw_pointer(theOther.clone(theOther.raw_pointer)){}

template<class MiddleBase>
BuxWrappedPointer(const BuxWrappedPointer<MiddleBase>
&theMiddleBase,
typename
boost::enable_if<boost::is_base_of<BaseClass,MiddleBase>,MiddleBase>::type
*pp=0):
theCloner(theMiddleBase.theCloner),
raw_pointer(static_cast<BaseClass *>
(theMiddleBase.theCloner(static_cast<const BaseClass
*>(theMiddleBase.raw_pointer)))){}

BuxWrappedPointer<BaseClass> & operator=(const
BuxWrappedPointer<BaseClass> &theOther)
{
BuxWrappedPointer<BaseClass> temp(theOther);
std::swap(temp.raw_pointer,raw_pointer);
return *this;
}


BaseClass * raw_pointer;


~BuxWrappedPointer(){delete raw_pointer;}
private:
BaseClass *clone(const BaseClass * theSource)const{
return static_cast<BaseClass*>(theCloner(static_cast<const
BaseClass*>(theSource)));
}
typedef void * (*clone_)(const void *);
clone_ theCloner;


};
#endif
--------------------------------------------------------------------------------------------------------------
<smart_ptr.h>

#ifndef SMART_POINTER__HEADER
#define SMART_POINTER__HEADER

#include"smart_ptr_policies.h"

template<
class BaseClass,
class smart_ptr {
public:
typedef typename boost::remove_const<BaseClass>::type non_const_base;

template<class T,template <class> class CLONE_POLICY >
friend class smart_ptr;

template<class DerivedClass>
smart_ptr(DerivedClass *
theDerivedPointer_):theWrappedPointer(theDerivedPointer_){}

smart_ptr(const smart_ptr<non_const_base,CLONE_POLICY>&
theOtherSmart_ptr):
theWrappedPointer(theOtherSmart_ptr.theWrappedPointer){}

smart_ptr(const smart_ptr<const non_const_base,CLONE_POLICY>&
theOtherSmart_ptr):
theWrappedPointer(theOtherSmart_ptr.theWrappedPointer){}

template<class T>
smart_ptr(const smart_ptr<T,CLONE_POLICY>& theOtherSmart_ptr,
typename boost::enable_if<boost::is_base_of<non_const_base,typename
boost::remove_const<T>::type>,T>::type *pp=0):
theWrappedPointer(theOtherSmart_ptr.theWrappedPointer){}

smart_ptr(){}

BaseClass *operator->()const{return
theWrappedPointer.raw_pointer;}

virtual ~smart_ptr(){}


private:
smart_ptr & operator=(const BaseClass *ptr);

CLONE_POLICY<non_const_base> theWrappedPointer;


};


#endif
--------------------------------------------------------------------------------------------------------------
<main.cpp>
#include<vector>
#include<iostream>

#include<cctype>
#include"smart_ptr.h"

using namespace std;


class Base {
public:
Base():ch('*'){
Count++;
}
Base(const Base&):ch('*'){
Count++;
}
virtual char show(){
return ch;
}
virtual ~Base(){Count--;}
virtual void v()const=0;
static unsigned long Count;
private:
char ch;
};
unsigned long Base::Count(0);

/// this will give us lots of derived classes
template<char ch_>
class Derived :public Base {
public:
Derived():ch(ch_){
Count++;
}
Derived(const Derived<ch_>&):ch(ch_){
Count++;
}
virtual char show(){
return ch;
}
virtual ~Derived(){Count--;}
virtual void v()const{}
static unsigned long Count;
private:
char ch;

};
template<char ch_>
unsigned long Derived<ch_>::Count(0);

template<char ch_>
class DerivedSquared :public Derived<ch_> {
public:
DerivedSquared():ch(std::tolower(ch_)){
Count++;
}
DerivedSquared(const DerivedSquared<ch_>&):ch(std::tolower(ch_)){
Count++;
}
virtual char show(){return ch;}
virtual ~DerivedSquared(){Count--;}
virtual void v()const{}
static unsigned long Count;
private:
char ch;

};
template<char ch_>
unsigned long DerivedSquared<ch_>::Count(0);




int main() {

smart_ptr<Derived<'B'>,BuxWrappedPointer> theDPA(new
DerivedSquared<'B'>);
cout << theDPA->show();

smart_ptr<Base,BuxWrappedPointer> theDPA2(theDPA);
cout << theDPA2->show();


}
 
G

Gianni Mariani

A few days a ago I posted my code for a deep copy pointer which
doesn't require the pointee object to have a virtual copy constructor.
I need help with checking that it was exception safe and exception
neutral/
I got a reply from Bux with his code for a smart pointer with far fewer
lines of code
and more cleaner design, not over engineered like mine.

http://groups.google.co.uk/group/co...9af25/89e5c18bc6bb5c7e?hl=en#89e5c18bc6bb5c7e


However there was one possible extension to that code :

'The trade-off is that copy_ptr<Base> and copy_ptr<Derived> are
unrelated '
Kai-Uwe Bux
I think I have managed to implement this extension, so I post my code
here along with a main.cpp that exhibists the extension.

Why do you need such a beast ? i.e. what are the requirements and use case ?
Any comments about my abuse of the language would be more than welcome

It's kind of hard to read.
 
K

Kai-Uwe Bux

A few days a ago I posted my code for a deep copy pointer which
doesn't require the pointee object to have a virtual copy constructor.
I need help with checking that it was exception safe and exception
neutral/
I got a reply from Bux with his code for a smart pointer with far fewer
lines of code
and more cleaner design, not over engineered like mine.

http://groups.google.co.uk/group/co...9af25/89e5c18bc6bb5c7e?hl=en#89e5c18bc6bb5c7e


However there was one possible extension to that code :

        'The trade-off is that  copy_ptr<Base> and copy_ptr<Derived> are
unrelated '
Kai-Uwe Bux
I think I have managed to implement this extension, so I post my code
here along with a main.cpp that exhibists the extension.

What a coincidence, your post prompted me to rethink that, too. I appears
that we arrived at very similar conclusions (see below).

Any comments about my abuse of the language would be more than welcome

General comment: you lines are too long.

--------------------------------------------------------------------------------------------------------------
<smart_ptr_policies.h>
#ifndef SMART_POINTER_POLICIES_HEADER
#define SMART_POINTER_POLICIES_HEADER


#include<algorithm>
#include<boost/utility.hpp>
#include<boost/type_traits.hpp>

//
http://groups.google.co.uk/group/comp.lang.c++/browse_thread/thread/b...



template<class DerivedClass>
void * Buxclone(const void *theOtherPtr){
                return new DerivedClass(*static_cast<const
DerivedClass*>(theOtherPtr));
}


template<class BaseClass>
struct BuxWrappedPointer {

           template<class T>
           friend  struct BuxWrappedPointer;

       BuxWrappedPointer():raw_pointer(0),theCloner(0){}

       template<class DerivedClass>
             BuxWrappedPointer(DerivedClass * ptr):raw_pointer(ptr),
                                 theCloner(&Buxclone<DerivedClass>){}


       BuxWrappedPointer(const BuxWrappedPointer &theOther):
                         theCloner(theOther.theCloner),
                                
                                raw_pointer(theOther.clone(theOther.raw_pointer))
                                {}

           template<class MiddleBase>
           BuxWrappedPointer(const BuxWrappedPointer<MiddleBase>
&theMiddleBase,
                                typename
boost::enable_if<boost::is_base_of<BaseClass,MiddleBase>,MiddleBase>::type
*pp=0):
                                        theCloner(theMiddleBase.theCloner),
                                        raw_pointer(static_cast<BaseClass *>
BaseClass
*>(theMiddleBase.raw_pointer)))){}

       BuxWrappedPointer<BaseClass> & operator=(const
BuxWrappedPointer<BaseClass> &theOther)
           {
                   BuxWrappedPointer<BaseClass> temp(theOther);
           std::swap(temp.raw_pointer,raw_pointer);
           return *this;
           }


      BaseClass * raw_pointer;


     ~BuxWrappedPointer(){delete raw_pointer;}
private:
          BaseClass *clone(const BaseClass * theSource)const{
                return static_cast<BaseClass*>(theCloner(static_cast<const
BaseClass*>(theSource)));
          }
      typedef void * (*clone_)(const void *);
      clone_  theCloner;


};
#endif
--------------------------------------------------------------------------------------------------------------

Oh boy, you are quite boost-savvy! I do not quite see, however, what all
this enable_if< ... > stuff is doing for you. If it is just to get the
right compiler errors if you do

copy_ptr< Base > b_ptr = copy_ptr< NotDerived >( new not Derived() );

then, I think, there are simpler means of going about it (see below). But I
might be missing something here.
<smart_ptr.h>

#ifndef SMART_POINTER__HEADER
#define SMART_POINTER__HEADER

#include"smart_ptr_policies.h"

template<
        class BaseClass,

class smart_ptr {
public:
                typedef typename boost::remove_const<BaseClass>::type
                non_const_base;

                template<class T,template <class> class CLONE_POLICY >
                friend class smart_ptr;

g++ gives me an error for the above saying that the declaration of
CLONE_POLICY shadows the template parameter.
        template<class DerivedClass>
             smart_ptr(DerivedClass *
theDerivedPointer_):theWrappedPointer(theDerivedPointer_){}

                smart_ptr(const smart_ptr<non_const_base,CLONE_POLICY>&
theOtherSmart_ptr):
                        
                        theWrappedPointer(theOtherSmart_ptr.theWrappedPointer)
                        {}

                smart_ptr(const smart_ptr<const
                non_const_base,CLONE_POLICY>&
theOtherSmart_ptr):                         
                        theWrappedPointer(theOtherSmart_ptr.theWrappedPointer)
                        {}

                template<class T>
                        smart_ptr(const smart_ptr<T,CLONE_POLICY>&
                        theOtherSmart_ptr,
                                typename
                                boost::enable_if<boost::is_base_of<non_const_base,typename
boost::remove_const<T>::type>,T>::type
*pp=0):
                                        theWrappedPointer(theOtherSmart_ptr.theWrappedPointer)
                                        {}

        smart_ptr(){}

        BaseClass *operator->()const{return
theWrappedPointer.raw_pointer;}

                virtual ~smart_ptr(){}

Why is this destructor virtual?
private:
        smart_ptr & operator=(const BaseClass *ptr);

        CLONE_POLICY<non_const_base> theWrappedPointer;


};


#endif

Again, I find the boost stuff confusing. What design specs necessitate the
use of non_const_base and remove_const?
<main.cpp>
#include<vector>
#include<iostream>

#include<cctype>
#include"smart_ptr.h"

using namespace std;


class Base {
public:
        Base():ch('*'){
                Count++;
        }
        Base(const Base&):ch('*'){
                Count++;
        }
        virtual char show(){
                return ch;
        }
        virtual ~Base(){Count--;}
        virtual void v()const=0;
        static unsigned long Count;
private:
        char ch;
};
unsigned long Base::Count(0);

/// this will give us lots of derived classes
template<char ch_>
class Derived :public Base {
public:
        Derived():ch(ch_){
                Count++;
        }
        Derived(const Derived<ch_>&):ch(ch_){
                Count++;
        }
        virtual char show(){
                return ch;
        }
        virtual ~Derived(){Count--;}
        virtual void v()const{}
        static unsigned long Count;
private:
        char ch;

};
template<char ch_>
unsigned long Derived<ch_>::Count(0);

template<char ch_>
class DerivedSquared :public Derived<ch_> {
public:
        DerivedSquared():ch(std::tolower(ch_)){
                Count++;
        }
        DerivedSquared(const DerivedSquared<ch_>&):ch(std::tolower(ch_)){
                Count++;
        }
        virtual char show(){return ch;}
        virtual ~DerivedSquared(){Count--;}
        virtual void v()const{}
        static unsigned long Count;
private:
        char ch;

};
template<char ch_>
unsigned long DerivedSquared<ch_>::Count(0);




int main() {

        smart_ptr<Derived<'B'>,BuxWrappedPointer> theDPA(new
DerivedSquared<'B'>);
        cout << theDPA->show();

        smart_ptr<Base,BuxWrappedPointer> theDPA2(theDPA);
        cout << theDPA2->show();


}

I found that

smart_ptr<int> i_ptr ( new int ( 5 ) );
cout << *i_ptr << '\n';

gives me an error. You should add operator*.



I worked a little on the non-policy based version to have copy_ptr<Base> and
copy_ptr<Derived> behave as desired. I also added a compile time check
agains using non-polymorphic base classes. This might be overkill.
(Alternatively, one could think about adding a deleter-function that would
use the right destructor in any case.)


#include <algorithm> // std::swap

template < typename T >
struct non_polymorphic_class_error {
enum {
trigger = sizeof( dynamic_cast<void*>( static_cast<T*>(0) ) )
};

};


// The clone function:
// ===================
template < typename T >
void * clone ( void * ptr ) {
return ( ptr == 0 ? 0
: static_cast<void*>
( new T ( *( static_cast<T*>( ptr ) ) ) ) );
}

// The copy_ptr:
// =============

template < typename T >
class copy_ptr {

friend void swap ( copy_ptr<T> & p, copy_ptr<T> & q ) {
std::swap( p.raw_ptr, q.raw_ptr );
std::swap( p.clone_fct, q.clone_fct );
}

template < typename D >
friend class copy_ptr;

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

public:

copy_ptr ( T * ptr = 0)
: raw_ptr ( ptr )
, clone_fct ( clone<T> )
{}

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

// copy construction clones:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( static_cast<T*>( other.clone_fct( other.raw_ptr ) ) )
, clone_fct ( other.clone_fct )
{}

template < typename D >
copy_ptr ( copy_ptr<D> const & other )
: raw_ptr ( static_cast<D*>( other.clone_fct( other.raw_ptr ) ) )
, clone_fct ( other.clone_fct )
{
non_polymorphic_class_error<T> d2;
}

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

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

template < typename D >
copy_ptr & operator= ( copy_ptr<D> 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( *( this->operator->() ) );
}

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

}; // copy_ptr<T>

Please, let me know what you think.


Best

Kai-Uwe Bux
 
N

Nindi73

Kai-Uwe Bux said:
What a coincidence, your post prompted me to rethink that, too. I appears
that we arrived at very similar conclusions (see below).



General comment: you lines are too long.
I apologise for the length of the lines, I think my problem is 2-fold.
Firstly I am not a proffesional developer
I have never really learnt a good style for code presentation, but I
suppose tools like doxygen make that a
non-excuse. Secondly, I think something just went wrong with the
pasting.
Oh boy, you are quite boost-savvy! I do not quite see, however, what all
this enable_if< ... > stuff is doing for you. If it is just to get the
right compiler errors if you do

copy_ptr< Base > b_ptr = copy_ptr< NotDerived >( new not Derived() );

then, I think, there are simpler means of going about it (see below). But I
might be missing something here.

re enable_if from the boost website

The enable_if family of templates is a set of tools
to allow a function template or a class
template specialization to include or exclude itself
from a set of matching functions or
specializations based on properties of its template
arguments. For example, one can define
function templates that are only enabled for, and
thus only match, an arbitrary set of types
defined by a traits class. The enable_if templates
can also be applied to enable class template
specializations.

So I am only enabling
BuxWrappedPointer(const BuxWrappedPointer<MiddleBase>
&theMiddleBase)

when MiddleBase is derived FROM Base. This allows :
clone_ptr<MiddleBase> theMiddleClass_ptr(new Derived);
clone_ptr<Base> theBaseClass_ptr (theMiddleClass_ptr); //
casting up the hierachy should be allowed

but disallows everything else including
clone_ptr<Base> theBaseClass_ptr (new Derived);
clone_ptr<MiddleBase> theMiddleBaseClass_ptr
(theMiddleClass_ptr); // casting down the hierachy should NOT be
allowed

g++ gives me an error for the above saying that the declaration of
CLONE_POLICY shadows the template parameter.


Why is this destructor virtual?


Again, I find the boost stuff confusing. What design specs necessitate the
use of non_const_base and remove_const?

This non_const_base gives me 'type' from 'const type'
So this allows me to do :
copy_ptr<Base> thePtr(new Derived);
copy_ptr<const Base> theConstPtr(thePtr);

Of course this may not be so important for deep copy but may be useful
for other policies. But I still want the Policy
class to be template on Base, even if the smart_ptr is templated on
Base or const Base, in some sense they are the same.

The line
template<class T>
smart_ptr(const smart_ptr<T,CLONE_POLICY>&

should be something like

template<class T, template <class> class OTHER_CLONE_POLICY>
smart_ptr(const
smart_ptr<T,OTHER_CLONE_POLICY>&

sorry, I use both Linux gcc and msvc, and I happend to be using msvc at
that moment, and it did not complain.

Again I am using the boost template stuff ONLY when T is derived FROM
Base, for same reasons as above. But I m ripping of the const of them
before I compare. That is

BaseClass = Base T = MiddleBase
This is ok
BaseClass = const Base T = MiddleBase
This is ok
BaseClass = Base T = const MiddleBase
This is ok
BaseClass = const Base T = const
MiddleBase This is ok



I found that

smart_ptr<int> i_ptr ( new int ( 5 ) );
cout << *i_ptr << '\n';

gives me an error. You should add operator*.

I completely agree, I should have posted a more complete class. But I
just wanted to show my extension to your trick
leaving out the other details. I intend to add other members too, such
as interoperability with auto_ptr etc

I worked a little on the non-policy based version to have copy_ptr<Base> and
copy_ptr<Derived> behave as desired. I also added a compile time check
agains using non-polymorphic base classes. This might be overkill.
(Alternatively, one could think about adding a deleter-function that would
use the right destructor in any case.)


#include <algorithm> // std::swap

template < typename T >
struct non_polymorphic_class_error {
enum {
trigger = sizeof( dynamic_cast<void*>( static_cast<T*>(0) ) )
};

};

I forbid myslef from using dynamic_cast, but I would have to try to
remeber the reasons why. But I do have a question.
Isn't it true that dynamic_cast happens at runtime and therefore we
have a overhead, whereas static_cast takes place at compile time, so
we don't. I think the boost is_base combined with boost enable_if lets
us 'error check' at compile time.

// The clone function:
// ===================
template < typename T >
void * clone ( void * ptr ) {
return ( ptr == 0 ? 0
: static_cast<void*>
( new T ( *( static_cast<T*>( ptr ) ) ) ) );
}

// The copy_ptr:
// =============

template < typename T >
class copy_ptr {

friend void swap ( copy_ptr<T> & p, copy_ptr<T> & q ) {
std::swap( p.raw_ptr, q.raw_ptr );
std::swap( p.clone_fct, q.clone_fct );
}

template < typename D >
friend class copy_ptr;

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

public:

copy_ptr ( T * ptr = 0)
: raw_ptr ( ptr )
, clone_fct ( clone<T> )
{}

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

// copy construction clones:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( static_cast<T*>( other.clone_fct( other.raw_ptr ) ) )
, clone_fct ( other.clone_fct )
{}

template < typename D >
copy_ptr ( copy_ptr<D> const & other )
: raw_ptr ( static_cast<D*>( other.clone_fct( other.raw_ptr ) ) )
, clone_fct ( other.clone_fct )
{
non_polymorphic_class_error<T> d2;
}

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

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

template < typename D >
copy_ptr & operator= ( copy_ptr<D> 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( *( this->operator->() ) );
}

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

}; // copy_ptr<T>

Please, let me know what you think.


I by no means am 'boost savy' I didn't even know about is_basr until a
few days ago. Its just that I find some real magical bits of
functionality there, its a waste of time trying to re-code some of it,
especially when the boost coders are in a far superior league to
of programmers to me. Also I prefer to do as much as possible at
compile time than at runtime.



N

PS I think my first ever use of is_base was to write this class.
 
N

Nindi73

Kai-Uwe Bux said:
What a coincidence, your post prompted me to rethink that, too. I appears
that we arrived at very similar conclusions (see below).



General comment: you lines are too long.



Oh boy, you are quite boost-savvy! I do not quite see, however, what all
this enable_if< ... > stuff is doing for you. If it is just to get the
right compiler errors if you do

copy_ptr< Base > b_ptr = copy_ptr< NotDerived >( new not Derived() );

then, I think, there are simpler means of going about it (see below). But I
might be missing something here.


g++ gives me an error for the above saying that the declaration of
CLONE_POLICY shadows the template parameter.


Why is this destructor virtual?


Again, I find the boost stuff confusing. What design specs necessitate the
use of non_const_base and remove_const?


I found that

smart_ptr<int> i_ptr ( new int ( 5 ) );
cout << *i_ptr << '\n';

gives me an error. You should add operator*.



I worked a little on the non-policy based version to have copy_ptr<Base> and
copy_ptr<Derived> behave as desired. I also added a compile time check
agains using non-polymorphic base classes. This might be overkill.
(Alternatively, one could think about adding a deleter-function that would
use the right destructor in any case.)


#include <algorithm> // std::swap

template < typename T >
struct non_polymorphic_class_error {
enum {
trigger = sizeof( dynamic_cast<void*>( static_cast<T*>(0) ) )
};

};


// The clone function:
// ===================
template < typename T >
void * clone ( void * ptr ) {
return ( ptr == 0 ? 0
: static_cast<void*>
( new T ( *( static_cast<T*>( ptr ) ) ) ) );
}

// The copy_ptr:
// =============

template < typename T >
class copy_ptr {

friend void swap ( copy_ptr<T> & p, copy_ptr<T> & q ) {
std::swap( p.raw_ptr, q.raw_ptr );
std::swap( p.clone_fct, q.clone_fct );
}

template < typename D >
friend class copy_ptr;

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

public:

copy_ptr ( T * ptr = 0)
: raw_ptr ( ptr )
, clone_fct ( clone<T> )
{}

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

// copy construction clones:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( static_cast<T*>( other.clone_fct( other.raw_ptr ) ) )
, clone_fct ( other.clone_fct )
{}

template < typename D >
copy_ptr ( copy_ptr<D> const & other )
: raw_ptr ( static_cast<D*>( other.clone_fct( other.raw_ptr ) ) )
, clone_fct ( other.clone_fct )
{
non_polymorphic_class_error<T> d2;
}

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

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

template < typename D >
copy_ptr & operator= ( copy_ptr<D> 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( *( this->operator->() ) );
}

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

}; // copy_ptr<T>

Please, let me know what you think.

Have been looking through your code and used this main.cpp

#include<iostream>

#include<cctype>
#include"copy_ptr.h"

using namespace std;


class Base {
public:
Base():ch('*'){
Count++;
}
Base(const Base&):ch('*'){
Count++;
}
virtual char show(){
return ch;
}
virtual ~Base(){Count--;}
virtual void v()const=0;
static unsigned long Count;
private:
char ch;
};
unsigned long Base::Count(0);

/// this will give us lots of derived classes
template<char ch_>
class Derived :public Base {
public:
Derived():ch(ch_){
Count++;
}
Derived(const Derived<ch_>&):ch(ch_){
Count++;
}
virtual char show(){
return ch;
}
virtual ~Derived(){Count--;}
virtual void v()const{}
static unsigned long Count;
private:
char ch;

};
template<char ch_>
unsigned long Derived<ch_>::Count(0);

template<char ch_>
class DerivedSquared :public Derived<ch_> {
public:
DerivedSquared():ch(std::tolower(ch_)){
Count++;
}
DerivedSquared(const DerivedSquared<ch_>&):ch(std::tolower(ch_)){
Count++;
}
virtual char show(){return ch;}
virtual ~DerivedSquared(){Count--;}
virtual void v()const{}
static unsigned long Count;
private:
char ch;

};
template<char ch_>
unsigned long DerivedSquared<ch_>::Count(0);




int main() {
char ch;

copy_ptr<Derived<'B'> > theDPA(new DerivedSquared<'B'>);
cout << theDPA->show();

copy_ptr<Base> theDPA2(theDPA);
cout << theDPA2->show();

cout << (*theDPA).show();
cout << (*theDPA2).show();

copy_ptr<const Base> theConstPtr(theDPA2);


cin >>ch;
}


I get an error with

copy_ptr<const Base> theConstPtr(theDPA2);

the error (in msvc) is

error C2682: cannot use 'dynamic_cast' to convert from 'const Base *'
to 'void *'

This may not be an issue for deep copy like I said previously, so I am
not sure if it is you requirement.

Just some other comments. As a personal preference, since there are two
main conponents to this class, the outwardly facing
part where on the BaseClass is involved and the internal part where
both BaseClass & DerivedClass are coming into play, to
break these apart , hence my policy.

If you decide to add the ability for const can you send me the code ?

Thnx
N
 
K

Kai-Uwe Bux

Posts are getting too long. Let's separate issues.

I forbid myslef from using dynamic_cast, but I would have to try to
remeber the reasons why. But I do have a question.
Isn't it true that dynamic_cast happens at runtime and therefore we
have a overhead, whereas static_cast takes place at compile time, so
we don't. I think the boost is_base combined with boost enable_if lets
us 'error check' at compile time.

If you use

non_polymorphic_class_error< Base > dummy;

then there is a dummy variable that is never used. The compiler will
optimize that away. The dynamic_cast, however, will not be evaluated since
it sits within a sizeof expression. Those are not evaluated, instead the
compiler figures out the correct value magically.

Maybe the best way to check is just to put the line

sizeof( dynamic_cast<void*>( static_cast<Base*>(0) ) );

somewhere. It evaluates to a const-expression without side-effects and
therefore should not create any code.


Best

Kai-Uwe Bux
 
N

Nindi

Kai-Uwe Bux said:
Posts are getting too long. Let's separate issues.



If you use

non_polymorphic_class_error< Base > dummy;

then there is a dummy variable that is never used. The compiler will
optimize that away. The dynamic_cast, however, will not be evaluated since
it sits within a sizeof expression. Those are not evaluated, instead the
compiler figures out the correct value magically.

Maybe the best way to check is just to put the line

sizeof( dynamic_cast<void*>( static_cast<Base*>(0) ) );

somewhere. It evaluates to a const-expression without side-effects and
therefore should not create any code.
I am not sure how it can do this, isn't dynamic_cast a runtime
evaluation ?
 
N

Nindi

Gianni said:
Why do you need such a beast ? i.e. what are the requirements and use case ?

I DID know, I am trying to remember why I needed this, after a while it
just became a curious exercise.
 
E

Earl Purple

A few days a ago I posted my code for a deep copy pointer which
doesn't require the pointee object to have a virtual copy constructor.

Just as well given that there is no such thing as a virtual
constructor.

class A
{
public:
virtual A( const A & ); // illegal. Constructors can't be virtual
};

< rest snipped >
 
K

Kai-Uwe Bux

Nindi said:
I am not sure how it can do this, isn't dynamic_cast a runtime
evaluation ?

So what? The standard says [5.2.7/5-6]:

...
Otherwise, v shall be a pointer to or an lvalue of a polymorphic
type (10.3).

The crucial thing here is the word "shall". It makes this provision into a
diagnosable rule, i.e., since the compiler knows at compile time whether
the argument to dynamic_cast has static type "pointer to polymorphic" the
compiler is required to issue a diagnostic if the provision is violated.
Try:

struct NonPolymorphic {};

int main ( void ) {
sizeof( dynamic_cast<void*>( static_cast<NonPolymorphic*>(0) ) );
}

You should get a *compile time* error on this one.

On the other hand

struct Polymorphic {

virtual
~Polymorphic ( void ) {}

};

int main ( void ) {
sizeof( dynamic_cast<void*>( static_cast<Polymorphic*>(0) ) );
}

compiles fine. Now, you may worry about runtime overhead. However, since we
embedded the cast within a sizeof expression, we can be sure that it will
not be evaluated. Instead, it is replaced by its result through compiler
magic (i.e., the result is found at compile time).



Best

Kai-Uwe Bux
 
N

Nindi

Kai-Uwe Bux said:
Nindi said:
I am not sure how it can do this, isn't dynamic_cast a runtime
evaluation ?

So what? The standard says [5.2.7/5-6]:

...
Otherwise, v shall be a pointer to or an lvalue of a polymorphic
type (10.3).

The crucial thing here is the word "shall". It makes this provision into a
diagnosable rule, i.e., since the compiler knows at compile time whether
the argument to dynamic_cast has static type "pointer to polymorphic" the
compiler is required to issue a diagnostic if the provision is violated.
Try:

struct NonPolymorphic {};

int main ( void ) {
sizeof( dynamic_cast<void*>( static_cast<NonPolymorphic*>(0) ) );
}

You should get a *compile time* error on this one.

On the other hand

struct Polymorphic {

virtual
~Polymorphic ( void ) {}

};

int main ( void ) {
sizeof( dynamic_cast<void*>( static_cast<Polymorphic*>(0) ) );
}

compiles fine. Now, you may worry about runtime overhead. However, since we
embedded the cast within a sizeof expression, we can be sure that it will
not be evaluated. Instead, it is replaced by its result through compiler
magic (i.e., the result is found at compile time).

I see. I'll have a play about with it.
Thanks
N
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top