From: Hicham Mouline on
Hello,

I have

struct params {
double field1;
double field2;
virtual ~params() {}
};

struct params1 : public params {
double fielda;
double fieldb;
};

I have dozens of structs derived from params.

I work mostly with params& and params* and const versions.
Given a
const params& p,
I wish to clone the object referred to it.

If p happens to params32, I wish to obtain a new instance of that object (in that all the fields are copied to the new object, including the fields specific to params32 of course)

I could add a non pure virtual function "params* clone() const" but then I'd have to add a clone() to all of the params_i

Is there a way to obtain this without touching at all any of the derived structs?

regards,


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Andy Venikov on
Hicham Mouline wrote:
> Hello,
> I have
> struct params {
> double field1;
> double field2;
> virtual ~params() {}
> };
> struct params1 : public params {
> double fielda;
> double fieldb;
> };
> I have dozens of structs derived from params.
> I work mostly with params& and params* and const versions.
> Given a
> const params& p,
> I wish to clone the object referred to it.
> If p happens to params32, I wish to obtain a new instance of that object (in that all the fields are copied to the new object, including the fields specific to params32 of course)
> I could add a non pure virtual function "params* clone() const" but then I'd have to add a clone() to all of the params_i
> Is there a way to obtain this without touching at all any of the derived structs?
> regards,

I'm afraid not.

Is your concern that you don't want to define clone() for every derived class?

If it's OK for you to change each derived class declaration from

struct derivedXXX : public params
to
struct derivedXXX : public clonable_params<derivedXXX>

Then you won't have to define clone() for every class. Just once in the base class.

I.e.

struct params
{
protected:
virtual params * do_clone() const =0;

public:
params * clone() const
{
return do_clone();
}
//
};

template <typename T>
struct clonable_params : public params
{
T * do_clone() const
{
return new T(*static_cast<T*>(this));
}
};


HTH,
Andy.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Juan Pedro Bolivar Puente on
On 13/05/10 22:13, Hicham Mouline wrote:
> Hello,
>
> I have
>
> struct params {
> double field1;
> double field2;
> virtual ~params() {}
> };
>
> struct params1 : public params {
> double fielda;
> double fieldb;
> };
>
> I have dozens of structs derived from params.
>
> I work mostly with params& and params* and const versions.
> Given a
> const params& p,
> I wish to clone the object referred to it.
>
> If p happens to params32, I wish to obtain a new instance of that object (in that all the fields are copied to the new object, including the fields specific to params32 of course)
>
> I could add a non pure virtual function "params* clone() const" but then I'd have to add a clone() to all of the params_i
>
> Is there a way to obtain this without touching at all any of the derived structs?
>

There isn't. But you can make a semi-automatic way to solving the problem:

template <class T>
struct copy_cloneable : public T
{
virtual copy_cloneable* clone() { return new copy_cloneable (*this); }
virtual ~copy_cloneable () {}
};

....

And for every params:

struct params_N_def : public params_M
{
...
};

typedef copy_cloneable<params_N_def> params_N;

Which are changes that you might even be able to automatize.

JP

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Juan Pedro Bolivar Puente on

>
> If it's OK for you to change each derived class declaration from
>
> struct derivedXXX : public params
> to
> struct derivedXXX : public clonable_params<derivedXXX>
>
> Then you won't have to define clone() for every class. Just once in the
> base class.
>
> I.e.
>
> struct params
> {
> protected:
> virtual params * do_clone() const =0;
>
> public:
> params * clone() const
> {
> return do_clone();
> }
> //
> };
>
> template <typename T>
> struct clonable_params : public params
> {
> T * do_clone() const
> {
> return new T(*static_cast<T*>(this));
> }
> };
>
>

The problem that I see with your solution is that it is limited to one
step hierarchies and that the cloning method overriding lacks covariance.

There is actually no way to make a solutions based on inheriting from
the "auto-clone" class that can respect covariance because in that case
the "auto-clone"d type is incomplete in the context of the clone()
function definition and therefore the compiler complains about covariance.

As an example:

template <class T, class Next = void>
struct copy_cloneable : public Next
{
virtual T* clone() { return new T (*static_cast<T*>(this)); }
virtual ~copy_cloneable () {}
};

template <class T>
struct copy_cloneable<T, void>
{
virtual T* clone() { return new T (*static_cast<T*>(this)); }
virtual ~copy_cloneable () {}
};

struct A : public copy_cloneable<A> {};
struct B : public copy_cloneable<B, A> {};

Yields:

test.cpp: In instantiation of 'copy_cloneable<B, A>':
test.cpp:19: instantiated from here
test.cpp:7: error: invalid covariant return type for 'T*
copy_cloneable<T, Next>::clone() [with T = B, Next = A]'
test.cpp:14: error: overriding 'T* copy_cloneable<T, void>::clone()
[with T = A]'

With GCC.

(Yeah, actually I built the code thinking that it was a good solution
just to realize about this partial definition problem, it's too late in
my time zone :p)

JP

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Andy Venikov on
Juan Pedro Bolivar Puente wrote:
<snip>

> There is actually no way to make a solutions based on inheriting from
> the "auto-clone" class that can respect covariance because in that case
> the "auto-clone"d type is incomplete in the context of the clone()
> function definition and therefore the compiler complains about covariance.
> As an example:
> template <class T, class Next = void>
> struct copy_cloneable : public Next
> {
> virtual T* clone() { return new T (*static_cast<T*>(this)); }
> virtual ~copy_cloneable () {}
> };
> template <class T>
> struct copy_cloneable<T, void>
> {
> virtual T* clone() { return new T (*static_cast<T*>(this)); }
> virtual ~copy_cloneable () {}
> };
> struct A : public copy_cloneable<A> {};
> struct B : public copy_cloneable<B, A> {};
> Yields:
> test.cpp: In instantiation of 'copy_cloneable<B, A>':
> test.cpp:19: instantiated from here
> test.cpp:7: error: invalid covariant return type for 'T*
> copy_cloneable<T, Next>::clone() [with T = B, Next = A]'
> test.cpp:14: error: overriding 'T* copy_cloneable<T, void>::clone()
> [with T = A]'
> With GCC.
> (Yeah, actually I built the code thinking that it was a good solution
> just to realize about this partial definition problem, it's too late in
> my time zone :p)
> JP


Huh, interesting point. I didn't compile the code myself that's why I didn't see this issue. You're right, there is no way to do co-variant returns with my solution. They are not strictly needed with my approach though, because I resorted to Non-Virtual Interface idiom and you can't do covariant returns with it anyway.

I like your solution, except that in C++03 you'll hit a road bump if the params class has a non-trivial constructor. In my solution I lay the burden of writing all non-trivial constructors on params_XXX classes that can just call the correct non-trivial constructor of params class. In you solution it's just not possible.

In C++0x on the other hand, you'll be able to inherit constructors, so your solution will work great there.

Andy.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]