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
_______________________________________________ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel