Difference between revisions of "management operators"
BenGrasset (talk | contribs) |
BenGrasset (talk | contribs) |
||
Line 7: | Line 7: | ||
<br> | <br> | ||
* Each record, even non-managed or empty, with management operator becomes a managed type.<br> | * Each record, even non-managed or empty, with management operator becomes a managed type.<br> | ||
− | * | + | * They make it possible to implement new custom types with their own memory management, e.g: new string types, fast TValue implementations without hacks on the 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 | + | * A simple virtual method table is generated for the management operators. Thanks to this it is possible to combine management operators with all RTL functions, such as InitializeArray / FinalizeArray / etc. |
<br> | <br> | ||
− | Management Operators | + | Management Operators can be used for many things: |
* More granularly controlling the lifetimes of simple value types / primitives | * More granularly controlling the lifetimes of simple value types / primitives | ||
* Implementing "nullable" value types | * Implementing "nullable" value types | ||
Line 20: | Line 20: | ||
* Much more | * Much more | ||
<br> | <br> | ||
− | + | They work correctly in all possible ways with the RTL: | |
* New (Initialize). | * New (Initialize). | ||
* Dispose (Finalize). | * Dispose (Finalize). | ||
Line 63: | Line 63: | ||
begin | begin | ||
WriteLn('Initialized TRec field i: ', r^.I = 0); // should always be zero, stack or heap | WriteLn('Initialized TRec field i: ', r^.I = 0); // should always be zero, stack or heap | ||
− | end; | + | end; |
+ | |||
var | var | ||
− | a,b:PRec; | + | a,b:PRec; |
+ | |||
begin | begin | ||
New(a);New(b); // standard "new" does not initialize, but now it does! | New(a);New(b); // standard "new" does not initialize, but now it does! | ||
Line 94: | Line 96: | ||
writeln('Just to let you know: I am finalizing..'); | writeln('Just to let you know: I am finalizing..'); | ||
end; | end; | ||
+ | |||
var | var | ||
a,b:PRec; | a,b:PRec; | ||
c:array of Trec; | c:array of Trec; | ||
+ | |||
begin | begin | ||
New(a);New(b); | New(a);New(b); | ||
Line 114: | Line 118: | ||
{$mode delphi}{$macro on} | {$mode delphi}{$macro on} | ||
program testaddref; | program testaddref; | ||
+ | |||
uses sysutils; | uses sysutils; | ||
+ | |||
type | type | ||
− | + | PRec = ^TRec; | |
− | + | ||
− | + | TRec = record | |
− | + | I : Integer; | |
− | end; | + | 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 | var | ||
− | + | a,b:array of Trec; | |
+ | |||
begin | begin | ||
− | + | setlength(a, 4); | |
− | + | b := copy(a); | |
end.</syntaxhighlight> | end.</syntaxhighlight> | ||
Line 143: | Line 152: | ||
<syntaxhighlight> | <syntaxhighlight> | ||
− | unit | + | unit UResourceHandlers; |
+ | |||
{$if FPC_FULLVERSION < 30101}{$ERROR this demo needs version 3.1.1}{$endif} | {$if FPC_FULLVERSION < 30101}{$ERROR this demo needs version 3.1.1}{$endif} | ||
{$mode delphi} | {$mode delphi} | ||
− | {$ | + | {$modeswitch advancedrecords} |
+ | |||
interface | interface | ||
+ | |||
uses | uses | ||
Classes, SysUtils; | Classes, SysUtils; | ||
+ | |||
type | type | ||
− | + | { TObjectHandler } | |
+ | |||
TObjectHandler = record | TObjectHandler = record | ||
− | + | obj : TObject; | |
− | + | class operator initialize(var hdl: TObjectHandler); | |
− | + | class operator finalize(var hdl: TObjectHandler); | |
end; | end; | ||
+ | |||
implementation | implementation | ||
+ | |||
{ TObjectHandler } | { TObjectHandler } | ||
+ | |||
class operator TObjectHandler.initialize(var hdl: TObjectHandler); | class operator TObjectHandler.initialize(var hdl: TObjectHandler); | ||
begin | begin | ||
hdl.obj := nil; | hdl.obj := nil; | ||
end; | end; | ||
+ | |||
class operator TObjectHandler.finalize(var hdl: TObjectHandler); | class operator TObjectHandler.finalize(var hdl: TObjectHandler); | ||
begin | begin | ||
FreeAndNil(hdl.obj); | FreeAndNil(hdl.obj); | ||
end; | end; | ||
− | + | ||
+ | end. | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Revision as of 03:03, 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.
- They make it possible to implement new custom types with their own memory management, e.g: new string types, fast TValue implementations without hacks on the RTL etc.
- Management operators have no result type as opposed to normal operators.
- A simple virtual method table is generated for the management operators. Thanks to this it is possible to combine management operators with all RTL functions, such as InitializeArray / FinalizeArray / etc.
Management Operators 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
They work 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 UResourceHandlers;
{$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.