Recent

Author Topic: Inherit from TFPGObjectList?  (Read 684 times)

MaartenJB

  • Full Member
  • ***
  • Posts: 112
Inherit from TFPGObjectList?
« on: May 11, 2023, 10:42:39 am »
Hi,

Is there a way to inherit from my TFPGObjectList class, but with another item object type?

Code: Pascal  [Select][+][-]
  1. program Project1; {$mode objfpc}{$H+}
  2.  
  3. uses
  4.   Classes, fgl;
  5.  
  6. type
  7.   TMyObject = class (TObject)
  8.     Name : String;
  9.   end;
  10.  
  11.   TMyObject2 = class (TMyObject)
  12.     Name2 : String;
  13.   end;
  14.  
  15.   TMyList = class( specialize TFPGObjectList<TMyObject> );
  16.   TMyList2 = class(TMyList); // Is there a way to inherit from TMyList but with TMyObject2 as object type?
  17.  
  18. var
  19.   MyList2 : TMyList2;
  20. begin
  21.   MyList2[0].Name  := 'Test'; // works
  22.   MyList2[0].Name2 := 'Test'; // <=-- this obviously does not work atm
  23. end.

I've also tried this horror code, which doesn't work, the above has my preference if that could somehow work.

Code: Pascal  [Select][+][-]
  1. program Project1; {$mode objfpc}{$H+}
  2.  
  3. uses Classes, fgl;
  4.  
  5. type
  6.   TMyObject = class (TObject)
  7.     Name : String;
  8.   end;
  9.  
  10.   TMyList = class(TFPSList);
  11.   generic TMyObjectList<T: TObject> = class(TMyList);
  12.   TMyList2 = class( specialize TMyObjectList<TMyObject> );
  13.  
  14. var
  15.   MyList : TMyList2;
  16. begin
  17.   MyList[0].Name := 'Test'; // <=-- Error: Illegal qualifier
  18. end.

Best regards,

Maarten

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Inherit from TFPGObjectList?
« Reply #1 on: May 11, 2023, 11:33:49 am »
You can carry the generics into your base list and use a generic restriction to restrict generics to inherit from a specific type:
Code: Pascal  [Select][+][-]
  1. type
  2.   TTest = class
  3.   public A: Integer;
  4.   end;
  5.  
  6.   TTest2 = class(TTest)
  7.   public B: Integer;
  8.   end;
  9.  
  10.   { TTestInheritedList }
  11.  
  12.   generic TTestInheritedList<T: TTest> = class(specialize TFPGObjectList<T>)
  13.   public function AddAllA: Integer;
  14.   end;
  15.  
  16.   { TTest2List }
  17.  
  18.   TTest2List = class(specialize TTestInheritedList<TTest2>)
  19.   public function AddAllB: Integer;
  20.   end;
  21.  
  22. function TTestInheritedList.AddAllA: Integer;
  23. var
  24.   i: Integer;
  25. begin
  26.   Result := 0;
  27.   for i:=0 to Count-1 do
  28.     Result += Items[i].A;
  29. end;
  30.  
  31. function TTest2List.AddAllB: Integer;
  32. var
  33.   i: Integer;
  34. begin
  35.   Result := 0;
  36.   for i:=0 to Count-1 do
  37.     Result += Items[i].B;
  38. end;
  39.  
  40. var
  41.   myList: TTest2List;
  42. begin
  43.   myList := TTest2List.Create;
  44.   try
  45.     myList.Add(TTest2.Create);
  46.     myList.Add(TTest2.Create);
  47.     WriteLn(myList.AddAllA);
  48.     WriteLn(myList.AddAllB);
  49.   finally
  50.     myList.Free;
  51.   end;
  52. end.

This way any specialization of TTestInheritedList can only be specialized with a class that inherits from TTest

MaartenJB

  • Full Member
  • ***
  • Posts: 112
Re: Inherit from TFPGObjectList?
« Reply #2 on: May 11, 2023, 12:10:25 pm »
Thank you! this is what I was looking for!


MaartenJB

  • Full Member
  • ***
  • Posts: 112
Re: Inherit from TFPGObjectList?
« Reply #3 on: May 11, 2023, 02:30:33 pm »
I got something weird.

If I want to create an item from the function AddAllA it expects TTest2 instead of TTest.

Code: Pascal  [Select][+][-]
  1. function TTestInheritedList.AddAllA: Integer;
  2. var
  3.   i: Integer;
  4. begin
  5.   Add(TTest.Create); // <=-- Error: Incompatible type for arg no. 1: Got "TTest", expected "TTest2"
  6.  
  7.   Result := 0;
  8.   for i:=0 to Count-1 do
  9.     Result += Items[i].A;
  10. end;

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Inherit from TFPGObjectList?
« Reply #4 on: May 11, 2023, 03:34:25 pm »
Yes, because at the time of specialization the compiler knows that the instance you are using is using the inherited type TTest2, which is what is actually put in the ObjectList.

And this is also important, because when you create the TTest here, this does not have a B, so you would have your TTest2List which inherits from it, that assumes that all elements are TTest2, but one of them isn't. Consider you call AddAllA and afterwards call AddAllB. AddAllB requires that all elements are of type TTest2, because it requires the member "B" to be read from teh object, so the element added in AddAllA is of type TTest and has no member "B" and would therefore not not be valid in the same list that is used for AddAllB.

If you want to add an Object of which you know it is of the right type, but you only get it as the parent type, you can cast it:
Code: Pascal  [Select][+][-]
  1. function CreateSomeTTest: TTest;
  2. begin
  3.   Result := TTest2.Create; // Creates a TTest2 but returns it as parent type TTest
  4. end;
  5.  
  6.     function TTestInheritedList.AddAllA: Integer;
  7.     var
  8.       i: Integer;
  9.     begin
  10.       Add(CreateSomeTTest as T); // CreateSomeTTest returns a TTest, but if you are sure that it is of the correct type for that list you can cast it via "as"
  11.      
  12.       Result := 0;
  13.       for i:=0 to Count-1 do
  14.         Result += Items[i].A;
  15.     end;
« Last Edit: May 11, 2023, 03:37:52 pm by Warfley »

MaartenJB

  • Full Member
  • ***
  • Posts: 112
Re: Inherit from TFPGObjectList?
« Reply #5 on: May 11, 2023, 07:48:38 pm »
Thanks again, I think I got my code working now. I'm starting to get a grasp of the generic class use. I need to let the info sink in a bit and read it again tomorrow, to fully understand.

Thanks!

 

TinyPortal © 2005-2018