Advanced Records, Operator Overloading & Helpers
In FPC objfpc mode, operators can also be declared globally with symbols:
31.1 Operator overloading in records (Delphi syntax)
program Complex;
{$IFDEF FPC}{$mode delphi}{$ENDIF}
{$APPTYPE CONSOLE}
uses SysUtils;
type
TComplex = record
Re, Im: Double;
class function Create(ARe, AIm: Double): TComplex; static;
class operator Add(const A, B: TComplex): TComplex;
class operator Subtract(const A, B: TComplex): TComplex;
class operator Multiply(const A, B: TComplex): TComplex;
class operator Equal(const A, B: TComplex): Boolean;
class operator NotEqual(const A, B: TComplex): Boolean;
class operator Negative(const A: TComplex): TComplex;
class operator Implicit(const D: Double): TComplex; // Double -> TComplex automatically
function Abs: Double;
function ToString: string;
end;
class function TComplex.Create(ARe, AIm: Double): TComplex;
begin
Result.Re := ARe;
Result.Im := AIm;
end;
class operator TComplex.Add(const A, B: TComplex): TComplex;
begin
Result.Re := A.Re + B.Re;
Result.Im := A.Im + B.Im;
end;
class operator TComplex.Subtract(const A, B: TComplex): TComplex;
begin
Result.Re := A.Re - B.Re;
Result.Im := A.Im - B.Im;
end;
class operator TComplex.Multiply(const A, B: TComplex): TComplex;
begin
Result.Re := A.Re * B.Re - A.Im * B.Im;
Re
…31.2 Operator names
LogicalNot / BitwiseAnd / BitwiseOr / BitwiseXor not / and / or / xor LeftShift / RightShift Inc() / Dec() TType(X) cast Initialize / Finalize custom managed record hooks Assign (Delphi) / Copy (FPC) record assignment hook
In FPC objfpc mode, operators can also be declared globally with symbols:
and inside advanced records as class operator + (const A, B: TVec2): TVec2;.
{$mode objfpc}
type
TVec2 = record X, Y: Double; end;
operator + (const A, B: TVec2): TVec2;
begin
Result.X := A.X + B.X;
Result.Y := A.Y + B.Y;
end;
operator * (const A: TVec2; S: Double): TVec2;
begin
Result.X := A.X * S;
Result.Y := A.Y * S;
end;31.3 Custom managed records
Records can run code on initialization, finalization, and copy — useful for RAII-style guards and smart pointers:
FPC 3.2 provides the same idea as management operators in advanced records: class operator Initialize(var aRec: TTracked);, Finalize, Copy, and AddRef.
// Delphi 10.4+
type
TTracked = record
Value: Integer;
class operator Initialize(out Dest: TTracked);
class operator Finalize(var Dest: TTracked);
end;
class operator TTracked.Initialize(out Dest: TTracked);
begin
Dest.Value := 0;
Writeln('init');
end;
class operator TTracked.Finalize(var Dest: TTracked);
begin
Writeln('finalize');
end;31.4 Class and record helpers
A helper adds methods to an existing type without inheritance — handy for types you cannot change:
Rules and caveats:
Only one helper per type is active in a scope (the nearest one wins) — a custom helper for string or Integer hides the RTL's TStringHelper/TIntegerHelper unless it inherits from them (record helper(TStringHelper) for string). FPC trunk adds {$modeswitch multihelpers}.
Helpers can add methods, properties, and class members, but not instance fields.
FPC objfpc mode uses type helper for Integer for primitive types (with {$modeswitch typehelpers}); record helper for works in delphi mode.
Use helpers sparingly: they are a convenience, not a design tool.
program Helpers;
{$IFDEF FPC}{$mode delphi}{$modeswitch typehelpers}{$ENDIF}
{$APPTYPE CONSOLE}
uses SysUtils, Classes;
type
TIntegerHelperEx = record helper for Integer
function IsEven: Boolean;
function Times(const S: string): string;
end;
TStringsHelper = class helper for TStrings
function JoinWith(const Sep: string): string;
end;
function TIntegerHelperEx.IsEven: Boolean;
begin
Result := not Odd(Self);
end;
function TIntegerHelperEx.Times(const S: string): string;
var
I: Integer;
begin
Result := '';
for I := 1 to Self do
Result := Result + S;
end;
function TStringsHelper.JoinWith(const Sep: string): string;
var
I: Integer;
begin
Result := '';
for I := 0 to Count - 1 do
begin
if I > 0 then Result := Result + Sep;
Result := Result + Strings[I];
end;
end;
var
N: Integer;
SL: TStringList;
begin
N := 4;
Writeln(N.IsEven, ' ', N.Times('ab')); // TRUE abababab
SL := TStringList.Create;
try
SL.Add('a'); SL.Add('b'); SL.Add('c');
Writeln(SL.JoinWith(' + ')); // a + b + c
finally
SL.Free;
end;
end.