A.M. Hoornweg 144 Posted December 18, 2023 Hello all, suppose I have a generic class that's going to be inherited. I want to give the base class a method that creates a new instance of the same object type (a class factory so to speak). How can I do that? I need to somehow tell the compiler which constructor to call. Type tSomeclass<T:iInterface>=CLass (tInterfacedObject, iSomeInterface<T>) //many methods here Procedure SomeMethod(intf: T); Function SomeFunction:T; Constructor Create(someparameters); Function CreateNewInstance:iSomeInterface<T>; end; ... Function tSomeclass<T>.CreateNewInstance:iSomeInterface<T> begin result:=Self.Classtype.Create(fSomeParameters); //Does not compile end; Share this post Link to post
Eugine Savin 4 Posted December 18, 2023 Ha, delphi 10.4 has a bug here. type iSomeInterface<T> = interface end; Type tSomeclass<T:iInterface>=CLass (tInterfacedObject, iSomeInterface<T>) Constructor Create(s: string); Function CreateNewInstance:iSomeInterface<T>; end; Function tSomeclass<T>.CreateNewInstance:iSomeInterface<T>; type TSelfClass = tSomeclass<T>; TSomeClassType = class of TSelfClass; // !!!!!!!!!!!! [dcc32 Error] Unit9.pas(36): E2021 Class type required begin result:=TSomeClassType(Self.Classtype).Create('s'); //Does not compile end; Share this post Link to post
Remy Lebeau 1396 Posted December 18, 2023 Even if you could get this to compile, you need to make the constructor virtual in order to call a derived constructor from a base metaclass type. Share this post Link to post
Uwe Raabe 2057 Posted December 18, 2023 Even if it would compile this way, it is quite possible that it does not do what you expect it to do: result := tSomeclass<T>(Classtype).Create(fSomeParameters); Share this post Link to post
Anders Melander 1783 Posted December 18, 2023 6 hours ago, A.M. Hoornweg said: Type tSomeclass<T:iInterface>=CLass (tInterfacedObject, iSomeInterface<T>) //many methods here Procedure SomeMethod(intf: T); Function SomeFunction:T; Constructor Create(someparameters); Function CreateNewInstance:iSomeInterface<T>; end; ... Function tSomeclass<T>.CreateNewInstance:iSomeInterface<T> begin result:=Self.Classtype.Create(fSomeParameters); //Does not compile end; I tHiNk YoU NeEd To DeBoUnCe YoUr ShIfT KeYs... I think you can get something to compile but even then I have a feeling that, whatever it is you are trying to do, isn't possible. FWIW, this one compiles: type IMyInterface<T> = interface function GetValue: T; function Clone: IMyInterface<T>; end; TMyClass<T> = class(TInterfacedObject, IMyInterface<T>) private FValue: T; private // IMyInterface<T> function GetValue: T; function Clone: IMyInterface<T>; public constructor Create(const AValue: T); virtual; function CloneObject: TMyClass<T>; end; function TMyClass<T>.Clone: IMyInterface<T>; begin Result := CloneObject; end; function TMyClass<T>.CloneObject: TMyClass<T>; begin Result := TMyClass<T>.Create(FValue); end; constructor TMyClass<T>.Create(const AValue: T); begin FValue := AValue; end; function TMyClass<T>.GetValue: T; begin Result := FValue; end; Share this post Link to post
A.M. Hoornweg 144 Posted December 18, 2023 That doesn't do the trick, because function TMyClass<T>.CloneObject: TMyClass<T>; ... will just construct an instance of the base class, I would have to re-implement that method in each derived class. With non-generic classes it is possible to achieve what I want (see below). With generics, the "class of..." syntax does not work. type SomeClassType = class of tSomeClass; tSomeClass = class protected SomeNumber: Integer; public constructor Create(aSomeNumber: Integer); virtual; function Copy: tSomeClass; procedure Whoami; end; tSomeClass2=Class(tSomeClass) End; tSomeClass3=Class(tSomeClass2) End; function tSomeClass.Copy: tSomeClass; var ctype: SomeClassType; begin ctype := SomeClassType(self.ClassType); result := ctype.Create(SomeNumber+1); end; constructor tSomeClass.Create(aSomeNumber: Integer); begin inherited Create; SomeNumber := aSomeNumber; end; procedure tSomeClass.Whoami; begin OutputDebugString(pchar( format('Class name = %s, number is %d',[self.classname,somenumber] ))); end; procedure TForm1.Button2Click(Sender: TObject); begin tSomeclass3.Create(1).Copy.Copy.Copy.Whoami; //yes, this test leaks some memory. end; Share this post Link to post
Anders Melander 1783 Posted December 18, 2023 1 hour ago, A.M. Hoornweg said: That doesn't do the trick, because function TMyClass<T>.CloneObject: TMyClass<T>; ... will just construct an instance of the base class, I would have to re-implement that method in each derived class. Yes, you are right - and you couldn't do it with polymorphism. Share this post Link to post
Anders Melander 1783 Posted December 18, 2023 https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Overview_of_Generics#Dynamic_instantiation Quote Dynamic instantiation at run time is not supported. Share this post Link to post
Stefan Glienke 2002 Posted December 18, 2023 (edited) You can trick/hack a bit and do what you want to achieve manually: This is basically what a regular ctor call on the class type does (see System._ClassCreate). function TSomeClass<T>.CreateNewInstance: ISomeInterface<T>; var obj: TSomeClass<T>; begin obj := TSomeClass<T>(ClassType.NewInstance); obj.Create(fSomeParameters); Result := obj; end; Edited December 19, 2023 by Stefan Glienke Fixed typo 1 Share this post Link to post
Remy Lebeau 1396 Posted December 18, 2023 25 minutes ago, Stefan Glienke said: This is basically what a regular ctor call on the class type does (see System._CreateClass). Where is System._CreateClass defined? I cannot find it in any RTL source file, and when I do a test creating a class instance from a metaclass, there is no such function call. Share this post Link to post
Stefan Glienke 2002 Posted December 19, 2023 (edited) 3 hours ago, Remy Lebeau said: Where is System._CreateClass defined? I cannot find it in any RTL source file, and when I do a test creating a class instance from a metaclass, there is no such function call. Sorry, I meant _ClassCreate - the compiler inserts a call to that routine in the prologue of each ctor. When calling the ctor on a class the compiler passes 1 as the second parameter which signals this routine to call TObject.NewInstance. Edited December 19, 2023 by Stefan Glienke Share this post Link to post
A.M. Hoornweg 144 Posted December 19, 2023 The reason I'm trying to achieve this is the following. I have made a generic tStreamableInterfaceList<T:iInterface> which is basically a managed Tlist<T> that implements iInterfacelist<T>. The elements are interfaces. This list object knows several special tricks such as saving its contents to XML and BSON. It can also load those elements back from such data files, which necessitates a virtual ClassFactory() method to create the correct objects "on the fly" based on the class names found in the data. So far, this all works very nicely. I wanted to enhance this list with a few LinQ-like methods such as: Function .Where(aCondition:tPredicate<T>):iInterfacelist<T>; Function .OrderBy(aCompare:tComparison<T>):iInterfacelist<T>; ... but in order to achieve that, the base class must be able to dynamically instantiate derived classes or else the resulting objects would have the base nonfunctional Classfactory() method. The "ugly" solution would be to put an abstract virtual Clone() method in the base class, but I'd very much like to avoid that. Share this post Link to post
A.M. Hoornweg 144 Posted December 19, 2023 13 hours ago, Stefan Glienke said: You can trick/hack a bit and do what you want to achieve manually: This is basically what a regular ctor call on the class type does (see System._ClassCreate). function TSomeClass<T>.CreateNewInstance: ISomeInterface<T>; var obj: TSomeClass<T>; begin obj := TSomeClass<T>(ClassType.NewInstance); obj.Create(fSomeParameters); Result := obj; end; Thank you! That seems to work indeed ! Share this post Link to post