Forum > General

Smart pointers revisited. Let me know what you think.

(1/12) > >>

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:
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---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:
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---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)

Nice code. However that boxed/unboxed code looks a little odd. Not that I couldn't adapt, but I do not find it that intuitive and natural as I would like it to be. Maybe it's just me and I do not see it properly, but I find this smart pointers example code more self explaining:

I am not an expert so this remark is not based on a topic knowledge, but more on a personal feeling when looking at smart pointers example code.

I prefer thaddy one, since it creates the object automatically.

But a non verbose option is somewhat we want too  ;) Because already defined in the type is the behaviour it will have "TAutoStringList" it says it all for me.

I can imagine an FPC automatic memory like this everywhere, it has bug consequences you can think of it?

Nice, although it looks a bit heavier compared to managed records. It seems that this implementation does not require any additional actions when copying (I believe the implementation with managed records does)? And it seems that the modifier "default" would improve the usefulness of this primitive.

@avra That implementation is not currently possible (anonymous methods are used). The [box] syntax originated from the current lack of default properties other that array properties.
But it has actually three advantages
- It is immediately obvious you are dealing with a smart class
- It has a very clear [unbox] option, something the code you refer to lacks.
- an easy auto-create, single step. The other code demands a separate step. Both create and destroy are implicit!
@lainz Currently it only works for classes with a parameterless constructor. But indeed, a type specialization is enough.
@avk I will add the alternative implementation with advanced records later today. But that one is not Delphi compatible and that was important to me.
And yes, the default modifier would be great. That's how I came up with the boxunbox syntax (with hindsight, I actually like that...) to work around it.
Most importantly is the single variable instantiation, although assignment is also possible (b:=c in the example)

Thank you for all the remarks.


[0] Message Index

[#] Next page

Go to full version