Difference between revisions of "Templates"
(pros and cons) |
(Example of iheritance) |
||
Line 54: | Line 54: | ||
type | type | ||
+ | // TListInteger<Integer> = class | ||
TListInteger = class(TGList) | TListInteger = class(TGList) | ||
// Additional fields and methods can be added here | // Additional fields and methods can be added here | ||
Line 83: | Line 84: | ||
===Inheritance=== | ===Inheritance=== | ||
+ | |||
+ | Generic classes could inherit from each other in way of non-generic classes do. Implementation is little difficult as only one specialized type of particular class can be defined per unit. To be able to specify generic type to generic type o other class some naming scheme should be kept. | ||
+ | |||
+ | Assume that we want to create advanced generic list TAdvancedList which have in addition Capacity field. | ||
+ | |||
+ | Base generic class definition: | ||
+ | ''GenericListInterface.tpl:'' | ||
+ | <delphi> // TGList<TListIndex, TListItem> = class | ||
+ | TGList = class | ||
+ | Items: array[TListIndex] of TListItem; | ||
+ | procedure Add(Item: TListItem); | ||
+ | end;</delphi> | ||
+ | |||
+ | ''GenericListImplementation.tpl:'' | ||
+ | <delphi>procedure TGList.Add(Item: TListItem); | ||
+ | begin | ||
+ | SetLength(Items, Length(Items) + 1); | ||
+ | Items[Length(Items) - 1] := Item; | ||
+ | end;</delphi> | ||
+ | |||
+ | Definition for enhanced generic class: | ||
+ | ''GenericAdvancedListInterface.tpl:'' | ||
+ | <delphi> | ||
+ | TListIndex = TAdvancedListIndex; | ||
+ | TListItem = TAdvancedListItem; | ||
+ | {$INCLUDE 'GenericListInterface.tpl'} | ||
+ | // TGAdvancedList<TAdvancedListIndex, TAdvancedListItem> = class(TGList) | ||
+ | TGAdvancedList = class | ||
+ | Capacity: TAdvancedListIndex; | ||
+ | end;</delphi> | ||
+ | |||
+ | ''GenericAdvancedListImplementation.tpl:'' | ||
+ | <delphi>{$INCLUDE 'GenericListImplementation.tpl'}</delphi> | ||
+ | |||
+ | No some specialization example: | ||
+ | ''AdvancedListInteger.pas:'' | ||
+ | <delphi>unit AdvancedListInteger; | ||
+ | |||
+ | interface | ||
+ | |||
+ | uses | ||
+ | Classes; | ||
+ | |||
+ | type | ||
+ | TAdvancedListIndex = Integer; // specified to some exact type | ||
+ | TAdvancedListItem = Integer; // specified to some exact type | ||
+ | {$INCLUDE 'GenericAdvancedListInterface.tpl'} | ||
+ | |||
+ | type | ||
+ | // TAdvancedListInteger<Integer, Integer> = class | ||
+ | TAdvancedListInteger = class(TGAdvancedList) | ||
+ | // Additional fields and methods can be added here | ||
+ | end; | ||
+ | |||
+ | implementation | ||
+ | |||
+ | {$INCLUDE 'GenericAdvancedListImplementation.tpl'} | ||
+ | |||
+ | end.</delphi> | ||
+ | |||
+ | Now we have specialized class from generic class which inherits form other generic class. | ||
===Constrains=== | ===Constrains=== |
Revision as of 11:02, 29 October 2010
Introduction
Templates is simple mechanism to solve problem of writing duplicate code to implement general class for specific type in strong typed language. It is used mainly for base non-object types as in case of objects inheritance of single base class TObject can be used to build general classes. In advanced languages templates are replaced by native language implementation called generics. Native implementation of generics in FPC 2.4.0 is not complete neither practically usable yet. Then templates could be used temporary in the meantime.
Pros:
- Type safe - no need to do annoying typecasting. Less chance to do error in typecasting.
- Better maintainability - Generic class code is written only once and used for all specialized classes
- Better code reuse - It is possible to create good generic reusable class set
- Better efficiency, scalability - generic classes types can be better adjusted for particular needs. On 16-bit machine specialized TList with 16-bit SmallInt index type could be used.
Cons:
- More complex
- Bigger code - Every specialized class is compiled separately and produce additional code
Usage
Classical example of basic generic class is list of items. Current Free Pascal offer three ways of use lists:
- array of - classical dynamic array structure which can hold dynamic items of same type. Items are accessible by direct addressing SomeArray[Index]. SetLength and Length functions are used to handle size.
- TList - objective way where class handle all list operations. Implementation in LCL keep compatibility with VCL where TList holds list of Pointers. If list of different item type is desired complete new class have to be copied and rewritten or typecasting have to be used in every place where list item is referenced. Typecasting is not effective e.g. Pointer(Byte) and not type safe Pointer(Int64). TList index is of type Integer. If Int64 or SmallInt index is needed than class have to be copied and rewritten.
- TCollection - is more generic and type safe but heavier solution for storing list of items. In this approach programmer have to create item class which inherits from TCollectionItem and set this newly created class type to TCollection constructor parameter.
- native generics - not complete implementation yet. Lack support of generic type reference, inheritance, constraints.
- templates - use code inclusion to workaround lack of native support. Can use inheritance and generic type reference. Constraints are given by used operators and other functions in generic class.
Templates tries to solve problem using concept of parametrized templates. Generic unit is written only once as template with use of generic not specified types in both interface and implementation section. Than in process of specialization class template is used and general template parameters are replaced by specific types. In result new specialized class is created.
GenericListInterface.tpl: <delphi> // TGList<T> = class
TGList = class Items: array of T; procedure Add(Item: T); end;</delphi>
GenericListImplementation.tpl: <delphi>procedure TGList.Add(Item: T); begin
SetLength(Items, Length(Items) + 1); Items[Length(Items) - 1] := Item;
end;</delphi>
In process of specialization early created template files are included to new unit.
ListInteger.pas: <delphi>unit ListInteger;
interface
uses
Classes;
type
T = Integer; // T is specified to some exact type
{$INCLUDE 'GenericListInterface.tpl'}
type
// TListInteger<Integer> = class TListInteger = class(TGList) // Additional fields and methods can be added here end;
implementation
{$INCLUDE 'GenericListImplementation.tpl'}
end.</delphi>
Finally we have new specialized unit called ListInteger and we can use our new specialized type in some code.
<delphi>program GenericTest; uses
ListInteger;
var
List: TListInteger;
begin
try List := TListInteger.Create; List.Add(1); List.Add(2); List.Add(3); finally List.Free; end;
end.</delphi>
Inheritance
Generic classes could inherit from each other in way of non-generic classes do. Implementation is little difficult as only one specialized type of particular class can be defined per unit. To be able to specify generic type to generic type o other class some naming scheme should be kept.
Assume that we want to create advanced generic list TAdvancedList which have in addition Capacity field.
Base generic class definition: GenericListInterface.tpl: <delphi> // TGList<TListIndex, TListItem> = class
TGList = class Items: array[TListIndex] of TListItem; procedure Add(Item: TListItem); end;</delphi>
GenericListImplementation.tpl: <delphi>procedure TGList.Add(Item: TListItem); begin
SetLength(Items, Length(Items) + 1); Items[Length(Items) - 1] := Item;
end;</delphi>
Definition for enhanced generic class: GenericAdvancedListInterface.tpl: <delphi>
TListIndex = TAdvancedListIndex; TListItem = TAdvancedListItem;
{$INCLUDE 'GenericListInterface.tpl'}
// TGAdvancedList<TAdvancedListIndex, TAdvancedListItem> = class(TGList) TGAdvancedList = class Capacity: TAdvancedListIndex; end;</delphi>
GenericAdvancedListImplementation.tpl: <delphi>{$INCLUDE 'GenericListImplementation.tpl'}</delphi>
No some specialization example: AdvancedListInteger.pas: <delphi>unit AdvancedListInteger;
interface
uses
Classes;
type
TAdvancedListIndex = Integer; // specified to some exact type TAdvancedListItem = Integer; // specified to some exact type
{$INCLUDE 'GenericAdvancedListInterface.tpl'}
type
// TAdvancedListInteger<Integer, Integer> = class TAdvancedListInteger = class(TGAdvancedList) // Additional fields and methods can be added here end;
implementation
{$INCLUDE 'GenericAdvancedListImplementation.tpl'}
end.</delphi>
Now we have specialized class from generic class which inherits form other generic class.
Constrains
Existed generic libraries
Every programmer can build own library of generic classes. There are some ready to use classes.
- TemplateGenerics - Lazarus package of some experimental generic classes. In addition to parametrized list item type it offer parametrized index type too. Can be downloaded from using svn client.
svn co http://svn.zdechov.net/svn/PascalClassLibrary/Generics/TemplateGenerics/ TemplateGenerics
See also
External links
- Object Pascal (Delphi) Templates
- Templates in Object Pascal
- Template (programming) - general description and priciples