Sign in Join

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

Mailing lists / fpc-pascal / 2013-09

class inheritance and type incompatibility

21 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

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

Xiangrong Fang

Hi All,

I have the following program:

  1 program test;
  2 {$mode objfpc}{$H+}
  3 uses tree;
  4 type
  5   TIntTree = class(specialize TTree<Integer>)
  6   public
  7     function Clone: TIntTree;
  8   end;
  9 function TIntTree.Clone: TIntTree;
 10 begin
 11   Result := TIntTree(inherited Clone);
 12 end;
 13 var
 14   it1, it2 : TIntTree;
 15 begin
 16   it1 := TIntTree.Create(1, nil);
 17   it2 := it1.Clone;
 18   WriteLn(it1.ClassName);
 19   WriteLn(it2.ClassName);
 20 end.

Which output:

TIntTree
TIntTree.TTree$LongInt

The source code of TTree is here:
https://github.com/xrfang/fpcollection/blob/master/src/units/tree.pas

Why the typecast on line 11 does not work? How to modify the code so that the cloned object has same type as the original one?

Regards,
Xiangrong

Xiangrong Fang
to Xiangrong Fang

2013/9/26 Xiangrong Fang

Show quoted text 7 lines
Which output: TIntTree TIntTree.TTree$LongInt

Forgot to ask, I understand that TTree$LongInt is the name the compiler gave to the generic class when it is specialized, but what is TIntTree.TTree$LongInt? What is the meaning of "Scoped" type (or type with "namespace"?)?

Sven Barth
to Xiangrong Fang: I have the following program: 1 program test; 2 {$mode objfpc}{$H+} 3 uses tree; 4 type 5…
Xiangrong Fang wrote 35 lines
Hi All, I have the following program: 1 program test; 2 {$mode objfpc}{$H+} 3 uses tree; 4 type 5 TIntTree = class(specialize TTree<Integer>) 6 public 7 function Clone: TIntTree; 8 end; 9 function TIntTree.Clone: TIntTree; 10 begin 11 Result := TIntTree(inherited Clone); 12 end; 13 var 14 it1, it2 : TIntTree; 15 begin 16 it1 := TIntTree.Create(1, nil); 17 it2 := it1.Clone; 18 WriteLn(it1.ClassName); 19 WriteLn(it2.ClassName); 20 end. Which output: TIntTree TIntTree.TTree$LongInt The source code of TTree is here: https://github.com/xrfang/fpcollection/blob/master/src/units/tree.pas Why the typecast on line 11 does not work? How to modify the code so that the cloned object has same type as the original one?

Please always think through this without generics. Your code looks roughly like this:

=== code begin ===

type
   TTreeInteger = class
     function Clone: TTreeInteger;
   end;
   TTreeInt = class(TTreeInteger)
     function Clone: TTreeInt;
   end;
function TTreeInteger.Clone: TTreeInteger;
begin
   Result := TTreeInteger.Create;
end;
function TTreeInt.Clone: TTreeInt;
begin
   Result := TTreeInt(inherited Clone);
end;

=== code end ===

As you can see you are constructing a TTreeInteger class inside TTreeInteger.Clone and nothing in the world afterwards can turn it into a TTreeInt. If you'd use the "as" operator in TTreeInt.Clone then you'd even get an exception, because you can not assign a TTreeInteger instance(!) to a TTreeInt.

What should work is the following (now with generics again):

=== code begin ===

type
   TTree<T> = class
   private type
     TSelfClass = class of TTree;
   public
     function Clone: TTree;
   end;
function TTree.Clone: TTree;
begin
   Result := TSelfClass(Self.ClassType).Create(Data, FParent);
   (* ... *)
end;

=== code end ===

Note: The cast to TSelfClass is necessary to have access to the overloaded constructor you added for TTree.

Regards,
Sven

Sven Barth
to Xiangrong Fang: 2013/9/26 Xiangrong Fang Forgot to ask, I understand that TTree$LongInt is the name the…
Xiangrong Fang wrote 13 lines
2013/9/26 Xiangrong Fang < > Which output: TIntTree TIntTree.TTree$LongInt Forgot to ask, I understand that TTree$LongInt is the name the compiler gave to the generic class when it is specialized, but what is TIntTree.TTree$LongInt? What is the meaning of "Scoped" type (or type with "namespace"?)?

Your TIntTree is declared like this:

=== code begin ===

type
   TIntTree = class(specialize TTree<Integer>)
   end;

=== code end ===

What the compiler now does is when it encounters the specialization it generates a new type definition of that type (globally in the current module, so that it can be reused by e.g. other classes) and adds a local symbol reference to that type. So for code it appears that the parent type was declared as a nested type inside the child type.

Regards,
Sven

Xiangrong Fang
to Sven Barth: Please always think through this without generics. Your code looks roughly like this: ===…
Show quoted text 20 lines
What should work is the following (now with generics again): === code begin === type TTree<T> = class private type TSelfClass = class of TTree; public function Clone: TTree; end; function TTree.Clone: TTree; begin Result := TSelfClass(Self.ClassType).**Create(Data, FParent); (* ... *) end; ​After change to this code in Clone(), I got this error:

​

​tree.pas(66,60) Error: Incompatible type for arg no. 2: Got "TIntTree.TTree$LongInt", expected "TTree"
​

Sven Barth
to Xiangrong Fang
Xiangrong Fang wrote 23 lines
What should work is the following (now with generics again): === code begin === type TTree<T> = class private type TSelfClass = class of TTree; public function Clone: TTree; end; function TTree.Clone: TTree; begin Result := TSelfClass(Self.ClassType).__Create(Data, FParent); (* ... *) end; ​After change to this code in Clone(), I got this error: ​ ​tree.pas(66,60) Error: Incompatible type for arg no. 2: Got "TIntTree.TTree$LongInt", expected "TTree"

Seems like a bug in 2.6.x. It's compiling in 2.7.1.

Regards,
Sven

Xiangrong Fang
to Sven Barth

2013/9/26 Sven Barth

Show quoted text 10 lines
​After change to this code in Clone(), I got this error: > > ​ > ​tree.pas(66,60) Error: Incompatible type for arg no. 2: Got > "TIntTree.TTree$LongInt", expected "TTree" > Seems like a bug in 2.6.x. It's compiling in 2.7.1.

​I altered the code so that it works in 2.6.2:

=== test program ===
program test;
{$mode objfpc}{$H+}
uses tree;
type
  TIntTree = class(specialize TTree<Integer>)
  public
    function Clone: TIntTree;
  end;
function TIntTree.Clone: TIntTree;
begin
  Result := TIntTree.Create(Data, FParent);
  DoClone(Result);
end;
var
  it1, it2 : TIntTree;
begin
  it1 := TIntTree.Create(1, nil);
  it2 := it1.Clone;
  WriteLn(it1.ClassName);
  WriteLn(it2.ClassName);
end.​

​=== tree clone code ===​
​type
generic TTree<T> = class
... ...

  protected
    procedure DoClone(Target: TTree);
  public
    ... ...
    function Clone: TTree;
    ... ...
  end;
implementation
procedure TTree.DoClone(Target: TTree);
var
  node: TTree;
begin
  node := FirstChild;
  while node  nil do begin
    node.Clone.Remove(Target);
    node := node.NextSibling;
  end;
end;
function TTree.Clone: TTree;
begin
  Result := TTree.Create(Data, FParent);
  DoClone(Result);
end;

=======​

​Is there any problem with this solution?

Regards,
Xiangrong​

Xiangrong Fang
to Sven Barth: Please always think through this without generics. Your code looks roughly like this: ===…

Hi Sven,

2013/9/26 Sven Barth

Show quoted text 5 lines
As you can see you are constructing a TTreeInteger class inside TTreeInteger.Clone and nothing in the world afterwards can turn it into a TTreeInt.

​Do you mean that casting TTreeInteger into TTreeInt is not possible because this is to cast a parent class into a child class? If so, how this is possible (mentioned in my earlier question about avoid typecasting):

type
  TIntTree = class(specialize TTree<Integer>)
  end;
var
  it1, it2: TIntTree;
begin
  ... ...
  it2 := TIntTree(it1.FirstChild);
end.

This is also casting TTree$Integer (parent class) to TIntTree (child class)?

Regards,
Xiangrong

Sven Barth
to Sven Barth: No, at least not on first sight :) Sven
Sven Barth wrote 5 lines
Am 27.09.2013 03:52 schrieb "Xiangrong Fang" < >: > Is there any problem with this solution? No, at least not on first sight :)

On second sight your solution is not correct, because you are using Clone inside your parent class which would not use the (non-virtual) Clone you created.

I've now played around with 2.6.2 myself and this should solve this problem:

=== code begin ===

type
   generic TTree<T> = class
   private type
     TSelfType = TTree;
     TSelfClass = class of TSelfType; // earlier I suggested TTree which
2.6.2 does not support
   public
     function Clone: TTree;
   end;
function TTree.Clone: TTree;
begin
   Result := TSelfClass(Self.ClassType).Create(Data, FParent);
   (...)
end;

=== code end ===

You can of course also add functions like "Clone: TIntTree" to your TIntTree which then only call "Result := TIntTree(inherited Clone)", but otherwise it should be ok now.

Regards,
Sven

Sven Barth
to Xiangrong Fang: 2013/9/26 Sven Barth ​Do you mean that casting TTreeInteger into TTreeInt is not…
Xiangrong Fang wrote 27 lines
Hi Sven, 2013/9/26 Sven Barth < > As you can see you are constructing a TTreeInteger class inside TTreeInteger.Clone and nothing in the world afterwards can turn it into a TTreeInt. ​Do you mean that casting TTreeInteger into TTreeInt is not possible because this is to cast a parent class into a child class? If so, how this is possible (mentioned in my earlier question about avoid typecasting): type TIntTree = class(specialize TTree<Integer>) end; var it1, it2: TIntTree; begin ... ... it2 := TIntTree(it1.FirstChild); end. This is also casting TTree$Integer (parent class) to TIntTree (child class)?

Depends on how you add the nodes. If you use TIntTree.Create(..., SomeOtherIntTree) then the class instance which is added to the FItems of the parent is a TIntTree. If you use TTree$Integer.Create then itwill be a TTree$Integer. Also see my other mail I just sent regarding your Clone call.

I'd suggest you to compile your code (for testing) with -CR which will turn all object type casts to "as" typecasts thus they will raise exceptions if the typecast is not possible (e.g. if you are casting a TTree$Integer instance to a TIntTree). This way you can find out where you still have problems.

Regards,
Sven

Xiangrong Fang
to Sven Barth: On second sight your solution is not correct, because you are using Clone inside your…

2013/9/28 Sven Barth

Show quoted text 5 lines
On second sight your solution is not correct, because you are using Clone inside your parent class which would not use the (non-virtual) Clone you created.

​What If I make Clone virtual? If by adding "virtual" to Clone, the problem is solved, then what's the difference between virtual Clone and the method you provided?
​

Show quoted text 8 lines
=== code begin === type generic TTree<T> = class private type TSelfType = TTree; TSelfClass = class of TSelfType; // earlier I suggested TTree which 2.6.2 does not support

​This compiles, however, if I remove my version of TIntTree (i.e. implement a Clone in it and call parent's DoClone), then the main program does not compile, it says:

demo2.lpr(20,13) Error: Incompatible types: got "TIntTree.TTree$LongInt"
expected "TIntTree"

I then can either:

1) add a Clone method to TIntTree and do a typecast inside it, as you said.
2) do a typecast in the main program, i.e. it2 := it1.Clone as TIntTree.

So, I guess the problem is solved? I have two further questions:

1) Why class of TTree does not work in 2.6.2 but class of TSelfType worked? Is it a problem of generics in 2.6.2, or it is *not* related to generics? I feel that TSelfType is same as TTree?

2) What is the difference between the following:

- it2 := TIntTree(it1.Clone);
- it2 := it1.Clone as TIntTree;

Where in the official document can I find the behavior/definition of "as"?

Thanks a lot.

Regards,
Xiangrong

Sven Barth
to Xiangrong Fang
Xiangrong Fang wrote 12 lines
2013/9/28 Sven Barth < > On second sight your solution is not correct, because you are using Clone inside your parent class which would not use the (non-virtual) Clone you created. ​What If I make Clone virtual? If by adding "virtual" to Clone, the problem is solved, then what's the difference between virtual Clone and the method you provided?

If you want to override the virtual Clone method of TTree<LongInt> in TIntTree you need to use the same result type + override or otherwise you gain nothing, because the compiler will not consider the method as overloaded and use the Clone method of TTree<LongInt> inside TTree<LongInt>'s methods instead.

Show quoted text 23 lines
​ === code begin === type generic TTree<T> = class private type TSelfType = TTree; TSelfClass = class of TSelfType; // earlier I suggested TTree which 2.6.2 does not support ​This compiles, however, if I remove my version of TIntTree (i.e. implement a Clone in it and call parent's DoClone), then the main program does not compile, it says: demo2.lpr(20,13) Error: Incompatible types: got "TIntTree.TTree$LongInt" expected "TIntTree" I then can either: 1) add a Clone method to TIntTree and do a typecast inside it, as you said. 2) do a typecast in the main program, i.e. it2 := it1.Clone as TIntTree.

Would you please show how exactly you changed the code? With all those methods calling each other it's a bit hard to imagine in my head what you changed. ;)

Show quoted text 6 lines
So, I guess the problem is solved? I have two further questions: 1) Why class of TTree does not work in 2.6.2 but class of TSelfType worked? Is it a problem of generics in 2.6.2, or it is *not* related to generics? I feel that TSelfType is same as TTree?

It's a problem of generics in 2.6.2. Principially TSelfType is the same as TTree, but the 2.6.2 compiler treats them a little bit differently.

Show quoted text 5 lines
2) What is the difference between the following: - it2 := TIntTree(it1.Clone); - it2 := it1.Clone as TIntTree;

The latter does a conversion at runtime, so if "it1.Clone" returns an instance that is not compatible with TIntTree (for example a TObject or a TTree<LongInt>) then an exception will be raised. The former does not raise such an exception (but will show you a warning if you try to e.g. convert a TTree<LongInt> to a TTree<String>, but AFAIK the "as" will show that warning as well) and does not require additional runtime code (thus if you know that a e.g. TTree<LongInt> variable really contains a TIntTree instance you can use the hard typecast). If you compile code with -CR or {$OBJECTCHECKS ON} then the hard typecast will be the same as "as".

Where in the official document can I find the behavior/definition of "as"?

Here: http://freepascal.org/docs-html/ref/refsu45.html#x135-14500012.8.7 (Chapter Expressions => Class operators)

Regards,
Sven

Xiangrong Fang
to Sven Barth

2013/9/29 Sven Barth

Show quoted text 6 lines
If you want to override the virtual Clone method of TTree<LongInt> in TIntTree you need to use the same result type + override or otherwise you gain nothing, because the compiler will not consider the method as overloaded and use the Clone method of TTree<LongInt> inside TTree<LongInt>'s methods instead.

​Yes, you are right, for virtual method, you need have exactly same type... But this leads me to think how the generics are implemented? For example:

=== snippet 1 ===
type
  TIntTree = class(specialize TTree<Integer>)
  public
    function Clone: TTree; override;
  end;
=== end of snippet 1 ===

This will leads to a compile error: Generics without specialization cannot be used as a type for a variable.

If so, why in the code TTree can be used everywhere, for example:

=== snippet 2 ===
function TTree.Level: Cardinal;
var
  n: TTree;  <-- here TTree is not specialized.
begin
  ... ...
end;
=== end of snippet 2 ===

In snippet 1, why the compiler don't treat the TTree same as its own type i.e. TIntTree? I imagine that TTree in snippet 1 is logically similar to TObject. I mean, a class's method is of course possible to return a value of its parent's type or any other class's instance?

Would you please show how exactly you changed the code? With all those

methods calling each other it's a bit hard to imagine in my head what you changed. ;)

​With your TSelfClass solution in TTree, I mean either:

=== solution 1 ===
type
  TIntTree = class(specialize TTree<Integer>
  public
    function Clone: TIntTree;
  end;
  function TIntTree.Clone: TIntTree;
  begin
    Result := TIntTree(inherited Clone); //typecast in TIntTree
  end;
​=== end of solution 1===
or:
=== solution 2 ===
type
  TIntTree = class(specialize TTree<Integer>)
  end;
begin //main
  it2 := TIntTree(it1.Clone);  //typecast in main program
end.
=== end of solution 2 ===​
​
Are both solutions correct?

Regards,
Xiangrong​
​

Xiangrong Fang
to Sven Barth: On second sight your solution is not correct, because you are using Clone inside your…

2013/9/28 Sven Barth

Show quoted text 17 lines
On second sight your solution is not correct, because you are using Clone inside your parent class which would not use the (non-virtual) Clone you created. I've now played around with 2.6.2 myself and this should solve this problem: === code begin === type generic TTree<T> = class private type TSelfType = TTree; TSelfClass = class of TSelfType; // earlier I suggested TTree which 2.6.2 does not support=== code end ===

​I think this solution only fixed the problem with 2.6.2 that you have to use TSelfType, but the logic is still not correct.

Using your modified version, I try to achieve some complex Clone​

​strategy, for example, either one of the following:

1) the descendant has added a field other than Data that needs to be cloned;
2) the clone is "non-verbatim", it will multiple Data by 2 when cloning.

Let's use the 2nd one as an example, the following does not work:

type
  TIntTree = class(specialize TTree<Integer>)
  public
    function Clone: TIntTree;
  end;
function TIntTree.Clone: TIntTree;
begin
  Result := TIntTree(inherited Clone);
  Result.Data *= 2;
end;

It only multiple the ROOT node of the cloned tree, not every node.

​
Regards,
Xiangrong

Sven Barth
to Xiangrong Fang: 2013/9/29 Sven Barth ​Yes, you are right, for virtual method, you need have exactly…
Xiangrong Fang wrote 13 lines
2013/9/29 Sven Barth < > If you want to override the virtual Clone method of TTree<LongInt> in TIntTree you need to use the same result type + override or otherwise you gain nothing, because the compiler will not consider the method as overloaded and use the Clone method of TTree<LongInt> inside TTree<LongInt>'s methods instead. ​Yes, you are right, for virtual method, you need have exactly same type... But this leads me to think how the generics are implemented?

The important things about generics:
- The generic type itself (in this case TTree) does *not* exist inside the code (bugs not withstanding...), only the textual representation of the generic exists as metadata inside the PPU (which you can not access from a program)
- When specializing a generic the compiler loads this metadata and reparses the textual representation whereby the generic type parameters are replaced by the types you specified, so in code a "specialize TTree<Integer>" looks like a "TTree<T>" which you manually have made non-generic and replaced each occurence of "T" with "Integer".

Show quoted text 13 lines
For example: === snippet 1 === type TIntTree = class(specialize TTree<Integer>) public function Clone: TTree; override; end; === end of snippet 1 === This will leads to a compile error: Generics without specialization cannot be used as a type for a variable.

"TTree" is not valid anywhere except inside the declaration of the generic (and only in non-Delphi modes).

Show quoted text 16 lines
If so, why in the code TTree can be used everywhere, for example: === snippet 2 === function TTree.Level: Cardinal; var n: TTree; <-- here TTree is not specialized. begin ... ... end; === end of snippet 2 === In snippet 1, why the compiler don't treat the TTree same as its own type i.e. TIntTree? I imagine that TTree in snippet 1 is logically similar to TObject. I mean, a class's method is of course possible to return a value of its parent's type or any other class's instance?

In case of generics in non-Delphi modes the own class name without generic parameters (here "TTree") is a place holder for the own type of the generic. The compiler replaces any reference to this type with references to the generic you are declaring. If you now specialize a generic (e.g. "specialize TTree<LongInt>") all those references are now handled as references to "TTree<LongInt>".

Show quoted text 30 lines
Would you please show how exactly you changed the code? With all those methods calling each other it's a bit hard to imagine in my head what you changed. ;) ​With your TSelfClass solution in TTree, I mean either: === solution 1 === type TIntTree = class(specialize TTree<Integer> public function Clone: TIntTree; end; function TIntTree.Clone: TIntTree; begin Result := TIntTree(inherited Clone); //typecast in TIntTree end; ​=== end of solution 1=== or: === solution 2 === type TIntTree = class(specialize TTree<Integer>) end; begin //main it2 := TIntTree(it1.Clone); //typecast in main program end. === end of solution 2 ===​ ​ Are both solutions correct?

Yes, correct are both.

Regards,
Sven

Sven Barth
to Xiangrong Fang: 2013/9/28 Sven Barth ​I think this solution only fixed the problem with 2.6.2 that you…
Xiangrong Fang wrote 44 lines
2013/9/28 Sven Barth < > On second sight your solution is not correct, because you are using Clone inside your parent class which would not use the (non-virtual) Clone you created. I've now played around with 2.6.2 myself and this should solve this problem: === code begin === type generic TTree<T> = class private type TSelfType = TTree; TSelfClass = class of TSelfType; // earlier I suggested TTree which 2.6.2 does not support=== code end === ​I think this solution only fixed the problem with 2.6.2 that you have to use TSelfType, but the logic is still not correct. Using your modified version, I try to achieve some complex Clone​ ​strategy, for example, either one of the following: 1) the descendant has added a field other than Data that needs to be cloned; 2) the clone is "non-verbatim", it will multiple Data by 2 when cloning. Let's use the 2nd one as an example, the following does not work: type TIntTree = class(specialize TTree<Integer>) public function Clone: TIntTree; end; function TIntTree.Clone: TIntTree; begin Result := TIntTree(inherited Clone); Result.Data *= 2; end; It only multiple the ROOT node of the cloned tree, not every node.

I would suggest you to add an additional protected virtual method to TTree which is called from TTree.Clone and you override that in your descendant classes.

Regards,
Sven

Xiangrong Fang
to Sven Barth

2013/9/29 Sven Barth

Show quoted text 5 lines
I would suggest you to add an additional protected virtual method to TTree which is called from TTree.Clone and you override that in your descendant classes.

Could
​ you please give an example how to do this? I tried the following:

=== code start ===
function TTree.DoClone: TTree; //protected, virtual
begin
  Result := TSelfClass(Self.ClassType).Create(Data, FParent);
end;
function TTree.Clone: TTree;
var
  node: TTree;
begin
  Result := DoClone;
  node := FirstChild;
  while node  nil do begin
    node.Clone.Remove(Result);
    node := node.NextSibling;
  end;
end;
=== code end ===

However, this does not work, because it seems that I cannot make any generic method virtual! In TIntTree, I have to write:

function TIntTree.DoClone: TIntTree;

As it is not possible to write TTree outside of the generic definition.

Regards,
Xiangrong

Sven Barth
to Xiangrong Fang
Xiangrong Fang wrote 37 lines
2013/9/29 Sven Barth < > I would suggest you to add an additional protected virtual method to TTree which is called from TTree.Clone and you override that in your descendant classes. Could ​ you please give an example how to do this? I tried the following: === code start === function TTree.DoClone: TTree; //protected, virtual begin Result := TSelfClass(Self.ClassType).Create(Data, FParent); end; function TTree.Clone: TTree; var node: TTree; begin Result := DoClone; node := FirstChild; while node nil do begin node.Clone.Remove(Result); node := node.NextSibling; end; end; === code end === However, this does not work, because it seems that I cannot make any generic method virtual! In TIntTree, I have to write: function TIntTree.DoClone: TIntTree; As it is not possible to write TTree outside of the generic definition.

This should do it:

=== code begin ===

type
   generic TTree<T> = class
   protected type
     TSelfType = TTree;
   protected
     procedure DoClone(aNode: TSelfType); virtual;
   public
     function Clone: TSelfType;
   end;
procedure TTree.DoClone(aNode: TSelfType);
begin
   (* empty *)
end;
function TTree.Clone: TSelfType;
var
   n: TTree;
begin
   Result := TSelfType(Self.ClassType).Create(Data, FParent);
   node := FirstChild;
   while node  nil do begin
     node.Clone.Remove(Result);
     node := node.NextSibling;
   end;
   DoClone(Result);
end;
type
   TIntTree = class(specialize TTree<Integer>)
   protected
     procedure DoClone(aNode: TSelfType; override;
   end;
procedure TIntTree.DoClone(aNode: TSelfType);
begin
   inherited;
   (* whatever *)
end;

=== code end ===

Regards,
Sven

Xiangrong Fang
to Sven Barth

2013/9/30 Sven Barth

Show quoted text 10 lines
However, this does not work, because it seems that I cannot make any > generic method virtual! In TIntTree, I have to write: > > function TIntTree.DoClone: TIntTree; > > As it is not possible to write TTree outside of the generic definition. > This should do it:

Right, this worked. My last question for this issue: is the TSelfType a 2.6.2 workaround, or it is required even in 2.7.1? I mean, how about this:

=== code ===
type
  generic TTree = class
  protected
    procedure DoClone(aNode: TTree); virtual;  //<<-- instead of TSelfType
  end;
  TIntTree = class(specialize TTree<Integer)
  protected
    procedure DoClone(aNode: TTree); override; //<<-- instead of TSelfType
  end;
=== end code ===

In another word, use "TTree" to represent any of its specialization and their descendants. Is it possible in 2.7.1 or later?

Thanks a lot.

Xiangrong

Sven Barth
to Xiangrong Fang
"Xiangrong Fang" wrote 16 lines
2013/9/30 Sven Barth > > >> However, this does not work, because it seems that I cannot make any >> generic method virtual! In TIntTree, I have to write: >> >> function TIntTree.DoClone: TIntTree; >> >> As it is not possible to write TTree outside of the generic definition. > > > This should do it: Right, this worked. My last question for this issue: is the TSelfType a

2.6.2 workaround, or it is required even in 2.7.1? I mean, how about this:

Show quoted text 14 lines
=== code === type generic TTree = class protected procedure DoClone(aNode: TTree); virtual; //<<-- instead of TSelfType end; TIntTree = class(specialize TTree<Integer) protected procedure DoClone(aNode: TTree); override; //<<-- instead of TSelfType end; === end code === In another word, use "TTree" to represent any of its specialization and

their descendants. Is it possible in 2.7.1 or later?

You don't require "TSelfType" in 2.7.1, but the correct one is not "TTree", but "specialize TTree<Integer>". If "TTree" should work then that's a bug...

Regards,
Sven