Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2013-09

Clone a instance of object

23 messages · 7 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Zaher Dirkey

Hi,
I have objects (classes) derived from base one

TBaseObject = class(TObject)
public
   constructor Create; virtual;
   function Clone:TBaseObject;
end;

TMyObject = class(TBaseObject)
my fields here
end;

o1, o2:TmyObject;
o1  already created;
o2 := o1.Clone;

in Clone i want to create new object from TmyObject but in base object in Clone method,like this

TBaseObject.Clone:TBaseObject;
begin
  Result:=TBaseObject(ClassType).Create;//I know it is wrong here
end;

A
​ny good idea?​

I am using last revision of Lazarus, FPC 2.6 on Windows XP SP3 Best Regards Zaher Dirkey

Reply by email

Sven Barth
to Zaher Dirkey
"Zaher Dirkey" wrote 20 lines
Hi, I have objects (classes) derived from base one TBaseObject = class(TObject) public constructor Create; virtual; function Clone:TBaseObject; end; TMyObject = class(TBaseObject) my fields here end; o1, o2:TmyObject; o1 already created; o2 := o1.Clone; in Clone i want to create new object from TmyObject but in base object in

Clone method,like this

Show quoted text 8 lines
TBaseObject.Clone:TBaseObject; begin Result:=TBaseObject(ClassType).Create;//I know it is wrong here end; A ny good idea?

You need to add a "Result.Assign(Self)" to your clone call and implement a virtually inherited "Assign" method in each class where you copy all fields. This is how "TPersistent" and its descendants do it as well.

Regards,
Sven

Reply by email

Zaher Dirkey
to Sven Barth
Sven Barth wrote
You need to add a "Result.Assign(Self)" to your clone call and implement a virtually inherited "Assign" method in each class where you copy all fields. This is how "TPersistent" and its descendants do it as well.

​Yes I added assign, but as example above, it is crashed when creating it

Result:=TBaseObject(ClassType).Create;
I am using last revision of Lazarus, FPC 2.6 on Windows XP SP3 Best Regards Zaher Dirkey

Reply by email

Michael Van Canneyt
to Zaher Dirkey: I have objects (classes) derived from base one TBaseObject = class(TObject) public…
Zaher Dirkey wrote 24 lines
Hi, I have objects (classes) derived from base one TBaseObject = class(TObject) public    constructor Create; virtual;    function Clone:TBaseObject; end; TMyObject = class(TBaseObject)    my fields here end; o1, o2:TmyObject; o1  already created; o2 := o1.Clone; in Clone i want to create new object from TmyObject but in base object in Clone method,like this TBaseObject.Clone:TBaseObject; begin   Result:=TBaseObject(ClassType).Create;//I know it is wrong here end;

This is wrong. You are typecasting a class reference to an object instance.

The following works:

{$mode objfpc}
Type
   TBaseObject = class(TObject)
   public
   constructor Create; virtual;
   function Clone:TBaseObject;
   end;
   TBaseObjectClass = Class of TBaseObject;
constructor TBaseObject.Create;
begin
end;
Function TBaseObject.Clone:TBaseObject;
Var
   C : TBaseObjectClass;
begin
   C:=TBaseObjectClass(ClassType);
   Result:=C.Create;
end;
Type
   TMyObject = class(TBaseObject)
   //   my fields here
   end;
Var
o1, o2:TmyObject;
begin
   o1:=TMyObject.Create;
   o2 := o1.Clone as TmyObject;
end.

Michael.

Reply by email

patspiper
to Michael Van Canneyt
Michael Van Canneyt wrote 30 lines
This is wrong. You are typecasting a class reference to an object instance. The following works: {$mode objfpc} Type TBaseObject = class(TObject) public constructor Create; virtual; function Clone:TBaseObject; end; TBaseObjectClass = Class of TBaseObject; constructor TBaseObject.Create; begin end; Function TBaseObject.Clone:TBaseObject; Var C : TBaseObjectClass; begin C:=TBaseObjectClass(ClassType); Result:=C.Create; end;
What would happen if Result := Self.ClassType.Create were used instead?
Show quoted text 13 lines
Type TMyObject = class(TBaseObject) // my fields here end; Var o1, o2:TmyObject; begin o1:=TMyObject.Create; o2 := o1.Clone as TmyObject; end.

Stephano

Reply by email

Michael Van Canneyt
to patspiper
patspiper wrote 31 lines
On 23/09/13 10:02, Michael Van Canneyt wrote: > > > This is wrong. You are typecasting a class reference to an object instance. > > The following works: > > {$mode objfpc} > > Type > TBaseObject = class(TObject) > public > constructor Create; virtual; > function Clone:TBaseObject; > end; > TBaseObjectClass = Class of TBaseObject; > > constructor TBaseObject.Create; > > begin > end; > > Function TBaseObject.Clone:TBaseObject; > > Var > C : TBaseObjectClass; > begin > C:=TBaseObjectClass(ClassType); > Result:=C.Create; > end; What would happen if Result := Self.ClassType.Create were used instead?

The wrong constructor would be called.
Classtype is equivalend to 'Class of TObject'.

The typecast is needed to ensure that the TBaseObject.Create constructor is called. Your basic polymorphism problem...

Michael.

Reply by email

Zaher Dirkey
to Michael Van Canneyt: This is wrong. You are typecasting a class reference to an object instance. The following…

On Mon, Sep 23, 2013 at 10:02 AM, Michael Van Canneyt <

Show quoted text 9 lines
wrote: Var C : TBaseObjectClass; begin C:=TBaseObjectClass(ClassType)**; Result:=C.Create; end;

​Yes, that help me, thanks.

Now about assigning I must repeat it (assign) for every child class copy its field members, is there any trick to copy the values like assigning record var to another record var?
I know it is kind of strange way :P

​Thanks​

​Zaher Dirkey​

Reply by email

Michael Van Canneyt
to Zaher Dirkey
Zaher Dirkey wrote 15 lines
On Mon, Sep 23, 2013 at 10:02 AM, Michael Van Canneyt wrote: Var   C : TBaseObjectClass; begin   C:=TBaseObjectClass(ClassType);   Result:=C.Create; end; ​Yes, that help me, thanks. Now about assigning I must repeat it (assign) for every child class copy its field members, is there any trick to copy the values like assigning record var to another record var? I know it is kind of strange way :P

There is no such safe mechanism.

The best you can do is make the properties published, and write an RTTI lookup routine that copies all Published values. I've done this several times, it works well for most situations.

Michael.

Reply by email

Zaher Dirkey
to Michael Van Canneyt

On Mon, Sep 23, 2013 at 12:46 PM, Michael Van Canneyt <

Show quoted text 8 lines
wrote: There is no such safe mechanism. The best you can do is make the properties published, and write an RTTI lookup routine that copies all Published values. I've done this several times, it works well for most situations.

​It is not the fastest way like assigning a record, can we ask it as a feature in FPC ?​

Best Regards
Zaher Dirkey

Reply by email

Mattias Gaertner
to Michael Van Canneyt: There is no such safe mechanism. The best you can do is make the properties published,…
Show quoted text 21 lines
On Mon, 23 Sep 2013, Zaher Dirkey wrote: > > On Mon, Sep 23, 2013 at 10:02 AM, Michael Van Canneyt wrote: > Var >   C : TBaseObjectClass; > begin >   C:=TBaseObjectClass(ClassType); >   Result:=C.Create; > end; > > > ​Yes, that help me, thanks. > > Now about assigning I must repeat it (assign) for every child class copy its field members, is there any trick to copy the values like assigning record var to another record > var? > I know it is kind of strange way :P There is no such safe mechanism.

Well, actually there is a safe mechanism. You use a record to store the class variables. But it looks clumsily:

type
 TMyClass = class(TPersistent)
 private
   type
     TMyClassVars = record
       a,b,c: string;
     end;
 private
   F: TMyClassVars;
 public
   procedure Assign(Source: TPersistent);
   property a: string read F.a write F.a;
 end;
procedure TMyClass.Assign(Source: TPersistent);
begin
  F:=TMyClass(Source).F;
  inherited;
end;
The best you can do is make the properties published, and write an RTTI lookup routine that copies all Published values. I've done this several times, it works well for most situations.

Mattias

Reply by email

Michael Van Canneyt
to Zaher Dirkey: On Mon, Sep 23, 2013 at 12:46 PM, Michael Van Canneyt < ​It is not the fastest way like…
Zaher Dirkey wrote 9 lines
On Mon, Sep 23, 2013 at 12:46 PM, Michael Van Canneyt wrote: There is no such safe mechanism. The best you can do is make the properties published, and write an RTTI lookup routine that copies all Published values. I've done this several times, it works well for most situations. ​It is not the fastest way like assigning a record, can we ask it as a feature in FPC ?​

It's impossible to do correctly.

Michael.

Reply by email

Graeme Geldenhuys
to Michael Van Canneyt: There is no such safe mechanism. The best you can do is make the properties published,…
Michael Van Canneyt wrote
I've done this several times, it works well for most situations.

My I also suggest Zaher takes a look at tiOPF's code to see how they implement class cloning.

Also be careful about the pitfalls of cloning a class. eg: How you want to handle more complex cases. eg: Cloning a class with a reference to another class, or cloning a class with an embedded class (field), etc... tiOPF's implementation allows the developer to decided how such cases should be handled.

Regard,
Graeme.

Reply by email

Frederic Da Vitoria
to Mattias Gaertner: On Mon, 23 Sep 2013 11:46:54 +0200 (CEST) Well, actually there is a safe mechanism. You…

2013/9/23 Mattias Gaertner

Show quoted text 51 lines
On Mon, 23 Sep 2013 11:46:54 +0200 (CEST) Michael Van Canneyt wrote: > > > On Mon, 23 Sep 2013, Zaher Dirkey wrote: > > > > > On Mon, Sep 23, 2013 at 10:02 AM, Michael Van Canneyt < > wrote: > > Var > > C : TBaseObjectClass; > > begin > > C:=TBaseObjectClass(ClassType); > > Result:=C.Create; > > end; > > > > > > Yes, that help me, thanks. > > > > Now about assigning I must repeat it (assign) for every child class copy its field members, is there any trick to copy the values like assigning record var to another record > > var? > > I know it is kind of strange way :P > > There is no such safe mechanism. Well, actually there is a safe mechanism. You use a record to store the class variables. But it looks clumsily: type TMyClass = class(TPersistent) private type TMyClassVars = record a,b,c: string; end; private F: TMyClassVars; public procedure Assign(Source: TPersistent); property a: string read F.a write F.a; end; procedure TMyClass.Assign(Source: TPersistent); begin F:=TMyClass(Source).F; inherited; end;

This wouldn't clone included objects. I mean: if the object to be cloned contains objects, those included objects should probably be cloned too, but putting them into a record wouldn't achieve that.

Frederic Da Vitoria (davitof) Membre de l'April - « promouvoir et défendre le logiciel libre » - http://www.april.org

Reply by email

Zaher Dirkey
to Mattias Gaertner: On Mon, 23 Sep 2013 11:46:54 +0200 (CEST) Well, actually there is a safe mechanism. You…

On Mon, Sep 23, 2013 at 12:57 PM, Mattias Gaertner <

Show quoted text 16 lines
wrote: type TMyClass = class(TPersistent) private type TMyClassVars = record a,b,c: string; end; private F: TMyClassVars; public procedure Assign(Source: TPersistent); property a: string read F.a write F.a; end;

​But in derived classes fields will not assigned​?

Best Regards
Zaher Dirkey

Reply by email

Mattias Gaertner
to Zaher Dirkey
Show quoted text 19 lines
On Mon, Sep 23, 2013 at 12:57 PM, Mattias Gaertner < > wrote: > type > TMyClass = class(TPersistent) > private > type > TMyClassVars = record > a,b,c: string; > end; > private > F: TMyClassVars; > public > procedure Assign(Source: TPersistent); > property a: string read F.a write F.a; > end; > ​But in derived classes fields will not assigned​?

Derived classes can use the same technique.

Mattias

Reply by email

Mattias Gaertner
to Frederic Da Vitoria: 2013/9/23 Mattias Gaertner This wouldn't clone included objects. I mean: if the object to…
Show quoted text 57 lines
2013/9/23 Mattias Gaertner > On Mon, 23 Sep 2013 11:46:54 +0200 (CEST) > Michael Van Canneyt wrote: > > > > > > > On Mon, 23 Sep 2013, Zaher Dirkey wrote: > > > > > > > > On Mon, Sep 23, 2013 at 10:02 AM, Michael Van Canneyt < > > wrote: > > > Var > > > C : TBaseObjectClass; > > > begin > > > C:=TBaseObjectClass(ClassType); > > > Result:=C.Create; > > > end; > > > > > > > > > Yes, that help me, thanks. > > > > > > Now about assigning I must repeat it (assign) for every child class > copy its field members, is there any trick to copy the values like > assigning record var to another record > > > var? > > > I know it is kind of strange way :P > > > > There is no such safe mechanism. > > Well, actually there is a safe mechanism. You use a record to store the > class variables. But it looks clumsily: > > type > TMyClass = class(TPersistent) > private > type > TMyClassVars = record > a,b,c: string; > end; > private > F: TMyClassVars; > public > procedure Assign(Source: TPersistent); > property a: string read F.a write F.a; > end; > > procedure TMyClass.Assign(Source: TPersistent); > begin > F:=TMyClass(Source).F; > inherited; > end; > This wouldn't clone included objects. I mean: if the object to be cloned contains objects, those included objects should probably be cloned too, but putting them into a record wouldn't achieve that.

Anything that needs code to create needs code to clone. You can always creates cases where cloning need special code. But if you limit yourself you can use some of the existing frameworks like TReader/TWriter of tiopf.

Mattias

Reply by email

Michael Van Canneyt
to Mattias Gaertner: On Mon, 23 Sep 2013 11:46:54 +0200 (CEST) Well, actually there is a safe mechanism. You…
Mattias Gaertner wrote 47 lines
On Mon, 23 Sep 2013 11:46:54 +0200 (CEST) Michael Van Canneyt wrote: > > > On Mon, 23 Sep 2013, Zaher Dirkey wrote: > >> >> On Mon, Sep 23, 2013 at 10:02 AM, Michael Van Canneyt wrote: >> Var >>   C : TBaseObjectClass; >> begin >>   C:=TBaseObjectClass(ClassType); >>   Result:=C.Create; >> end; >> >> >> ​Yes, that help me, thanks. >> >> Now about assigning I must repeat it (assign) for every child class copy its field members, is there any trick to copy the values like assigning record var to another record >> var? >> I know it is kind of strange way :P > > There is no such safe mechanism. Well, actually there is a safe mechanism. You use a record to store the class variables. But it looks clumsily: type TMyClass = class(TPersistent) private type TMyClassVars = record a,b,c: string; end; private F: TMyClassVars; public procedure Assign(Source: TPersistent); property a: string read F.a write F.a; end; procedure TMyClass.Assign(Source: TPersistent); begin F:=TMyClass(Source).F; inherited; end;

Yes. But that works only for 'simple' types and still requires you to write code.

For the more general case where you can have arrays, classes, interfaces and whatnot as fields of your object, there is simply no correct way.

Michael.

Reply by email

patspiper
to Michael Van Canneyt
Michael Van Canneyt wrote 61 lines
On Mon, 23 Sep 2013, Mattias Gaertner wrote: > On Mon, 23 Sep 2013 11:46:54 +0200 (CEST) > Michael Van Canneyt wrote: > >> >> >> On Mon, 23 Sep 2013, Zaher Dirkey wrote: >> >>> >>> On Mon, Sep 23, 2013 at 10:02 AM, Michael Van Canneyt >>> wrote: >>> Var >>> C : TBaseObjectClass; >>> begin >>> C:=TBaseObjectClass(ClassType); >>> Result:=C.Create; >>> end; >>> >>> >>> ​Yes, that help me, thanks. >>> >>> Now about assigning I must repeat it (assign) for every child class >>> copy its field members, is there any trick to copy the values like >>> assigning record var to another record >>> var? >>> I know it is kind of strange way :P >> >> There is no such safe mechanism. > > Well, actually there is a safe mechanism. You use a record to store the > class variables. But it looks clumsily: > > type > TMyClass = class(TPersistent) > private > type > TMyClassVars = record > a,b,c: string; > end; > private > F: TMyClassVars; > public > procedure Assign(Source: TPersistent); > property a: string read F.a write F.a; > end; > > procedure TMyClass.Assign(Source: TPersistent); > begin > F:=TMyClass(Source).F; > inherited; > end; Yes. But that works only for 'simple' types and still requires you to write code. For the more general case where you can have arrays, classes, interfaces and whatnot as fields of your object, there is simply no correct way.

Can't the clipboard help in cloning? Similar to the IDE's copy and paste.

Stephano

Reply by email

Frederic Da Vitoria
to Michael Van Canneyt: Yes. But that works only for 'simple' types and still requires you to write code. For the…

2013/9/23 Michael Van Canneyt

Show quoted text 59 lines
On Mon, 23 Sep 2013, Mattias Gaertner wrote: On Mon, 23 Sep 2013 11:46:54 +0200 (CEST) > Michael Van Canneyt wrote: > > On Mon, 23 Sep 2013, Zaher Dirkey wrote: >> >> On Mon, Sep 23, 2013 at 10:02 AM, Michael Van Canneyt < >>> > wrote: >>> Var >>> C : TBaseObjectClass; >>> begin >>> C:=TBaseObjectClass(ClassType)**; >>> Result:=C.Create; >>> end; >>> >>> Yes, that help me, thanks. >>> >>> Now about assigning I must repeat it (assign) for every child class >>> copy its field members, is there any trick to copy the values like >>> assigning record var to another record >>> var? >>> I know it is kind of strange way :P >>> >> >> There is no such safe mechanism. >> > > Well, actually there is a safe mechanism. You use a record to store the > class variables. But it looks clumsily: > > type > TMyClass = class(TPersistent) > private > type > TMyClassVars = record > a,b,c: string; > end; > private > F: TMyClassVars; > public > procedure Assign(Source: TPersistent); > property a: string read F.a write F.a; > end; > > procedure TMyClass.Assign(Source: TPersistent); > begin > F:=TMyClass(Source).F; > inherited; > end; > Yes. But that works only for 'simple' types and still requires you to write code. For the more general case where you can have arrays, classes, interfaces and whatnot as fields of your object, there is simply no correct way.

Isn't there? Maybe it wouldn't be easy, but IIRC Delphi offers something related to this with TComponents and Streams. This Delphi mechanism I knew worked only for properties, but I found references that there existed a wider mechanism in D2010.

Frederic Da Vitoria (davitof) Membre de l'April - « promouvoir et défendre le logiciel libre » - http://www.april.org

Reply by email

Sven Barth
to patspiper: Can't the clipboard help in cloning? Similar to the IDE's copy and paste. Stephano
patspiper wrote 64 lines
On 23/09/13 14:57, Michael Van Canneyt wrote: > > > On Mon, 23 Sep 2013, Mattias Gaertner wrote: > >> On Mon, 23 Sep 2013 11:46:54 +0200 (CEST) >> Michael Van Canneyt wrote: >> >>> >>> >>> On Mon, 23 Sep 2013, Zaher Dirkey wrote: >>> >>>> >>>> On Mon, Sep 23, 2013 at 10:02 AM, Michael Van Canneyt >>>> wrote: >>>> Var >>>> C : TBaseObjectClass; >>>> begin >>>> C:=TBaseObjectClass(ClassType); >>>> Result:=C.Create; >>>> end; >>>> >>>> >>>> ​Yes, that help me, thanks. >>>> >>>> Now about assigning I must repeat it (assign) for every child >>>> class copy its field members, is there any trick to copy the >>>> values like assigning record var to another record >>>> var? >>>> I know it is kind of strange way :P >>> >>> There is no such safe mechanism. >> >> Well, actually there is a safe mechanism. You use a record to store the >> class variables. But it looks clumsily: >> >> type >> TMyClass = class(TPersistent) >> private >> type >> TMyClassVars = record >> a,b,c: string; >> end; >> private >> F: TMyClassVars; >> public >> procedure Assign(Source: TPersistent); >> property a: string read F.a write F.a; >> end; >> >> procedure TMyClass.Assign(Source: TPersistent); >> begin >> F:=TMyClass(Source).F; >> inherited; >> end; > > Yes. But that works only for 'simple' types and still requires you to > write code. > > For the more general case where you can have arrays, classes, > interfaces and whatnot as fields of your object, there is simply no > correct way. Can't the clipboard help in cloning? Similar to the IDE's copy and paste.

Even then you'd need to convert the fields to the format the clipboard expects. And if you managed to correctly "stream" all properties than you don't need the clipboard anyway...

Regards,
Sven

Reply by email

Michael Van Canneyt
to Frederic Da Vitoria: 2013/9/23 Michael Van Canneyt Isn't there? Maybe it wouldn't be easy, but IIRC Delphi…
Frederic Da Vitoria wrote 8 lines
Yes. But that works only for 'simple' types and still requires you to write code. For the more general case where you can have arrays, classes, interfaces and whatnot as fields of your object, there is simply no correct way. Isn't there? Maybe it wouldn't be easy, but IIRC Delphi offers something related to this with TComponents and Streams. This Delphi mechanism I knew worked only for properties, but I found references that there existed a wider mechanism in D2010.

There is no GENERAL way.

The RTTI based mechanisms make special assumptions about how to reference components, tpersistents and whatnot.

If I have a field that is a stream. How are you going to correctly copy this ? Just the stream instance pointer ? The stream+Contents ?

The answer depends on how the classes are supposed to behave. The language itself cannot give you this mechanism.

Michael.

Reply by email

Zaher Dirkey
to Graeme Geldenhuys: My I also suggest Zaher takes a look at tiOPF's code to see how they implement class…

On Mon, Sep 23, 2013 at 12:58 PM, Graeme Geldenhuys <

Show quoted text 8 lines
wrote: Also be careful about the pitfalls of cloning a class. eg: How you want to handle more complex cases. eg: Cloning a class with a reference to another class, or cloning a class with an embedded class (field), etc... tiOPF's implementation allows the developer to decided how such cases should be handled.

​The idea is simply than needs to use a stream or RTTI, it is like a record, if looked at an object like as a record but it is inheritable. ​Compile can now every object body, and do the assigning as record assign. ​
That's the feature I like to have. ​

Best Regards
Zaher Dirkey

Reply by email

Zaher Dirkey
to Zaher Dirkey
Zaher Dirkey wrote 7 lines
​The idea is simply than needs to use a stream or RTTI, it is like a record, if looked at an object like as a record but it is inheritable. ​Compile can now every object body, and do the assigning as record assign. ​ That's the feature I like to have. ​ Best Regards

​This is a funny example about what in my mind about what I talk (I know it is stupid and unsafe)
If you look it is only for the class that not yet inherited yet, and work only for the top class, but the compiler can guss all inheited/derived classes and make the same assign.
If some understand it, maybe give more explain.
​----------------------------------------------------- type
TmyRec = record

    s: string;
    i: integer;
  end;
  PmyRec=^TmyRec;
  TmyObj = class(TObject)
    s: string;
    i: integer;
  end;
procedure TForm1.Button1Click(Sender: TObject);
var
  o1, o2: TmyObj;
begin
  o1 := TmyObj.Create;
  o2 := TmyObj.Create;
  o1.s := 'test1';
  o1.i := 122;
  PmyRec(@o2)^ := PmyRec(@o1)^;
  WriteLn(o2.s);
  WriteLn(o2.i);
end;
​-----------------------------------------------------​

Best Regards
Zaher Dirkey

Reply by email