Difference between revisions of "management operators"

From Free Pascal wiki
Jump to navigationJump to search
Line 9: Line 9:
 
* 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.<br>
 
* 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.<br>
 
* Management operators have no result type as opposed to normal operators.<br>
 
* Management operators have no result type as opposed to normal operators.<br>
* 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.
+
* A simple virtual method table is generated for the management oprators. Thanks to this it is possible to combine management operators with all RTL functions, such as InitializeArray / FinalizeArray / e.t.c.
 
<br>
 
<br>
 
Management Operators features can be used for many things:
 
Management Operators features can be used for many things:

Revision as of 02:55, 2 November 2019

Management Operators

From Free Pascal version 3.1.1 onwards, there is a new language feature called management operators for advanced records.

The new operators are: Initialize, Finalize, AddRef and Copy.

These are a fairly unique feature, and are called "management operators" 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 it is possible to combine management operators with all RTL functions, such as InitializeArray / FinalizeArray / e.t.c.


Management Operators features can be used for many things:

  • More granularly controlling the lifetimes of simple value types / primitives
  • Implementing "nullable" value types
  • Custom ARC implementations
  • A very fast RTTI.TValue implementation
  • As a 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 anything else we have in Pascal.
  • Much more


It works correctly in all possible ways with the 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; 
 c:array of Trec;
begin
    New(a);New(b);
    Dispose(a);Dispose(b);
    writeln('Just before program termination this will also be finalized');
    Setlength(c,4);
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.
todo: add simple example! There is a (complex) example in /tests/test/tmoperator8.pas

A Simple example of use

Simple resource handler

 unit UResourseHandlers;  
 {$if FPC_FULLVERSION < 30101}{$ERROR this demo needs version 3.1.1}{$endif}  
 {$mode delphi} 
 {$modeSwitch advancedRecords}  
 interface  
 uses  
   Classes, SysUtils;  
 type  
     { TObjectHandler }  
  TObjectHandler = record  
   obj : TObject;  
   class operator initialize(var hdl:TObjectHandler);  
   class operator finalize(var hdl:TObjectHandler);  
  end;  
 implementation  
 { TObjectHandler }  
 class operator TObjectHandler.initialize(var hdl: TObjectHandler);  
 begin  
   hdl.obj := nil;  
 end;  
 class operator TObjectHandler.finalize(var hdl: TObjectHandler);  
 begin  
   FreeAndNil(hdl.obj);  
 end;  
 end.

How to use it

procedure ExtractionResultTests.ObjectHandlerTest;  
 var  
  a: TRow;  
  ah : TObjectHandler;  
 begin  
   a:= TRow.Create;  
   ah.obj:= a;  
 end;

In this case the Destructor of the TRow object is called when the handler goes out of scope, the same idea could be used for other resources like TMutex or TCriticalSeccions i guess.