Recent

Author Topic: Is it possible to accept *any* generic as a parameter?  (Read 2173 times)

hayanninja

  • New Member
  • *
  • Posts: 45
Is it possible to accept *any* generic as a parameter?
« on: January 01, 2018, 02:39:58 am »
Let's say I have a generic descendant of TFPGObjectList<>:

Code: [Select]
TMoreSpecificObjectList<T: TMoreSpecificObject> = class(specialize TFPGObjectList<T>)
The intent being not to use this with TMoreSpecificObject itself of course, but with descendants of such.

Now, I have a procedure:

Code: [Select]
procedure TMoreSpecificObjectList.WriteNamesToStrings(aDst: TStrings);
var
  Item: TMoreSpecificObject;
begin
  aDst.Clear;
  for Item in Self do
    aDst.Add(Item.Name);
end;

(Of course, the "Name" property exists on TMoreSpecificObject, not only some / all of its descendants.)


Finally, I want to do this somewhere else:

Code: [Select]
procedure <whatever>.SetListBoxFromList(aSrc: TMoreSpecificObjectList; aDst: TListBox);
var
  OldIndex: Integer;
begin
  OldIndex := aDst.ItemIndex;
  aSrc.WriteNamesToStrings(aDst.Items);
  aDst.ItemIndex := OldIndex;
end;

(Perhaps because I want to be able to call "SetListBoxFromList" for several TMoreSpecificObjectList specializations, each of which specialize to a different TMoreSpecificObject descendant.)

In particular, note that I don't need any specific features of specific TMoreSpecificObject descendants; just the "Name" property that they all have (since it's in TMoreSpecificObject or an ancestor of such).

The above code does not work, saying that an unspecialized generic cannot be used as a variable type. Is there any way around this, short of creating a type that's specifically "TMoreSpecificObjectList = class(specialize TFPGObjectList<TMoreSpecificObject>)", implementing this functionality in that, and then creating another generic class that descends from that?

(Note: All code samples in this post were quickly mocked up to demonstrate what I'm trying to do. I'm well aware that the code snippets here are leaving out important checks and stuff.)

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Is it possible to accept *any* generic as a parameter?
« Reply #1 on: January 01, 2018, 01:00:53 pm »
Generics are used as a template for code generation (for lazy ones). No inheritance and all OOP features. Simple duplication of code. You can make a procedure template and the compiler generates as many procedures as there are types. But better (do not be lazy) refuse generics and use standard class with inheritance and type checking.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Is it possible to accept *any* generic as a parameter?
« Reply #2 on: January 02, 2018, 10:35:35 pm »
As long as your <whatever> is not a generic itself you can do this in trunk/3.1.1 (allowing generic declarations inside generics is a ToDo item):
Code: Pascal  [Select][+][-]
  1. type
  2.   generic TMoreSpecificObjectList<T{: TMoreSpecificObject}> = class(specialize TFPGObjectList<T>)
  3.   end;
  4.  
  5.   TWhatEver = class
  6.     generic procedure SetListBoxFromList<T: TMoreSpecificObject>(aSrc: specialize TMoreSpecificObjectList<T>; aDst: TListBox);
  7.   end;
  8.  
  9. generic procedure TWhatEver.SetListBoxFromList<T>(aSrc: specialize TMoreSpecificObjectList<T>; aDst: TListBox);
  10. var
  11.   OldIndex: Integer;
  12. begin
  13.   OldIndex := aDst.ItemIndex;
  14.   aSrc.WriteNamesToStrings(aDst.Items);
  15.   aDst.ItemIndex := OldIndex;
  16. end;
  17.  
  18. begin
  19.   whateverinstance.specialize SetListBoxFromList<MySubType>(MySubTypeListVar, TargetList);
  20. end.
  21.  

Please note that due to a bug you currently can't use the constraint for the TMoreSpecificObjectList<> as then the implementation of SetListBoxFromList<>() will complain (just test it, you'll see what I mean).

 

TinyPortal © 2005-2018