Sign in Join
Wiki copytemporary

Templates

English · Français · Русский

Introduction

Note: Templates as presented on this page are no longer needed because Generics support in FPC is complete and usable enough.

Templates is a simple mechanism which can be used to solve the problem of writing duplicate code to implement a general class for specific types in a strongly typed language. It is used mainly for base (non-object) types; for objects, inheritance from the single base class TObject is usually used to build general classes. In advanced languages, templates are replaced by a native-language implementation of so-called “generics” or ”parametrized types”. Templates are another more complex alternative to implement generic code with FPC.

Pros:

Cons:

Usage

The classic example of a basic generic class is a list of items. Free Pascal currently offers three ways of using lists:

Templates tries to solve the problem using the concept of parametrized include files. A generic unit is written only once, as a template with the use of generic (not specified) types in both interface and implementation sections. Then in the process of specialization, a class template is used and general template parameters are replaced by specific types to create a new specialized class.

GenericList.inc:

pascal
{$IFDEF INTERFACE}     
  // TGList<T> = class
  TGList = class
    Items: array of T;
    procedure Add(Item: T);
  end;
{$UNDEF INTERFACE}     
{$ENDIF}     

{$IFDEF IMPLEMENTATION}     
procedure TGList.Add(Item: T);
begin
  SetLength(Items, Length(Items) + 1);  
  Items[Length(Items) - 1] := Item;
end;
{$UNDEF IMPLEMENTATION}     
{$ENDIF}

In process of specialization early created template files are included to new unit.

ListInteger.pas:

pascal
unit ListInteger;
{$mode ObjFPC} 
interface

uses
  Classes;

type
  T = Integer; // T is specified to some exact type
{$DEFINE INTERFACE}
{$INCLUDE 'GenericList.inc'}

type
  // TListInteger<Integer> = class
  TListInteger = class(TGList)
    // Additional fields and methods can be added here
  end;

implementation

{$DEFINE IMPLEMENTATION}
{$INCLUDE 'GenericList.inc'}

end.

Finally we have new specialized unit called ListInteger and we can use our new specialized type in some code.

pascal
program GenericTest;
{$mode ObjFPC}
uses
  ListInteger;
var
  List: TListInteger;
begin
  try
    List := TListInteger.Create;
    List.Add(1);
    List.Add(2);
    List.Add(3);
  finally
    List.Free;
  end;
end.

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 class which inherits from other generic class some naming scheme should be kept.

Assume that we want to create advanced generic list TAdvancedList which have Capacity field in addition.

Base generic class definition: GenericList.inc:

pascal
{$IFDEF INTERFACE}     
  // TGList<TListIndex, TListItem> = class
  TGList = class
    Items: array[TListIndex] of TListItem;
    procedure Add(Item: TListItem);
  end;
{$UNDEF INTERFACE}
{$ENDIF}

{$IFDEF IMPLEMENTATION}
procedure TGList.Add(Item: TListItem);
begin
  SetLength(Items, Length(Items) + 1);  
  Items[Length(Items) - 1] := Item;
end;
{$UNDEF IMPLEMENTATION}
{$ENDIF}

Definition for enhanced generic class:

GenericAdvancedList.inc:

pascal
{$IFDEF INTERFACE}     
  TListIndex = TAdvancedListIndex;
  TListItem = TAdvancedListItem;
{$DEFINE INTERFACE}     
{$INCLUDE 'GenericList.inc'}
  // TGAdvancedList<TAdvancedListIndex, TAdvancedListItem> = class(TGList)
  TGAdvancedList = class
    Capacity: TAdvancedListIndex;
  end;
{$UNDEF INTERFACE}
{$ENDIF}

{$IFDEF IMPLEMENTATION}
{$INCLUDE 'GenericList.inc'}
{$UNDEF IMPLEMENTATION}
{$ENDIF}

Now a specialization example:

AdvancedListInteger.pas:

pascal
unit AdvancedListInteger;

interface

uses
  Classes;

type
  TAdvancedListIndex = Integer; // specified to some exact type
  TAdvancedListItem = Integer; // specified to some exact type
{$DEFINE INTERFACE}
{$INCLUDE 'GenericAdvancedList.inc'}

type
  // TAdvancedListInteger<Integer, Integer> = class
  TAdvancedListInteger = class(TGAdvancedList)
    // Additional fields and methods can be added here
  end;

implementation

{$DEFINE IMPLEMENTATION}
{$INCLUDE 'GenericAdvancedList.inc'}

end.

Now we have specialized class from generic class which inherits form other generic class.

Constraints

It is good to have class which e.g. works as containers for any other type. Container class doesn't need to know anything about contained class. But some classes could benefits from knowledge of what can be done with contained class. In native generics implementation there is possibility to restrict group of types which can be used as type parameters of generic class. In templates there aren't such feature but constraints are done simply by usage of generic type in generic class. If generic type is used for addition and subtraction than only types which supports that operations can be used. String can be concatenated using plus operator too. But if multiplication is used than only numeric types are allowed. If generic type is used as array index, than only ordinal types can be used. If some e.g. Free method is used over generic type than constraint include all classes. So in templates operations performed with generic type limits set of usable types for specialization.

Theoretical constraint groups:

Generic classes

There are many classes which can benefit from being generic:

There are some interesting specialized types which can replace non-generic types:

Specialized generic class Similar data type
TList<Char> string
TList<WideChar> WideString
TList<Byte> array of Byte
TList<string> Classes.TStrings
TList<Pointer> Classes.TList
TList<Boolean> Classes.TBits
TList<TObject> Contnrs.TObjectList
TList<TComponent> Contnrs.TComponentList
TList<TClass> Contnrs.TClassList
TStack<Pointer> Contnrs.TStack
TStack<TObject> Contnrs.TObjectStack
TQueue<Pointer> Contnrs.TQueue
TQueue<TObject> Contnrs.TObjectQueue
TStream<Byte> Classes.TMemoryStream
TDictionary<TPair<string, string>> Classes.TStringList
TList<TMethod> LCLProc.TMethodList
TTree<TTreeNode> ComCtrls.TTreeView
TPoint<Integer> Classes.TPoint
TPoint<SmallInt> Classes.TSmallPoint
TRectangle<Integer> Classes.TRect

Existed generic libraries

Every programmer can build own library of generic classes.

Some ready to use classes:

See also

External links


From the Free Pascal wiki, page "Templates", as it stood on 2020-02-29 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 1, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.