Skip to content
Lesson 2 of 7

Step 1 of 5 · Reading · ~2 min

Learn

Object Pascal Classes

Generics and Containers

A container that stores Pointer and hands you back Pointer forces a cast at every use site, and a wrong cast is a crash rather than a compile error. Generics move that check to compile time: you name the element type once, and the compiler refuses every mismatched Add and every mismatched read.

The container you have on this grader

FreePascal ships two generic container libraries, and which one you get depends on the compiler version. Generics.Collections (TList<T>, TDictionary<K,V>) arrived in FPC 3.2. The grader behind this course runs FPC 3.0.4, where that unit does not exist at all — uses Generics.Collections fails with Fatal: Can't find unit Generics.Collections. What 3.0.4 does have is fgl, the Free Generic Library:

program demo;
{$mode objfpc}{$H+}
uses fgl;
type
    TIntList = specialize TFPGList<integer>;
var
    list: TIntList;
begin
    list := TIntList.Create;
    try
        list.Add(10);
        list.Add(20);
        WriteLn(list[1]);      // 20
        WriteLn(list.Count);   // 2
    finally
        list.Free;
    end;
end.

The specialize keyword is the trap

In {$mode objfpc} you cannot write TFPGList<integer> directly the way Delphi or C# lets you. Object Pascal spells the instantiation out: specialize at the use site, generic at the declaration site.

type
    TIntList  = specialize TFPGList<integer>;   { correct in objfpc mode }
    TBadList  = TFPGList<integer>;              { Error: generic type without specialization }

    generic TBox<T> = class                     { declaring your own }
    private
        FValue: T;
    public
        property Value: T read FValue write FValue;
    end;

    TIntBox = specialize TBox<integer>;

Giving the specialization a named type (TIntList) rather than repeating it inline is the usual style, and it is what keeps the rest of the program readable.

What fgl gives you

  • TFPGList<T> — a growable array: Add, Count, [i], Delete, IndexOf, Sort.
  • TFPGObjectList<T> — the same, but it frees the objects it holds when it is freed.
  • TFPGMap<K, V> — a sorted key/value map: Add, KeyData[k], IndexOf.

A TFPGList is enumerable, so for x in list do is the idiomatic read loop — you do not need the index unless you actually want it:

total := 0;
for x in list do
    total := total + x;

Memory still belongs to you

Generics change the type system, not the memory model. TIntList.Create allocates, and you free it in a finally exactly as with any other class. A TFPGList of objects frees the list, not the objects in it — that is what TFPGObjectList is for, and confusing the two is the standard leak in Pascal code that has just discovered containers.

Generics here are monomorphized: each specialize compiles its own copy of the code, the way C++ templates do. That is why the type checking is complete and why nothing is boxed.

Your exercise

Build a specialize TFPGList<integer>, fill it from stdin, and sum it with for x in list do. The mistake the compiler catches first is dropping specialize: TFPGList<integer> on its own is a syntax error in objfpc mode, not a subtle bug.

Up nextException HandlingExceptions and File I/O

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…