Difference between revisions of "Generics"

From Lazarus wiki
Jump to navigationJump to search
m (Correct link to "Interfaces", not to "unit interface")
 
(3 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
{{Generics}}
 
{{Generics}}
 +
 +
'''Generics''' are sometimes called parameterized types.
  
 
==Introduction==
 
==Introduction==
[[FPC]] has had official support for generics in [[Mode_ObjFPC|<syntaxhighlight lang="pascal" enclose="none">{$mode ObjFPC}</syntaxhighlight>]] since version 2.2. and in [[Mode_Delphi|<syntaxhighlight lang="pascal" enclose="none">{$mode Delphi}</syntaxhighlight>]] since version 2.6.0.
+
[[FPC]] has had official support for generics in [[Mode_ObjFPC|<syntaxhighlight lang="pascal" inline>{$mode ObjFPC}</syntaxhighlight>]] since version 2.2.0, and in [[Mode_Delphi|<syntaxhighlight lang="pascal" inline>{$mode Delphi}</syntaxhighlight>]] since version 2.6.0.
 
 
Generics are sometimes called parameterized types.
 
  
 
The reason that FPC supports two different dialects is simply that FPC implemented generics several years before [[Delphi]].
 
The reason that FPC supports two different dialects is simply that FPC implemented generics several years before [[Delphi]].
  
It is possible to use [[Unit|units]] written in <syntaxhighlight lang="pascal" enclose="none">{$mode ObjFPC}</syntaxhighlight> syntax in other units that use <syntaxhighlight lang="pascal" enclose="none">{$mode Delphi}</syntaxhighlight>, and vice versa.
+
It is possible to use [[Unit|units]] written in <syntaxhighlight lang="pascal" inline>{$mode ObjFPC}</syntaxhighlight> syntax in other units that use <syntaxhighlight lang="pascal" inline>{$mode Delphi}</syntaxhighlight>, and vice versa.
  
The '''Free Generics Library''' or '''FGL''' is an FPC-native collection of generic containers written in <syntaxhighlight lang="pascal" enclose="none">{$mode ObjFPC}</syntaxhighlight>.
+
The '''Free Generics Library''' or '''FGL''' is an FPC-native collection of generic containers written in <syntaxhighlight lang="pascal" inline>{$mode ObjFPC}</syntaxhighlight>.
  
The '''rtl-generics''' package is a larger, somewhat more featureful collection of generic containers written in <syntaxhighlight lang="pascal" enclose="none">{$mode Delphi}</syntaxhighlight> that tries to be compatible with the Delphi generics library. This package is provided as standard in FPC 3.1.1.+ but there is a version for FPC 3.0.4. available.
+
The '''rtl-generics''' package is a larger, somewhat more featureful collection of generic containers written in <syntaxhighlight lang="pascal" inline>{$mode Delphi}</syntaxhighlight> that tries to be compatible with the Delphi generics library. This package is provided as standard in FPC 3.1.1+, but there is a version for FPC 3.0.4 available as well.
  
 
Both '''FGL''' and '''rtl-generics''' can be used in both syntax modes.
 
Both '''FGL''' and '''rtl-generics''' can be used in both syntax modes.
  
==fgl unit==
+
== FGL unit ==
The easiest way to get started with generics is to use the [http://freepascal.org/docs-html/current/rtl/fgl/index.html fgl unit], which is a prototype unit for base system generic [[Class|classes]]. So far it contains a few basic classes:
+
The easiest way to get started with generics is to use the [https://www.freepascal.org/docs-html/current/rtl/fgl/index.html fgl unit], which is a prototype unit for base system generic [[Class|classes]]. So far it contains a few basic classes:
 
* TFPGList
 
* TFPGList
 
* TFPGObjectList
 
* TFPGObjectList
Line 23: Line 23:
 
* TFPGMap
 
* TFPGMap
  
===Getting Started===
+
=== Getting started ===
 
The following simple example shows how to store multiple instances of a user defined class in a list:
 
The following simple example shows how to store multiple instances of a user defined class in a list:
 
+
<syntaxhighlight lang="pascal">
<syntaxhighlight>
 
 
{$mode objfpc}
 
{$mode objfpc}
 
uses fgl;
 
uses fgl;
Line 51: Line 50:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Custom Generic Classes==
+
== Custom generic classes ==
 
If the generics defined in the fgl unit do not suit your needs, you may need to define your own generic classes from scratch using the underlying language primitives.
 
If the generics defined in the fgl unit do not suit your needs, you may need to define your own generic classes from scratch using the underlying language primitives.
  
 
A generic class is defined using the [[Keyword|keyword]] '''generic''' before the class name and use in class declaration:
 
A generic class is defined using the [[Keyword|keyword]] '''generic''' before the class name and use in class declaration:
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
type
 
type
 
   generic TList<T> = class
 
   generic TList<T> = class
Line 63: Line 62:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Example of generic class implementation:
+
Here's an example of a generic class implementation:
 
+
<syntaxhighlight lang="pascal">
<syntaxhighlight>
 
 
implementation
 
implementation
  
Line 75: Line 73:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
A generic class, [[Object|object]], [[Record|record]], [[Interface|interface]] or [[Method|method]] can be simply specialized for a particular type by using the '''specialize''' keyword.
+
A generic class, [[Object|object]], [[Record|record]], [[Interfaces|interface]] or [[Method|method]] can be simply specialized for a particular type by using the '''specialize''' keyword.
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
Type   
 
Type   
 
   TIntegerList = specialize TList<Integer>;
 
   TIntegerList = specialize TList<Integer>;
Line 83: Line 81:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Other Points ==
+
== Other points ==
# The [[Compiler|compiler]] parses a generic, but instead of generating code it stores all tokens in a token buffer inside the PPU file.
+
# The [[Compiler|compiler]] parses a generic, but instead of generating code it stores all tokens in a token buffer inside the PPU file.
# The compiler parses a specialization; for this it loads the token buffer from the PPU file and parses that again. It replaces the generic parameters (in most examples "T") by the particular given type (e.g. '''[[Longint|LongInt]]''', '''[[TObject]]''').
+
# The compiler parses a specialization; for this it loads the token buffer from the PPU file and parses that again. It replaces the generic parameters (in most examples "T") by the particular given type (e.g. '''[[Longint|LongInt]]''', '''[[TObject]]'''). The code basically appears as if the same class had been written as the generic but with T replaced by the given type.
The code basically appears as if the same class had been written as the generic but with T replaced by the given type.
 
  
Therefore in theory there should be no speed differences between a "normal" class and a generic one.
+
Therefore, in theory, there should be no speed differences between a "normal" class and a generic one.
  
 
== Example ==
 
== Example ==
 
{{:How to use generics}}
 
{{:How to use generics}}
  
==See also==
+
== See also ==
 
* [[Templates]]
 
* [[Templates]]
 
* [[Generics proposals]]
 
* [[Generics proposals]]
* [[Data Structures, Containers, Collections]]
+
* [[Data Structures, Containers, Collections|Data Structures, Containers and Collections]]
  
==External links==
+
== External links ==
* [http://www.freepascal.org/docs-html/ref/refch8.html Free Pascal: Reference guide - Generics]
+
* [https://www.freepascal.org/docs-html/ref/refch8.html Free Pascal: Reference Guide &ndash; Generics]

Latest revision as of 13:43, 28 March 2024

English (en) français (fr) 한국어 (ko) polski (pl) русский (ru)

Generics are sometimes called parameterized types.

Introduction

FPC has had official support for generics in {$mode ObjFPC} since version 2.2.0, and in {$mode Delphi} since version 2.6.0.

The reason that FPC supports two different dialects is simply that FPC implemented generics several years before Delphi.

It is possible to use units written in {$mode ObjFPC} syntax in other units that use {$mode Delphi}, and vice versa.

The Free Generics Library or FGL is an FPC-native collection of generic containers written in {$mode ObjFPC}.

The rtl-generics package is a larger, somewhat more featureful collection of generic containers written in {$mode Delphi} that tries to be compatible with the Delphi generics library. This package is provided as standard in FPC 3.1.1+, but there is a version for FPC 3.0.4 available as well.

Both FGL and rtl-generics can be used in both syntax modes.

FGL unit

The easiest way to get started with generics is to use the fgl unit, which is a prototype unit for base system generic classes. So far it contains a few basic classes:

  • TFPGList
  • TFPGObjectList
  • TFPGInterfacedObjectList
  • TFPGMap

Getting started

The following simple example shows how to store multiple instances of a user defined class in a list:

{$mode objfpc}
uses fgl;

type
   TMyClass = class(TObject)
      fld1 : string;
   end;

   TMyList = specialize TFPGObjectList<TMyClass>;

var
   list : TMyList;
   c : TMyClass;

begin
   // create the list and add an element
   list := TMyList.Create;
   c := TMyClass.Create;
   c.fld1 := 'c1';
   list.Add(c);
   // retrieve an element from the list
   c := list[0];

Custom generic classes

If the generics defined in the fgl unit do not suit your needs, you may need to define your own generic classes from scratch using the underlying language primitives.

A generic class is defined using the keyword generic before the class name and use in class declaration:

type
  generic TList<T> = class
    Items: array of T;
    procedure Add(Value: T);
  end;

Here's an example of a generic class implementation:

implementation

procedure TList.Add(Value: T);
begin
  SetLength(Items, Length(Items) + 1);
  Items[Length(Items) - 1] := Value;
end;

A generic class, object, record, interface or method can be simply specialized for a particular type by using the specialize keyword.

Type  
  TIntegerList = specialize TList<Integer>;
  TPointerList = specialize TList<Pointer>;
  TStringList = specialize TList<string>;

Other points

  1. The compiler parses a generic, but instead of generating code it stores all tokens in a token buffer inside the PPU file.
  2. The compiler parses a specialization; for this it loads the token buffer from the PPU file and parses that again. It replaces the generic parameters (in most examples "T") by the particular given type (e.g. LongInt, TObject). The code basically appears as if the same class had been written as the generic but with T replaced by the given type.

Therefore, in theory, there should be no speed differences between a "normal" class and a generic one.

Example

An example of how to use generics to write a function gmax() that takes the maximum of two not-yet-typed variables. Note that while the functions here are namespaced by the classname, FPC versions from 3.1.1 onwards also support fully free-standing generic methods.

program UseGenerics;

{$mode objfpc}{$H+}

type
  generic TFakeClass<_GT> = class
    class function gmax(a,b: _GT):_GT;
  end;

  TFakeClassInt = specialize TFakeClass<integer>;
  TFakeClassDouble = specialize TFakeClass<double>;

  class function TFakeClass.gmax(a,b: _GT):_GT;
  begin
    if a > b then 
      result := a
    else 
      result := b;
  end;

begin
    // show max of two integers
  writeln( 'Integer GMax:', TFakeClassInt.gmax( 23, 56 ) );
    // show max of two doubles
  writeln( 'Double GMax:', TFakeClassDouble.gmax( 23.89, 56.5) );
  readln();
end.

See also

External links