management operators

From Lazarus wiki
Jump to navigationJump to search

Management Operators

From Free Pascal version 3.1.1 there is a new language feature called management operators for advanced records. The new operators are: Initialize, Finalize, AddRef and Copy. The new operators are a unique feature and are called "management operators". That is because:

  • Each record,even non-managed or empty, with management operator becomes a managed type.
  • It is now possible to implement new custom types with their own memory management, e.g: new string types, fast TValue implementations without hacks on RTL etc.
  • Management operators have no result type as opposed to normal operators.
  • A simple virtual method table is generated for the management oprators. Thanks to this is possible to work with management operators together with all RTL functions like InitializeArray and FinalizeArray.


Management Operators features can be used for many things:

  • Support for value types.
  • Nullable types.
  • Some custom ARC variations.
  • Speed up existing types.
  • Very fast RTTI.TValue implementation.
  • As replacement for manually called Init/Done record methods like in mORMot for many types (for example SynCommons.TSynLocker).
  • Auto init/finit for pointers/classes/simple types or whatever we have in Pascal.


It works correctly in all possible ways with RTL:

  • New (Initialize).
  • Dispose (Finalize).
  • Initialize (Initialize).
  • Finalize (Finalize).
  • InitializeArray (Initialize).
  • FinalizeArray (Finalize).
  • SetLength (Initialize/Finalize).
  • Copy (AddRef).
  • RTTI.IsManaged.


Managements operators are often called implicitly, for example:

  • Global variables (Initialize/Finalize).
  • Local variables (Initialize/Finalize).
  • For fields inside records, objects or classes (Initialize/Finalize).
  • Variable assignment (Copy).
  • For parameters for routines - AddRef/Finalize/none - this depends on modifiers like var/constref/const.


Initialize

The initialize operator is called after memory allocation for a record and called after the internal compiler call to recordrtti(data,typeinfo,@int_initialize); It allows automatic initialization for a record. A simple example is:

{$if FPC_FULLVERSION < 30101}{$ERROR this demo needs version 3.1.1}{$endif}
{$mode delphi}{$macro on}
program TestInitialize;

type
    PRec = ^TRec;
    TRec = record
      I : Integer;
      class operator initialize(var aRec:TRec);
    end;

    class operator TRec.initialize(var aRec:TRec);
    begin
      aRec :=Default(TRec); // initialize to default
    end;

    procedure printTRec(r : PRec);
    begin
        WriteLn('Initialized TRec field i: ', r^.I = 0);  // should always be zero, stack or heap
    end; 
var
 a,b:PRec; 
begin
    New(a);New(b); // standard "new" does not initialize, but now it does! 
    PrintTRec(a);
    PrintTRec(b);
    Dispose(a);Dispose(b);
end.


Finalize

Finalize is called when a record goes out of scope and called before the internal call to recordrtti(data,typeinfo,@int_finalize); It is useful for automatic custom finalization code. A simple example looks like:

{$if FPC_FULLVERSION < 30101}{$ERROR this demo needs version 3.1.1}{$endif}
{$mode delphi}{$macro on}
program testfinalize;

type
    PRec = ^TRec;
    TRec = record
      I : Integer;
      class operator finalize(var aRec:TRec);
    end;

    class operator TRec.finalize(var aRec:TRec);
    begin
      writeln('Just to let you know: I am finalizing..');
    end;
var
 a,b:PRec; 
begin
    New(a);New(b);
    Dispose(a);Dispose(b);
end.


AddRef

AddRef is called after the contents of a record has been duplicated by copying the contents byte by byte and is called *after* FPC internal call recordrtti(data,typeinfo,@int_addref); By itself it does not any lifetime management, but you can use it to implement it. See also Copy. example:

{$if FPC_FULLVERSION < 30101}{$ERROR this demo needs version 3.1.1}{$endif}
{$mode delphi}{$macro on}
program testaddref;
uses sysutils;
type
    PRec = ^TRec;
    TRec = record
      I : Integer;
      class operator AddRef(var aRec:TRec);
    end;

    class operator TRec.Addref(var aRec:TRec);
    begin
      writeln('Just to let you know: maybe you can do lifetime management here..');
    end;
var
 a,b:array of Trec; 
begin
    setlength(a,4);
    b:= copy(a);
end.

Copy

The Copy operator, if implemented, is called instead of the default copy behavior. This operator is responsible for copying everything that's needed from the source to the target.