[Lazarus] Generics type as parameter
Leonardo M. Ramé
l.rame at griensu.com
Sat May 12 18:07:18 CEST 2012
On 2012-05-12 17:00:22 +0200, Sven Barth wrote:
> On 12.05.2012 13:32, Leonardo M. Ramé wrote:
> >I'm trying to work in an abstract way with generics, I need to create a
> >method receiving a un-specialized generic and do some work on it.
> >
> >Example:
> >
> >function ProcessGeneric(AList: TFPGList);
> >begin
> > ... do something ...
> >end;
> >
> >Imagine your application uses many lists of specialized objects, such as
> >TCustomer, TBooks, TColors. To create a list of those types, you'd do
> >something like this:
> >
> > TCustomers = specialize TFPGList<TCustomer>;
> > TBooks = specialize TFPGList<TBook>;
> > TColors = specialize TFPGList<TColor>;
> >
> >Then, you need to pass any of those types to "ProcessGeneric":
> >
> >var
> > lCustomers: TCustomers;
> > lBooks: TBooks;
> > lColors: TColors;
> >
> >begin
> > ProcessGeneric(lCustomers);
> > ProcessGeneric(lBooks);
> > ProcessGeneric(lColors);
> >end;
> >
> >Without generics, I can use, for example, a TObjectList, or a
> >TCollection, without any problem. Is there a way to do this using
> >Generics?.
>
> There are basically to ways you can achive this (one is supported by FPC the
> other not yet):
> * descend the generic from a baseclass you can work with (e.g. as Michalis
> already mentioned the FGL classes all descend from a baseclass that is
> tailored to the needs of the generic, e.g. TFPGList descends from TFPSList)
> * use generic procedures (this is not yet supported with FPC, it's on my
> ToDo list though).
>
> In case of generic procedures your example will then look like this (in mode
> Delphi):
>
> === example begin ===
>
> procedure ProcessGeneric<T>(AList: TFPGList<T>);
> begin
> ...
> end;
>
> var
> lCustomers: TCustomers;
> lBooks: TBooks;
> lColors: TColors;
>
> begin
> ProcessGeneric<TCustomer>(lCustomers);
> ProcessGeneric<TBook>(lBooks);
> ProcessGeneric<TColor>(lColors);
> end;
>
> === example end ===
>
> (in mode ObjFPC there would be a bit more "generic" and "specialize" of
> course)
>
> In mode Delphi you can also use a workaround in the current version (2.7.1)
> of FPC (it should work at least, but I haven't tested it...):
>
> === workaround begin ===
>
> TMyListProcessor<T> = class
> class procedure ProcessGeneric(AList: TFPGList<T>);
> end;
>
> class procedure TMyListProcessor<T>(AList: TFPGList<T>);
> begin
>
> end;
>
> var
> lCustomers: TCustomers;
> lBooks: TBooks;
> lColors: TColors;
>
> begin
> TMyListProcessor<TCustomer>.ProcessGeneric(lCustomers);
> TMyListProcessor<TBook>.ProcessGeneric(lBooks);
> TMyListProcessor<TColor>.ProcessGeneric(lColors);
> end;
>
> === workaround end ===
>
> This won't work as written above in mode ObjFPC (yet), because you'd always
> need to specialize the class in a type section.
>
> Regards,
> Sven
>
Thanks Sven and Michalis. Using the suggestion by Michalis, I got a
solution mixing Generics and regular objects.
What I'm pursuing is to replace my TCollection based ORM, with a
Generics based one, this allow users to write less code, in a clearer
way.
The way the ORM works, is to create a TCollection instance, then execute
an ORM's method to load/save data into/from it, example:
var
lCustomers: TCustomers; // this is a TCollection descendant
begin
lCustomers := TCustomers.Create;
FConnector.LoadData(lCustomers, []);
... do something with lCustomers ...
lCustomers.Free;
end;
As you can see, FConnector.LoadData receives a TCollection as parameter,
and using RTTI, it fills each TCustomer published property.
With Michalis's solution, I can turn my ORM from TCollections to
Generics with very little changes, see how small is the unit customer
now, instead of a complete TCollection/TCollectionItem definition:
///// ---- customer.pas ----
unit customer;
{$mode objfpc}
interface
uses
Classes;
type
TCustomer = class
private
FName: string;
FAge: Integer;
published
property Name: string read FName write FName;
property Age: Integer read FAge write FAge;
end;
implementation
end.
///// ---- generics.pas ----
program generics;
{$mode objfpc}
uses
sysutils,
customer,
typinfo,
variants,
fgl;
type
TCustomerList = specialize TFPGList<TCustomer>;
procedure ProcessGeneric(AList: TFPSList);
var
I: Integer;
lVal: string;
begin
for I:= 0 to AList.Count - 1 do
begin
lVal := string(GetPropValue(TObject(AList.Items[I]^), 'Name'));
writeln(Format('%s (%d)', [lVal, I]));
end;
end;
var
lList: TCustomerList;
lCustomer: TCustomer;
begin
lList := TCustomerList.Create;
lCustomer := TCustomer.Create;
lCustomer.Name := 'Martín';
lCustomer.Age := 37;
lList.Add(lCustomer);
lCustomer := TCustomer.Create;
lCustomer.Name := 'Nico';
lCustomer.Age := 28;
lList.Add(lCustomer);
ProcessGeneric(lList);
lList.Free;
end.
--
Leonardo M. Ramé
http://leonardorame.blogspot.com
More information about the Lazarus
mailing list