Generics typecast for parameterized type descendant lazarus / 64-FPC development ---------------------------------------------------------------------- daniel_sap · 2024-02-19 11:06 Hi, Recently I use more actively free pascal and I noticed that I have to do typecasting very often when using parameterized type descendants in generics Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  TMyClass.startTheList();var  myList: TList;begin  myList := TList.Create();  doSomething(myList);  // the compiler forces me to do the typecast doSomething(TList(myList));end; TMyClass.doSomething(myVar: TList);begin  // some code using the listend;   For me it looks good to pass TList when TList is needed cause TMyClass is descendant of TObject and can act as TObject. Even more passing TMyList when TList should be fine, without typecasting. It looks a little bit to me like compiler forces me to typecast when I assign descendant class variable to parent class variable Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  TMyClass.do();var  obj: TObject;  myObj: TMyClass;begin  obj := TObject(myObj); end;   Do you know if such feature is planned for implementation. Does it look good for you to be implemented. Do you see some issues in implementing this (in C# I think you cannot even do the typecast. In Java allows me to do it fine) ---------------------------------------------------------------------- cdbc · 2024-02-19 11:38 Hi Have you tried {$mode delphi} ?!? The 2 modes 'objpas' & 'delphi' behave differently wrt. generics. Regards Benny ---------------------------------------------------------------------- Thaddy · 2024-02-19 12:07 Like so?Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode delphi}uses generics.collections;typeTMyclass = class;//forwardTMyList = TList; //declare a type does the trick TmyClass = classprivate  MyList:TMyList;public  procedure doSomething;  constructor Create;  destructor Destroy;override;end; constructor TMyClass.Create;begin  inherited create;  myList := TMyList.Create;end; destructor TMyClass.Destroy;begin  myList.Free;  inherited;end;  procedure TMyClass.doSomething;begin  // some code using the listend; var  myClass:TMyClass;begin  myClass:=TMyClass.Create;  myClass.DoSomething;  myClass.Free;end. Specializing a type prevents the need for typecasting a variable all the time. This example has no typecasts at all.. And requires no further specializations. In mode objfpc you have to add one (1) word:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type.....  TMyList = specialize TList; Rest is the same. Although there are maybe use cases for specializing on a var I have not seen a convincing example. Specialize on a type. ---------------------------------------------------------------------- cdbc · 2024-02-19 14:47 Hi @Thaddy: Thanks mate, something like that was on my mind, but I couldn't explain it as well, as you just did  Regards Benny ---------------------------------------------------------------------- Thaddy · 2024-02-19 15:43 It is not only "planned" as OP asked, it is an essential part of the generic syntax modes. Well written code can use both modes, I might add. For mode objectfpc, just the keyword specialize is added once, and the rest of the code is the exact same. No typecasts... The only "difficult" part is  the forward declaration of TmyClass to solve the interdependency that exists between TmyClass and TmyList. But that is specific to this example and can usually be avoided. To be more specific, since TmyList is specialized from TList, and the implementation of TMyClass is later, this specific example needed a forward declaration of TMyClass, since TMyClass uses TmyList. But that is for the rest not of interest in the case of handling generics. ---------------------------------------------------------------------- daniel_sap · 2024-02-20 01:04 Thank you all for the information, thoughts, guidelines I didn't mention that i use delphi mode everywhere (except in cases where I do static library linking) May be also the code example I gave is not explaining very good what happens on my side. So, I did a search of TList( in one of my projects and am attaching the screenshot here. It returns 150 typecasts and this is only for the TList cases, there are other also - TDoubleLinkedList, TSingleLinkedList, TDoubleEndedQueue<> and more. So, if I sum the typecasts in all the projects they are thousands. Of course I can do the typecasts (even if I don't like it, how it looks) but wanted to ask, and rise it here, and learn something more ---------------------------------------------------------------------- Thaddy · 2024-02-20 05:45 Quote from: daniel_sap on February 20, 2024, 02:04:21 am It returns 150 typecasts and this is only for the TList cases, there are other also - TDoubleLinkedList, TSingleLinkedList, TDoubleEndedQueue<> and more.There should not be any typecasts in your code if you followed my advise.Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode delphi}type  TmyList = Tlist;  TmyDoubleLinkedlist =TDoubleLinkedList; // from which generics  TmySingleLinkedList = TSingleLinkedList;  // framework did you  TmyDoubleEndedQueue  = TDoubleEndedQueue; // get these.If you work along those lines there are NO typecasts. I like to see a bit more code, especially where you type cast and on what. All I can see beforehand is that you did not follow my advise and still specialize on variables instead of declaring a type. This is no different from well written C# or C++. And I need to know which generics framework you use: FreePascal has several, all good but all slightly different to work with. Note that if you write such a framework yourself, you might encounter some, but not many, typecasts in the framework internals if the generics you write are pointer based, things like hardcasting to T, which would be bad design. Plz give us some more code, I am a certified Borland/Inprise/Embarcadero CppBuilder and Delphi trainer (certified upto XE2). My example looks deceptively simple, but it is core to how to use generics, so look again: types, not vars, please and your need for typecasts will automagically disappear in most cases. Again, within the internals of a generics framework typecasts can happen, but usually not in the code that uses that framework. Here's another small example from scratch:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program scratchlist;{$mode delphi}{ Both examples compile but  the good example avoids repeated specialization}{$define good}type    TList = class    private      FItems: array of T;    public      procedure Add(Value: T);    end;{$ifdef good}    // Good example use    TIntList = TList;    TStrList = TList;{$endif} procedure TList.Add(Value: T);begin  SetLength(FItems, Length(FItems) + 1);  FItems[High(FItems)] := Value;end; // Bad Example use:var{$ifndef good}  intList: TList;  strList: TList;{$else good}  intlist: TIntList;  strList: TstrList;{$endif}begin{$ifndef good}  intList := Tlist.create;  strList := Tlist.Create;{$else good}  intList := TIntList.Create;  strList := TStrList.Create;{$endif}  intList.Add(100);  strList.Add('Hello, World!');  strList.Free;  intList.Free;end.In mode objfpc the code for the bad example looks even more horrible. Note the extra specialization you need when specializing var instead of type . The code is just for demo purposes. ---------------------------------------------------------------------- TRon · 2024-02-20 06:11 I'm with Thaddy on this one. I never quite understood why almost all the Delphi examples I came across do not make use of type specialization but instead use the tedious long winded repetitive notation in their code. Is there perhaps a downside that I am not aware of ? ---------------------------------------------------------------------- 440bx · 2024-02-20 06:16 Quote from: TRon on February 20, 2024, 07:11:41 amIs there perhaps a downside that I am not aware of ? The following is a guess (easy to verify but I haven't).  When using type specialization wouldn't a breakpoint placed anywhere in the generic's code break for all specializations instead of a particular one ? (it might be desirable for the breakpoint to be "active" for only one type not all of them.) ---------------------------------------------------------------------- Thaddy · 2024-02-20 06:58 Quote from: 440bx on February 20, 2024, 07:16:29 amit might be desirable for the breakpoint to be "active" for only one type not all of them. I see the point, but setting a breakpoint inside the generics code itself seems to make no difference between var or type specialization. (using my second example that I added). ---------------------------------------------------------------------- daniel_sap · 2024-02-20 08:40 As all my life I specialized on variables, arguments ... it is a little bit difficult to switch to specialize type. I'm thinking and reading stuff on the topic, so may be I could switch my mind to type specialization. Another thing is if I have to create classes for every generic and specialization class combination. For example Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   TMyClass = class   end;   TMyClassForList = TList;  TMyClassForDoubleLinkedList = TList;  TMyClassForSingle.... I want to introduce one new class in the project and actually I have to introduce 5, 10 depending on what I would like to use it for. Despite the extra classes introduced, I really don't like to specify what the class will be used for where it is declared. (I declare TMyClass but now this unit will use also the units where it will be used) So, to avoid this I have to move this type specs in separate unit. Another thing is that, for me it looks like the parameterized type is more like configuring the TList class. TList class is doing the job, everything is implemented, but I want to give some more information how I want it to work. No new methods or fields I want to introduce, no method to override also, so why I need a descendant class. Also if I have hierarchy, I have to repeat the procedure Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   TMyClass1 = class  end;  TMyClass1ForList = TList;  TMyClass1ForDoubleLinkedList = TList;  TMyClass1ForSingle....   TMyClass2 = class(TMyClass1)  end;  TMyClass2ForList = TList;  TMyClass2ForDoubleLinkedList = TList;  TMyClass2ForSingle....  Also I tried to specialize the type and it still not working (for variable assigning and argument passing) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode delphi} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,  Generics.Collections; type   TListWithObject = TList;   TMyClass = class  public    someText: String;  end;   TListWithMyClass = TList;   { TForm1 }   TForm1 = class(TForm)    Label1: TLabel;    procedure FormCreate(Sender: TObject);  private   public     procedure doit();  procedure doListWithObject(list: TListWithObject);   end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject);begin end; procedure TForm1.doit();var  myObjList: TListWithMyClass;  var2: TListWithObject;begin  myObjList := TListWithMyClass.Create();  var2 := myObjList;  doListWithObject(myObjList);end; procedure TForm1.doListWithObject(list: TListWithObject);begin  Label1.Caption := IntToStr(list.Count);end; end.  Have compilation error Code: [Select]unit1.pas(56,11) Error: Incompatible types: got "TList" expected "TList" QuotePlz give us some more code, I am a certified Borland/Inprise/Embarcadero CppBuilder and Delphi trainer (certified upto XE2). I can prepare some real code example, but the code above illustrates the issue. QuoteAnd I need to know which generics framework you use: FreePascal has several, all good but all slightly different to work with. Note that if you write such a framework yourself, you might encounter some, but not many, typecasts in the framework internals if the generics you write are pointer based, things like hardcasting to T, which would be bad design. I'm using Generics.Collections. But I thought that the typecast is not related to specific usage of generics. (may be I'm missing something) ---------------------------------------------------------------------- VisualLab · 2024-02-20 18:29 Quote from: daniel_sap on February 20, 2024, 09:40:53 amAs all my life I specialized on variables, arguments ... it is a little bit difficult to switch to specialize type. I'm thinking and reading stuff on the topic, so may be I could switch my mind to type specialization. Another thing is if I have to create classes for every generic and specialization class combination. For example Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   TMyClass = class   end;   TMyClassForList = TList;  TMyClassForDoubleLinkedList = TList;  TMyClassForSingle....  If there are no differences in implementation between the TMyClassForList and TMyClassForDoubleLinkedList classes, this is an "artificial" creation of redundant classes and is completely unnecessary. There is no point in creating new types (classes) due to the role they are assigned in the program. Variables are enough for this. Descendants are created when there is a difference in their implementation, i.e. they have additional fields, methods, properties, events that differ among the descendants. Quote from: daniel_sap on February 20, 2024, 09:40:53 amAlso I tried to specialize the type and it still not working (for variable assigning and argument passing) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode delphi} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,  Generics.Collections; type   TListWithObject = TList;   TMyClass = class  public    someText: String;  end;   TListWithMyClass = TList;   { TForm1 }   TForm1 = class(TForm)    Label1: TLabel;    procedure FormCreate(Sender: TObject);  private   public     procedure doit();  procedure doListWithObject(list: TListWithObject);   end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject);begin end; procedure TForm1.doit();var  myObjList: TListWithMyClass; // <- this list stores objects of type TMyClass  var2: TListWithObject; // <- while this list stores objects of type TObjectbegin  myObjList := TListWithMyClass.Create(); // <- this list stores objects of type TMyClass  var2 := myObjList; // <- while this list stores objects of type TObject  doListWithObject(myObjList);end; procedure TForm1.doListWithObject(list: TListWithObject);begin  Label1.Caption := IntToStr(list.Count);end; end.  Have compilation error Code: [Select]unit1.pas(56,11) Error: Incompatible types: got "TList" expected "TList" You assign a variable of type TListWithMyClass to a variable of type TListWithObject. These are two different types. ---------------------------------------------------------------------- PascalDragon · 2024-02-20 20:15 Quote from: daniel_sap on February 19, 2024, 12:06:54 pmDo you know if such feature is planned for implementation. Does it look good for you to be implemented. Do you see some issues in implementing this No, something like this isn't planned, because it's hard to determine whether TList and TList can be considered compatible, because unlike e.g. Java FPC does its generic handling more like C++ and thus you can have something like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc} uses  Generics.Collections; type  TSomeType = class    { no virtual here by choice! }    procedure Test;  end;   TSomeOtherType = class(TSomeType)    procedure Test;  end; procedure TSomeType.Test;begin  Writeln('TSomeType');end; procedure TSomeOtherType.Test;begin  Writeln('TSomeOtherType');end; procedure Test(aList: specialize TList);var  t: TSomeType;begin  for t in aList do    t.Test;end; procedure Test(aList: specialize TList);var  t: TSomeOtherType;begin  for t in aList do    t.Test;end; var  l1: specialize TList;  l2: specialize TList;begin  l1 := specialize TList.Create;  l1.Add(TSomeType.Create);   l2 := specialize TList.Create;  l2.Add(TSomeOtherType.Create);   // will call TSomeType.Test  Test(l1);   // will call TSomeOtherType.Test  Test(l2);   // will call TSomeType.Test which could be totally wrong  Test(specialize TList(l2));end.