I revisited my old smart pointer code - from this forum - since we have no default parameter for types other than arrays(yet).
I think I came up with a rather nice - my opinion - rewrite. Note I have also a version based on management operators, this is just the interfaced type.
It works only with classes with parameter-less constructors at the moment.
What's new:
- You don't have to use typecasts.
- Syntax for boxed is immediately obvious, more readable, therefor less prone to mix ups
- Hard casts still work too
- Assignments to a T(whateverclass) gives you a reference to the boxed instance
- You can Unbox! before lazy scope ends.
Here's an example with explanation:
program smartptrdemo;
{$mode delphi}{$ifdef mswindows}{$apptype console}{$endif}{$H+}
uses classes, smartptrs;
type
TAutoStringlist = Auto<Tstringlist>;
procedure TestLocal;
var
l:TAutoStringList;
begin
l[box].Add('Test me, local');
writeln(L[box].text);
end;
var
a,c:TAutoStringList;
b:Tstringlist;
begin
// Auto-creates a boxed stringlist if it does not exist yet.
// You can subsequently refer to the stringlist as a[box]
// This has the advantage that the syntax makes it clear
// that it is a boxed variable, automatically released.
a[box].add('test');
writeln('> ' ,a[box].text);
// You can also manually release the boxed stringlist if required
// This can be an advantage when you want to control the release time
a[unbox];
// This auto-creates a new stringlist that is empty
// because the previous is unboxed i.e. destroyed.
// The text property is initially empty, of course.
writeln('> ' ,a[box].text, '< should be just a space between > and <');
a[box].Add('test some more');
writeln('> ' ,a[box].text);
// test for local variables
TestLocal;
// Implicit:
// If you do not want to use the [box] syntax, declare a TStrings
// and assign the boxed value to it. No need to create.
// b is now a reference to c[box] and is the boxed stringlist.
c:=default(TAutoStringlist); // shut up compiler
b:=c;
// Explicit:
// You can hardcast Auto<T> to T
// (Again an alternative syntax)
TStringlist(c).Add('Test more');
// c is updated, so is b, b is a reference.
writeln(b.Text);
// all cleanup is automatic
end.
And here's the (upgraded, new) unit:
unit smartptrs;
{$ifdef fpc}{$mode delphi}{$endif}
interface
{$ifdef fpc}{$push}{$endif}{$interfaces com}
type
TBoxtype = (Box,UnBox);
Auto<T:class, constructor> = record
strict private
FValue:T;
FFreeTheValue:IInterface;
function GetValue:T;
function GetValueFrom(v:TBoxtype):T;
type
TFreeTheValue = class(TInterfacedObject)
private
fObjectToFree:TObject;
public
constructor Create(anObjectToFree: T);
destructor Destroy;override;
end;
public
constructor Create(AValue: T);overload;
procedure Create;overload;
class operator Implicit(var smart: Auto<T>):T;
class operator Explicit(var smart: Auto<T>):T;
property value: T read GetValue;
property ValueFrom[a:TBoxType]:T read GetValueFrom;default;
end;
{$ifdef fpc}{$pop 'delphi is on its own here'}{$endif}
implementation
constructor Auto<T>.TFreeTheValue.Create(anObjectToFree:T);
begin
self.fObjectToFree := anObjectToFree;
end;
destructor Auto<T>.TFreeTheValue.Destroy;
begin
fObjectToFree.Free;
inherited;
end;
constructor Auto<T>.Create(AValue:T);
begin
FValue := AValue;
FFreeTheValue := TFreeTheValue.Create(FValue);
end;
procedure Auto<T>.Create;
begin
Auto<T>.Create(T.Create);
end;
class operator Auto<T>.Implicit(var smart: Auto<T>):T;
begin
Result := Smart.Value;
end;
class operator Auto<T>.Explicit(var smart: Auto<T>):T;
begin
Result := Smart.Value;
end;
function Auto<T>.GetValueFrom(v:TBoxType):T;
begin
if v = Box then Result:=GetValue else begin FFreeTheValue := nil; end;
end;
function Auto<T>.GetValue:T;
begin
if not Assigned(FFreeTheValue) then
Self := Auto<T>.Create(T.Create);
Result := FValue;
end;
end.
That's very simple code isn't it?
P.S: This version is Delphi compatible, the version with management operators is not. (But has basically the same interface section and usage pattern)