deep/real copy of derived objects

Discussion in 'C++' started by Hicham Mouline, Aug 20, 2010.

  1. Hello

    I have a base struct B from which 2 struct D1 and D2 derive publicly. A 3rd
    struct D3 derives from D1.

    This is data aggregation.

    A "manager" class needs to store copies of these objects when its ctor is
    called, the ctor takes as an argument const B&.
    How can I take a "real" copy of the argument?
    What should I store as a member of "manager"? ideally just a base pointer or
    reference?

    Regards,
    Hicham Mouline, Aug 20, 2010
    #1
    1. Advertising

  2. Hicham Mouline <>, on 20/08/2010 00:11:36, wrote:

    > Hello
    >
    > I have a base struct B from which 2 struct D1 and D2 derive publicly. A 3rd
    > struct D3 derives from D1.
    >
    > This is data aggregation.
    >
    > A "manager" class needs to store copies of these objects when its ctor is
    > called, the ctor takes as an argument const B&.
    > How can I take a "real" copy of the argument?
    > What should I store as a member of "manager"? ideally just a base pointer or
    > reference?


    I'm not sure I fully understand your question, so cope with my response.

    You're dealing with polymorphic objects and "manager" only receives a
    reference to their base, ok so far.

    Now you want to create a "real" copy of that argument, and with that I
    suppose you mean that if I pass a D3& to it as a B&, then "manager"
    should be able to create a D3 copy out of that B& - note the absence of
    the ampersand there.

    OK, if "manager" can be aware of all that hierarchy and if you're not
    concerned with some runtime overhead, then you could interrogate B& for
    its real type by dynamically casting it, in turn, to a pointer to each
    derived type - it is important to cast to a pointer so that you can
    check it for nullity afterwards, whereas casting to another reference
    equates to an assertion that would lead to a bad_cast exception in the
    failure case (which in turn would make your code unnecessarily more
    convoluted).

    The other option is to add a clone() method to the whole hierarchy and
    going on ignoring the stored clone's type, at the price of having to
    delete it at the appropriate moment - while the previous method allows
    you storing automatic instances of those copies, but the difference
    wouldn't be that signifying as you could store the pointer in a "smart"
    way in any case.

    I hope the above is clear enough, otherwise I'll post some code to
    illustrate my points - assuming I didn't misinterpret your question :)

    --
    FSC - http://userscripts.org/scripts/show/59948
    http://fscode.altervista.org - http://sardinias.com
    Francesco S. Carta, Aug 20, 2010
    #2
    1. Advertising

  3. * Hicham Mouline, on 20.08.2010 01:11:
    >
    > I have a base struct B from which 2 struct D1 and D2 derive publicly. A 3rd
    > struct D3 derives from D1.
    >
    > This is data aggregation.
    >
    > A "manager" class


    Oops, that's a design smell (or stench).

    Some classes are conceptuelly "doers", like a functor class, a thread class or a
    class encapsulating a routine hierarchy.

    However, such a "doer" class is characterized by having an operator() or some
    method called "execute" or "run" or such, that is, an instance is meant to
    provide a service like a function, produced by a member function call.

    A "manager" class is not that kind, it's most often just a hodgepodge collection
    of unrelated responsibilities and unrelated knowledge snippets.

    Think about responsibilities, think about what knowledge is needed for each
    responsibility, re-factor as necessary.


    > needs to store copies of these objects when its ctor is
    > called, the ctor takes as an argument const B&.
    > How can I take a "real" copy of the argument?


    See the FAQ item about cloning. Heads-up: the FAQ uses the term "virtual
    construction". The FAQ is about the only trustworthy source of info that uses
    that term for cloning, although some authorities in OO have done that (earlier).


    > What should I store as a member of "manager"? ideally just a base pointer or
    > reference?


    A smart pointer.

    But I think you should get rid of the "manager". ;-)


    Cheers & hth.,

    - Alf

    --
    blog at <url: http://alfps.wordpress.com>
    Alf P. Steinbach /Usenet, Aug 20, 2010
    #3
  4. Hicham Mouline

    Jorgen Grahn Guest

    On Fri, 2010-08-20, Alf P. Steinbach /Usenet wrote:
    > * Hicham Mouline, on 20.08.2010 01:11:
    >>
    >> I have a base struct B from which 2 struct D1 and D2 derive publicly. A 3rd
    >> struct D3 derives from D1.
    >>
    >> This is data aggregation.
    >>
    >> A "manager" class

    >
    > Oops, that's a design smell (or stench).
    >
    > Some classes are conceptuelly "doers", like a functor class, a thread class or a
    > class encapsulating a routine hierarchy.
    >
    > However, such a "doer" class is characterized by having an operator() or some
    > method called "execute" or "run" or such, that is, an instance is meant to
    > provide a service like a function, produced by a member function call.
    >
    > A "manager" class is not that kind, it's most often just a hodgepodge collection
    > of unrelated responsibilities and unrelated knowledge snippets.


    Right. When I see the all-too-frequent "FooManager" classes, their
    name usually means "This things owns a lot of Foo instances, but I'm
    not sure what other responsibilities it has. It's more important
    than a mere container, though!"

    > Think about responsibilities, think about what knowledge is needed for each
    > responsibility, re-factor as necessary.


    And try to find a class name that fits those responsibilities.

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
    Jorgen Grahn, Aug 20, 2010
    #4
  5. "Alf P. Steinbach /Usenet" <> wrote in
    message news:i4khnj$ouv$-september.org...
    >* Hicham Mouline, on 20.08.2010 01:11:
    >>
    >> I have a base struct B from which 2 struct D1 and D2 derive publicly. A
    >> 3rd
    >> struct D3 derives from D1.
    >>
    >> This is data aggregation.
    >>
    >> A "manager" class

    >
    > Oops, that's a design smell (or stench).
    >
    > Some classes are conceptuelly "doers", like a functor class, a thread
    > class or a class encapsulating a routine hierarchy.
    >
    > However, such a "doer" class is characterized by having an operator() or
    > some method called "execute" or "run" or such, that is, an instance is
    > meant to provide a service like a function, produced by a member function
    > call.
    >
    > A "manager" class is not that kind, it's most often just a hodgepodge
    > collection of unrelated responsibilities and unrelated knowledge snippets.
    >
    > Think about responsibilities, think about what knowledge is needed for
    > each responsibility, re-factor as necessary.
    >
    >
    >> needs to store copies of these objects when its ctor is
    >> called, the ctor takes as an argument const B&.
    >> How can I take a "real" copy of the argument?

    >
    > See the FAQ item about cloning. Heads-up: the FAQ uses the term "virtual
    > construction". The FAQ is about the only trustworthy source of info that
    > uses that term for cloning, although some authorities in OO have done that
    > (earlier).
    >
    >
    >> What should I store as a member of "manager"? ideally just a base pointer
    >> or
    >> reference?

    >
    > A smart pointer.
    >
    > But I think you should get rid of the "manager". ;-)
    >
    >
    > Cheers & hth.,
    >
    > - Alf


    Thanks,
    You are definitely right.
    The full story is this:

    I have daily measures of different experiment types taken over years.
    The "manager" class is called historical_data that contains the list of 20
    years of daily measures.
    historical_data stores its own experiment object instance. Indeed, these
    instances exist only within the scope of historical_data.

    class historical_data {
    historical_data(const experiment&, begin, end);
    historical_data(const historical_data&);
    private:
    historical_data(const experiment&);
    const experiment // what here a const-ref pointer smart pointer ?
    std::list<entry> data_; // an entry contains a boost::gregorian::date and
    the measure (a set of doubles, same for all experiments)
    };

    After a 1st iteration, it appeared then that I have different types of
    experiments.These experiments are just struct like

    struct experiment1 {
    double d;
    int i;
    };

    they all share a couple fields which I've put in a base class
    "experiment_measure" and specific fields to each.

    Different historical datas could be for instance traversed together, or
    merged (to get the intersection of identical dates)....
    A historical data can be plot, can be viewed.

    Should I change historical_data to have 1 different class for each different
    experiment?
    Should I change the experiment class hierarchy to implement a clone method?

    I'll take a look at the FAQ re virtual construction.

    Thanks,
    Hicham Mouline, Aug 21, 2010
    #5
  6. Hicham Mouline <>, on 21/08/2010 23:56:00, wrote:

    > "Alf P. Steinbach /Usenet"<> wrote in
    > message news:i4khnj$ouv$-september.org...
    >> * Hicham Mouline, on 20.08.2010 01:11:
    >>>
    >>> I have a base struct B from which 2 struct D1 and D2 derive publicly. A
    >>> 3rd
    >>> struct D3 derives from D1.
    >>>
    >>> This is data aggregation.
    >>>
    >>> A "manager" class

    >>
    >> Oops, that's a design smell (or stench).
    >>
    >> Some classes are conceptuelly "doers", like a functor class, a thread
    >> class or a class encapsulating a routine hierarchy.
    >>
    >> However, such a "doer" class is characterized by having an operator() or
    >> some method called "execute" or "run" or such, that is, an instance is
    >> meant to provide a service like a function, produced by a member function
    >> call.
    >>
    >> A "manager" class is not that kind, it's most often just a hodgepodge
    >> collection of unrelated responsibilities and unrelated knowledge snippets.
    >>
    >> Think about responsibilities, think about what knowledge is needed for
    >> each responsibility, re-factor as necessary.
    >>
    >>
    >>> needs to store copies of these objects when its ctor is
    >>> called, the ctor takes as an argument const B&.
    >>> How can I take a "real" copy of the argument?

    >>
    >> See the FAQ item about cloning. Heads-up: the FAQ uses the term "virtual
    >> construction". The FAQ is about the only trustworthy source of info that
    >> uses that term for cloning, although some authorities in OO have done that
    >> (earlier).
    >>
    >>
    >>> What should I store as a member of "manager"? ideally just a base pointer
    >>> or
    >>> reference?

    >>
    >> A smart pointer.
    >>
    >> But I think you should get rid of the "manager". ;-)
    >>
    >>
    >> Cheers& hth.,
    >>
    >> - Alf

    >
    > Thanks,
    > You are definitely right.


    So you're thinking about eliminating "historical_data"? (as you're going
    to say below, that's your "manager")

    > The full story is this:
    >
    > I have daily measures of different experiment types taken over years.
    > The "manager" class is called historical_data that contains the list of 20
    > years of daily measures.
    > historical_data stores its own experiment object instance. Indeed, these
    > instances exist only within the scope of historical_data.


    So "historical_data" (formerly the "manager") is the only owner of your
    "experiment" instances (either directly or indirectly), where
    "experiment" should be the former "B" class (the base class) in your "B,
    D1, D2, D3" hierarchy above.

    In that case you should either keep the "historical_data" or you should
    create another container for your "experiment"s.

    If the "experiment" instances must be grouped and handled separately, I
    think you could keep "historical_data".

    > class historical_data {
    > historical_data(const experiment&, begin, end);


    What are "begin" and "end"?

    > historical_data(const historical_data&);
    > private:
    > historical_data(const experiment&);
    > const experiment // what here a const-ref pointer smart pointer ?


    A smart pointer, for the details you have given so far, without
    completely revolutionizing your design. But read below.

    > std::list<entry> data_; // an entry contains a boost::gregorian::date and
    > the measure (a set of doubles, same for all experiments)
    > };
    >
    > After a 1st iteration, it appeared then that I have different types of
    > experiments.These experiments are just struct like
    >
    > struct experiment1 {
    > double d;
    > int i;
    > };
    >
    > they all share a couple fields which I've put in a base class
    > "experiment_measure" and specific fields to each.


    I do not understand the above... that's the advantage of posting
    complete and meaningful code at the first post: it avoids
    misunderstandings and unneeded question/answer exchanges about something
    that should be straight and direct C++.

    > Different historical datas could be for instance traversed together, or
    > merged (to get the intersection of identical dates)....


    These are very important aspects which should be analyzed in their
    actual implementation, as above, with straight and direct C++.

    > A historical data can be plot, can be viewed.


    That is secondary, you should separate data _implementation_
    (allocation, aggregation, ownership and so on) from data _presentation_
    (plotting, view and so on).

    > Should I change historical_data to have 1 different class for each different
    > experiment?


    I would never do something like that. I would make a template out of it,
    before duplicating code by hand.

    > Should I change the experiment class hierarchy to implement a clone method?


    It depends on the amount of different experiments and how much they
    differ from each other (as for their actual implementation).

    I'd really like to see more of what you have coded so far, I like to
    create code based on other's implementations and targets, just give me
    some more details and some more code to munch and I would show you a
    couple of different approaches - if you are interested, of course... the
    subject is quite interesting for me.

    --
    FSC - http://userscripts.org/scripts/show/59948
    http://fscode.altervista.org - http://sardinias.com
    Francesco S. Carta, Aug 22, 2010
    #6
  7. "Alf P. Steinbach /Usenet" <> wrote in
    message news:i4khnj$ouv$-september.org...
    >* Hicham Mouline, on 20.08.2010 01:11:
    >>

    > See the FAQ item about cloning. Heads-up: the FAQ uses the term "virtual
    > construction". The FAQ is about the only trustworthy source of info that
    > uses that term for cloning, although some authorities in OO have done that
    > (earlier).
    >

    Is there a way to obtain this clone() function
    1.without changing the class hierarchy at all?
    2.a smaller change than what's presented in the FAQ

    regards,
    Hicham Mouline, Aug 28, 2010
    #7
  8. Hicham Mouline

    Kai-Uwe Bux Guest

    Hicham Mouline wrote:

    >
    > "Alf P. Steinbach /Usenet" <> wrote in
    > message news:i4khnj$ouv$-september.org...
    >>* Hicham Mouline, on 20.08.2010 01:11:
    >>>

    >> See the FAQ item about cloning. Heads-up: the FAQ uses the term "virtual
    >> construction". The FAQ is about the only trustworthy source of info that
    >> uses that term for cloning, although some authorities in OO have done
    >> that (earlier).
    >>

    > Is there a way to obtain this clone() function
    > 1.without changing the class hierarchy at all?
    > 2.a smaller change than what's presented in the FAQ


    To some degree: instead of making the class clonable, you can use smart
    pointers with deep copy semantics. Search the archives for "clone_ptr" or
    "copy_ptr" and you should find some hints.


    Best

    Kai-Uwe Bux
    Kai-Uwe Bux, Aug 28, 2010
    #8
  9. "Kai-Uwe Bux" <> wrote in message
    news:i5a9ib$n37$...
    > To some degree: instead of making the class clonable, you can use smart
    > pointers with deep copy semantics. Search the archives for "clone_ptr" or
    > "copy_ptr" and you should find some hints.
    >
    >
    > Best
    >
    > Kai-Uwe Bux


    I found this
    http://groups.google.com/group/comp...b857444d?lnk=gst&q=clone_ptr#addaeb3bb857444d
    with your code from Dec 2006.

    I've also found this
    http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c10407

    My case involves a hiearchy of structs. I assume I need to make the base
    dtor virtual for the clone_ptr deleter to work.

    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.

    regards,
    Hicham Mouline, Aug 28, 2010
    #9
  10. Hicham Mouline

    Kai-Uwe Bux Guest

    Hicham Mouline wrote:

    >
    > "Kai-Uwe Bux" <> wrote in message
    > news:i5a9ib$n37$...
    >> To some degree: instead of making the class clonable, you can use smart
    >> pointers with deep copy semantics. Search the archives for "clone_ptr" or
    >> "copy_ptr" and you should find some hints.
    >>
    >>
    >> Best
    >>
    >> Kai-Uwe Bux

    >
    > I found this
    >

    http://groups.google.com/group/comp...b857444d?lnk=gst&q=clone_ptr#addaeb3bb857444d
    > with your code from Dec 2006.
    >
    > I've also found this
    > http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c10407
    >
    > My case involves a hiearchy of structs. I assume I need to make the base
    > dtor virtual for the clone_ptr deleter to work.


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

    In short, the use of clone_ptr<> has to be taken into account at the point
    of creating objects. You cannot do it as an afterthought: once type
    information is lost, it impossible to recover with standard means. (Maybe,
    intimate knowledge of a given implementation can allow you to read out
    vtable information; but that I would not know about.)


    Best

    Kai-Uwe Bux
    Kai-Uwe Bux, Aug 28, 2010
    #10
  11. "Kai-Uwe Bux" <> wrote in message
    news:i5bgel$16h$...
    > 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.

    Would you going with your implementation from Dec 2006, or would you advise
    a different one?

    regards,
    Hicham Mouline, Aug 29, 2010
    #11
  12. Hicham Mouline

    Kai-Uwe Bux Guest

    Hicham Mouline wrote:

    >
    > "Kai-Uwe Bux" <> wrote in message
    > news:i5bgel$16h$...
    >> 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 ?
    &copy_ptr<D>::default_null :
    &copy_ptr<D>::default_copy ) )
    , the_del ( conversion_deleter<D>
    ( ptr == 0 ?
    &copy_ptr<D>::default_do_nothing :
    &copy_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 ?
    &copy_ptr<D>::default_do_nothing :
    &copy_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
    Kai-Uwe Bux, Aug 29, 2010
    #12
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. VisionSet
    Replies:
    8
    Views:
    4,892
    Tris Orendorff
    Apr 29, 2004
  2. Alex
    Replies:
    2
    Views:
    1,218
  3. Replies:
    26
    Views:
    2,108
    Roland Pibinger
    Sep 1, 2006
  4. Replies:
    1
    Views:
    393
    myork
    May 23, 2007
  5. Replies:
    1
    Views:
    385
    Victor Bazarov
    May 23, 2007
Loading...

Share This Page