[fpc-devel] Pascal Smart Pointers Idea + ARC implementation
Maciej Izak
hnb.code at gmail.com
Sat Oct 10 02:41:02 CEST 2015
I am working on smart pointers for FPC (something more than pure ARC for
classes).
This solution is full compatible with existing code base.
I would like to know what do you think about my conception and work.
First step is almost done -> new record operators:
=== begin code ===
{$MODESWITCH MANAGEMENTOPERATORS}
type // Record with automatic constructor and destructor
TValue = record
class operator Initialize(var aRec: TValue);
class operator Finalize(var aRec: TValue);
class operator Copy(constref aSource: TValue; var aDest: TValue);
end;
=== end code ===
MANAGEMENTOPERATORS are quite simple to implement. MANAGEMENTOPERATORS were
created initially for fast implementation of TValue in RTTI.pas, but they
inspired me for smart pointers.
The second step is some concept of low level structures for smart pointers
(in this conception we have two kinds of smart pointers - smart pointers
for objects and smart pointers for pointers):
=== code begin ===
PRawSmartPtr = ^TRawSmartPtr;
TRawSmartPtr = record
private
FInstance: Pointer;
FRefCount: PLongint;
function GetInstance: Pointer;
function GetRefCount: Longint;
public
property Instance: Pointer read GetInstance;
property RefCount: Longint read GetRefCount;
end;
PRawSmartObj = ^TRawSmartObj;
TRawSmartObj = type TRawSmartPtr;
function TRawSmartPtr.GetInstance: Pointer;
begin
if (FRefCount = nil) or (FRefCount^ <= 0) then
Exit(nil);
Result := FInstance;
end;
function TRawSmartPtr.GetRefCount: Longint
begin
if FRefCount = nil then
Exit(0);
Result := FRefCount^;
end;
=== code end ===
For smart pointers we need to implement in some way ".", standard inplicit
opertor, "^", "@" and "@@" operator. I thought about it for a long time.
IMO minimal invasive method is "default" keyword, used as below:
=== code begin ===
TSmartPtr<T> = record
// similar as overloading [] operators for property x[v: string]:
integer read gx write sx; default;
Instance: ^T; default; // default keyword for non property, can be used
only for field of pointer type.
RefCount: PLongint;
class operator Initialize(var aRec: TSmartPtr<T>);
class operator Finalize(var aRec: TSmartPtr<T>);
class operator Copy(constref aSource: TSmartPtr<T>; var aDest:
TSmartPtr<T>);
// implicit or explicit operator should be used before "default" field
operator Implicit(constref aValue: T); // special version of
Implicit/Explicit is also needed (available only when is used default for
field)
operator Explicit: TRawSmartPtr;
end;
class operator TSmartPtr<T>.Initialize(var aRec: TSmartPtr<T>);
begin
aRec.RefCount := nil;
end;
class operator TSmartPtr<T>.Finalize(var aRec: TSmartPtr<T>);
begin
if aRec.RefCount <> nil then
if InterLockedDecrement(aRec.RefCount^)=0 then
begin
Dispose(aRec.RefCount);
Dispose(aRec.Instance);
end;
end;
class operator TSmartPtr<T>.Copy(constref aSource: TSmartPtr<T>; var aDest:
TSmartPtr<T>);
begin
if aDest.RefCount <> nil then
Finalize(aDest);
if aSource.RefCount <> nil then
InterLockedIncrement(aSource.RefCount^);
aDest.RefCount := aSource.RefCount;
aDest.Instance := aSource.Instance;
end;
operator TSmartPtr<T>.Implicit(constref aValue: T);
begin
if aDest.RefCount <> nil then
Finalize(aDest);
New(RefCount);
RefCount^ := 0;
InterLockedIncrement(RefCount^);
Instance := @aValue;
end;
operator TSmartPtr<T>.Explicit: TRawSmartPtr;
begin
Result.RefCount := RefCount;
Result.Instance := Instance;
end;
=== code end ===
TSmartObj<T: TObject> is very similar to TSmartPtr<T>, the difference exist
inside Finalize - where is called Instance^.Free method instead of
Dispose(aRec.Instance).
few examples:
=== code begin === simple use case
var
myObj: TSmartObj<TObject>;
begin // <- call Initialize operator
myObj := TObject.Create; // <- call Implicit operator
end; // <- call Finalize operator. Dec(RefCount) and if RefCount = 0 then
call Instance^.Free
=== code end ===
=== code begin === smart pointer and exceptions
var
myObj: TSmartObj<TObject>;
begin // <- call Initialize operator
myObj := TObject.Create; // <- call Implicit operator
try // we don't need try-finally-end anymore because operator Finalize is
always called
WriteLn(myObj.ClassName); // will print TObject. Equivalent of
WriteLn(p.Instance^.ClassName);
raise Exception.Create('Error Message');
except
end;
end; // <- call Finalize operator. Dec(RefCount) and if RefCount = 0 then
call Instance^.Free
=== code end ===
=== code begin === access to TSmartObj/TSmartPtr
var
p: TSmartPtr<PInteger>;
begin // <- call Initialize operator
WriteLn(TRawSmartPtr(p).RefCount); // <-- call Explicit . Will print 0
p := New(PInteger); // <-- call Implicit
p^ := 10; // use "default" magic, equivalent of p.Instance^^ := 10
WriteLn(Assigned(p)); // will print true, equivalent of
WriteLn(Assigned(p.Instance^));
WriteLn(TRawSmartPtr(p).RefCount); // <-- call Explicit . Will print 1
end; // <- call Finalize operator. Dec(RefCount) and if RefCount = 0 then
call Dispose(Instance)
=== code end ===
and so one :D. This solution is perfect for any Generics.Collections,
fpc-stl, fgl library. We can use in simple way weak references and strong
references with auto free memory of objects/pointers.
next step is syntax sugar (oxygene compatibility):
var
SomeObj: unretained TSomeObject; // equivalent of SomeObj: TSomeObject;
SomeObj: weak TSomeObject; // equivalent of SomeObj:
TWeakObj<TSomeObject>; <- TWeakObj is modified version of TSmartObj (to
rethink)
SomeObj: strong TSomeObject; // equivalent of SomeObj:
TSmartObj<TSomeObject>;
next step is {$MODE DELPHINEXTGEN} - in this mode any object will be
declared as TSmartObj.
--
Best regards,
Maciej Izak
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-devel/attachments/20151010/12862631/attachment.html>
More information about the fpc-devel
mailing list