[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