Topic: Automating cloning


Author: haberg.REMOVE@matematik.su.se (Hans Aberg)
Date: 1999/06/03
Raw View
In article <3753F1B1.3297@eae.com>, neppert.gernot@eae.com wrote:
>> The problem is if when deriving from a derived class which in its turn is
>> derived from the root. For example
>> template <class Derived>
>> class Root {
>> public:
>>     virtual Root* clone() const { return new Derived(*this); }
>> };
>>
>
>> class Derived1 : public Root<Derived1> { };
>
>How's this gonna work? In function Root<Derived1>::clone you are trying
>to construct a Derived object from a Root<Derived1> object, which is a
>base class of Derived1.

Right. This is the other problem with the template approach: Deriving from
Root<D> produces a class with a name Root<D>, and not only Root, as you
want.

>Anyway, the problem with this template-approach is that you are not
>building any usable class hierarchy. If you implement a virtual function
>to clone objects, you will also want to be able to use references to the
>clonable base class in order to clone objects!

Such a problem can of course be fixed by inserting a new non-template base
class. For example,

class RootBase {
    virtual Root* clone() const = 0;
};

template <class Derived>
class Root : virtual public RootBase {
public:
    virtual Root* clone() const { return new Derived(*this); }
};

Now

class Derived1 : public Root<Derived1> { };

is derived from the RootBase.

Then the problem with resolving the multiple clone()'s when deriving from
Derived1 persists:

class Derived2 : public Root<Derived2>, public Derived1 { };

gets two clone(), one from Root<Derived2>, and one from Derived1, which in
C++ must be resolved by adding a new clone() to Derived2. And then the
whole point of using templates is being lost.

So one then ends up with two solutions, new C++ language support, or in
the lack of that, the use of preprocessing macros.

  Hans Aberg   * Anti-spam: Remove ".REMOVE" from email address.
               * Email: Hans Aberg <haberg.REMOVE@member.ams.org>
               * Home Page: <http://www.matematik.su.se/~haberg/>
               * AMS member listing: <http://www.ams.org/cml/>

      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: haberg.REMOVE@matematik.su.se (Hans Aberg)
Date: 1999/06/03
Raw View
In article <375493C8.A78B89F5@nonexistant.com>, "Robert O'Dowd"
<nospam@nonexistant.com> wrote:
>> class Derived1 : public Root<Derived1> { };
>
>I'm not sure if this is allowed (maybe someone could clarify).

It looks funny, as the compiler might need the size of Derived1 in order
to compute the size of Root<Derived1>, which is needed before the size of
Derived1 can be computed. However, my compiler accepted it.

>> class Derived2 : public Root<Derived2>, public Derived1 { };
>>
>> Then one must sort out the different clone()'s that Derived2 gets. One can
>> do this with a simple macro:
>> #define Class(Name) class Name : public Root<Name>
>> Class(Derived2), public Derived1 { };
>
>Assuming the kind of inheritence you're talking about above is possible,
>you could do this with function overloading, viz
>
>template<class Derived>
>class Root
>{
>   public:
>
>      virtual Derived *clone(Derived *) { return new Derived(*this);};
>};

The problem is that in Derived2, one gets a Root<Derived2>::clone() and a
Derived1::clone(), and the conflict must be resolved in C++ by writing a
new Derived2::clone(). Then there is no point in using inheritance, and
one can just as well use macros directly.

Note however that your definition of clone is wrong, it should return a
pointer to Root and not a pointer to Derived. And I do not know why your
clone() has an extra argument Derived*: Derived is derived from the Root,
and Derived::clone() then already has a polymorhic argument.

Note also that just as a class D has two copy constructors D::D(const D&)
and D::D(D&), one should have two clone operators
    Root* D::clone() const;
    Root* D::clone();

>All this seems like hard work to me though....

Without language support, the clone() must be written in by hand in every
derived class from the root, and if it is forgotten somewhere, the cloning
will produce the wrong object, namely the first base class object which
jas a clone() operator written in.

So it is unfortunate that C++ does not have any kind of language support
either for a clone() directly, or to help it being written in.


>Let's say that X is a base class, Y is publically derived from X, and
>both X
>and Y have copy constructors.  Then I suggest a logic like.
>
>clone(address) will return a clone of the object at address.  An example
>would be ...

>    X *x = new Y;
>    Y *y = clone(x);
>    X *cx = clone(x);

Note that the clone operator needs two arguments, the object cloned, and
the return type. If I write this out in a macro, it looks like

#define define_clone(Root, Derived) \
    Root* clone() const { return new Derived(*this); }

to be fit into every class derived from Root:

class Derived : virtual public Root {
public:
    define_clone(Root, Derived);
};

If one tries the variation you used about with a
    define_clone(Derived, Derived)
then there will be proble with the return type of this clone:
Derived::clone() will not have the same return type as
    define_clone(Root, Root)
so it will not be able to use this as the same virtual function. And the
return value of Derived::clone() must first be converted to a Root value
using a dynamic_cast.

But it might still be possible to provide C++ language support so that
every class D behaves as though it had a
    D* D::clone() const;

Then in the use
    Root *rp = new D();
    Root *rp1 = rp->clone();
the clone() would look up the
    D* D::clone() const
operator, and convert the return value to type Root*.

  Hans Aberg   * Anti-spam: Remove ".REMOVE" from email address.
               * Email: Hans Aberg <haberg.REMOVE@member.ams.org>
               * Home Page: <http://www.matematik.su.se/~haberg/>
               * AMS member listing: <http://www.ams.org/cml/>

      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Lisa Lippincott <lisa_lippincott@advisories.com>
Date: 1999/06/04
Raw View
Hans Aberg's postings about automated cloning suggested to me this (wacky?)
possible extension to the C++ language:

   -- Allow base classes to provide templates which are instantiated
      to produce members of derived classes.  For access control purposes,
      treat such instantiations as members of the base class.

   -- Allow template member functions to be virtual when all the
      template parameters are determined by the derived class.

Putting these together, one can express clonability in a class:

struct Clonable
  {
   template < class Derived >
   virtual Derived *Derived::Clone()
     { return new Derived( *this ); }
  };

A derived class could change change the behavior in all further derived
classes by hiding the base class member:

struct WeirdlyClonable: public Clonable
  {
   template < class FurtherDerived >
   virtual FurtherDerived *FurtherDerived::Clone()
      { return Whatever(); }
  };

Or it could change the behavior in its exact type with a specialization:

struct StrangelyClonable: public Clonable
  {
   using Clonable::Clone;
   template <>
   virtual StrangelyClonable *StrangelyClonable::Clone()
      { return Whatever(); }
  };

So what do people think?  Interesting?  Unworkable?  Bizarre?

                                                --Lisa Lippincott


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: haberg.REMOVE@matematik.su.se (Hans Aberg)
Date: 1999/06/01
Raw View
In article <7i3o5r$qf9$1@nnrp1.deja.com>, Andrei Alexandrescu
<andrewalex@hotmail.com> wrote:
>Did anyone come with a way to automate cloning objects? You know, that
>"Duplicate" method that you write again and again:

The problem is if when deriving from a derived class which in its turn is
derived from the root. For example
template <class Derived>
class Root {
public:
    virtual Root* clone() const { return new Derived(*this); }
};

class Derived1 : public Root<Derived1> { };
class Derived2 : public Root<Derived2>, public Derived1 { };

Then one must sort out the different clone()'s that Derived2 gets. One can
do this with a simple macro:
#define Class(Name) class Name : public Root<Name>
Class(Derived2), public Derived1 { };

However, a C++ language support would be better:

A simple way to do it is by adding the keyword "This", which means the
derived class currently in scope, even though the definition is in some
base class.

With this suggestion, the definition would be
class Root {
public:
    virtual Root* clone() const { return new This(*this); }
};

When writing

class Derived : public Root { };

the compiler would replace This with Derived, and then insert the function
definition of clone() so obtained into class Derived.

  Hans Aberg   * Anti-spam: Remove ".REMOVE" from email address.
               * Email: Hans Aberg <haberg.REMOVE@member.ams.org>
               * Home Page: <http://www.matematik.su.se/~haberg/>
               * AMS member listing: <http://www.ams.org/cml/>

      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Gernot <neppert.gernot@eae.com>
Date: 1999/06/01
Raw View
Hans Aberg wrote:
> The problem is if when deriving from a derived class which in its turn is
> derived from the root. For example
> template <class Derived>
> class Root {
> public:
>     virtual Root* clone() const { return new Derived(*this); }
> };
>

> class Derived1 : public Root<Derived1> { };

How's this gonna work? In function Root<Derived1>::clone you are trying
to construct a Derived object from a Root<Derived1> object, which is a
base class of Derived1.

Anyway, the problem with this template-approach is that you are not
building any usable class hierarchy. If you implement a virtual function
to clone objects, you will also want to be able to use references to the
clonable base class in order to clone objects!
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Robert O'Dowd" <nospam@nonexistant.com>
Date: 1999/06/03
Raw View
Hans Aberg wrote:
>
> In article <7i3o5r$qf9$1@nnrp1.deja.com>, Andrei Alexandrescu
> <andrewalex@hotmail.com> wrote:
> >Did anyone come with a way to automate cloning objects? You know, that
> >"Duplicate" method that you write again and again:
>
> The problem is if when deriving from a derived class which in its turn is
> derived from the root. For example
> template <class Derived>
> class Root {
> public:
>     virtual Root* clone() const { return new Derived(*this); }
> };
>
> class Derived1 : public Root<Derived1> { };

I'm not sure if this is allowed (maybe someone could clarify).

> class Derived2 : public Root<Derived2>, public Derived1 { };
>
> Then one must sort out the different clone()'s that Derived2 gets. One can
> do this with a simple macro:
> #define Class(Name) class Name : public Root<Name>
> Class(Derived2), public Derived1 { };

Assuming the kind of inheritence you're talking about above is possible,
you could do this with function overloading, viz

template<class Derived>
class Root
{
   public:

      virtual Derived *clone(Derived *) { return new Derived(*this);};
};

All this seems like hard work to me though....


>
> However, a C++ language support would be better:
>
[A suggested approach snipped]

Language support for a clone keyword has been on my wish-list for a long
time.

I suggest something like....

Let's say that X is a base class, Y is publically derived from X, and
both X
and Y have copy constructors.  Then I suggest a logic like.

clone(address) will return a clone of the object at address.  An example
would be ...

main()
{
    X *x = new Y;

    Y *y = clone(x);
    X *cx = clone(x);

       // whatever

    delete x;
    delete y;
    delete cx;
    return 0;
}

The effect of clone(reference) would be analogous.

Obviously, a formal specification of the language support would need to
consider
things like

a)   What happens if we try to clone an object that has no copy
constructor?
       I suggest throwing an exception.

b)   What happens if we override operator new/delete?

c)   What happens if we try to clone a NULL pointer?


-<Automagically included trailer>
Robert O'Dowd                       Ph    +61 (8) 8259 6546
MOD/DSTO                     Fax    +61 (8) 8259 5139
P.O. Box 1500                       Email:
robert.odowd@dsto.defence.gov.au
Salisbury, South Australia, 5108

Disclaimer: Opinions above are mine and may be worth what you paid for
them
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]