Recent

Author Topic: Smart pointers revisited. Let me know what you think.  (Read 1826 times)

Thaddy

  • Hero Member
  • *****
  • Posts: 9298
Re: Smart pointers revisited. Let me know what you think.
« Reply #15 on: August 09, 2019, 11:37:59 am »
Thaddy, I propose a version where the structure only takes the size of a pointer. Instead of the default property and additional "Value" field, I suggest only one "Self" field.
That is possible, but relies on the record layout. First field being the same as the pointer to record itself. IMHO that is implementation detail.

Frankly I can't improve much without compiler support. ( and keep clean code)

I will consider the suggestion, though. Thx.
« Last Edit: August 09, 2019, 11:56:21 am by Thaddy »
also related to equus asinus.

Thaddy

  • Hero Member
  • *****
  • Posts: 9298
Re: Smart pointers revisited. Let me know what you think.
« Reply #16 on: August 10, 2019, 11:51:28 am »
I have edited the unit to use T instead of TObject, as previously remarked. (Was an oversight)
Still playing with ASerge's idea.
also related to equus asinus.

zamronypj

  • New Member
  • *
  • Posts: 43
    • Fano Framework, Free Pascal web application framework
Re: Smart pointers revisited. Let me know what you think.
« Reply #17 on: August 10, 2019, 12:57:03 pm »
@Thaddy

I am trying to grasp the idea. 

Code: Pascal  [Select]
  1. TAutoStringList = TAuto<TStringList>;

CMIIW, code above works because constructor TStringList.create() does not have any parameters. If constructor has parameters, then how do you pass that required parameters to constructor?
« Last Edit: August 10, 2019, 01:02:25 pm by zamronypj »
Fano Framework, Free Pascal web application framework https://fanoframework.github.io
Personal Projects https://v3.juhara.com
Github https://github.com/zamronypj

Thaddy

  • Hero Member
  • *****
  • Posts: 9298
Re: Smart pointers revisited. Let me know what you think.
« Reply #18 on: August 10, 2019, 01:38:26 pm »
As it stands (and I documented in the introduction) it is only for classes with parameter-less constructors.
However, I am working on an RTTI based version. That isn't lightweight, though. This one is.

Also note this is NOT a stop-gap for all memory management, never has been the intention.
« Last Edit: August 10, 2019, 01:43:56 pm by Thaddy »
also related to equus asinus.

ASerge

  • Hero Member
  • *****
  • Posts: 1423
Re: Smart pointers revisited. Let me know what you think.
« Reply #19 on: August 10, 2019, 06:53:31 pm »
If constructor has parameters, then how do you pass that required parameters to constructor?
Code: Pascal  [Select]
  1. Auto<T:class, constructor> = record
means: T is a class that defines a default constructor (a public parameterless constructor)

avk

  • Full Member
  • ***
  • Posts: 169
    • my self-education project
Re: Smart pointers revisited. Let me know what you think.
« Reply #20 on: August 11, 2019, 07:33:30 am »
@Thaddy, I didn’t wait for your implementation with managed records and therefore decided to make my own:
Code: Pascal  [Select]
  1. unit shref;
  2.  
  3. {$mode delphi}
  4.  
  5. interface
  6.  
  7. uses
  8.   SysUtils;
  9.  
  10. type
  11.  
  12.   TGSharedRef<T: class, constructor> = record
  13.   private
  14.     FInstance: T;
  15.     FRefCount: PInteger;
  16.     procedure InitInstance(aValue: T);
  17.     procedure Release;
  18.     function  GetInstance: T;
  19.     procedure SetInstance(aValue: T);
  20.     class operator Initialize(var s: TGSharedRef<T>); inline;
  21.     class operator Finalize(var s: TGSharedRef<T>);
  22.     class operator Copy(constref aSrc: TGSharedRef<T>; var aDst: TGSharedRef<T>); inline;
  23.   public
  24.   type
  25.     TInstance = T;
  26.     class operator Implicit(var s: TGSharedRef<T>): T; inline;
  27.     class operator Explicit(var s: TGSharedRef<T>): T; inline;
  28.     property Instance: T read GetInstance write SetInstance;
  29.   end;
  30.  
  31. implementation
  32.  
  33. procedure TGSharedRef<T>.InitInstance(aValue: T);
  34. begin
  35.   FInstance := aValue;
  36.   if aValue <> nil then
  37.     begin
  38.       New(FRefCount);
  39.       FRefCount^ := 1;
  40.     end;
  41. end;
  42.  
  43. procedure TGSharedRef<T>.Release;
  44. begin
  45.   if FRefCount <> nil then
  46.     begin
  47.       if InterlockedDecrement(FRefCount^) = 0 then
  48.         begin
  49.           Dispose(FRefCount);
  50.           FInstance.Free;
  51.         end;
  52.       FRefCount := nil;
  53.     end;
  54. end;
  55.  
  56. function TGSharedRef<T>.GetInstance: T;
  57. begin
  58.   if FRefCount = nil then
  59.     InitInstance(T.Create);
  60.   Result := FInstance;
  61. end;
  62.  
  63. procedure TGSharedRef<T>.SetInstance(aValue: T);
  64. begin
  65.   if aValue <> FInstance then
  66.     begin
  67.       Release;
  68.       InitInstance(aValue);
  69.     end;
  70. end;
  71.  
  72. class operator TGSharedRef<T>.Initialize(var s: TGSharedRef<T>);
  73. begin
  74.   s.FRefCount := nil;
  75. end;
  76.  
  77. class operator TGSharedRef<T>.Finalize(var s: TGSharedRef<T>);
  78. begin
  79.   s.Release;
  80. end;
  81.  
  82. class operator TGSharedRef<T>.Copy(constref aSrc: TGSharedRef<T>; var aDst: TGSharedRef<T>);
  83. begin
  84.   aDst.Release;
  85.   if aSrc.FRefCount <> nil then
  86.     InterLockedIncrement(aSrc.FRefCount^);
  87.   aDst.FInstance := aSrc.Instance;
  88.   aDst.FRefCount := aSrc.FRefCount;
  89. end;
  90.  
  91. class operator TGSharedRef<T>.Implicit(var s: TGSharedRef<T>): T;
  92. begin
  93.   Result := s.Instance;
  94. end;
  95.  
  96. class operator TGSharedRef<T>.Explicit(var s: TGSharedRef<T>): T;
  97. begin
  98.   Result := s.Instance;
  99. end;
  100.  
  101. end.
  102.  

usage:
Code: Pascal  [Select]
  1. program sharedref_test;
  2.  
  3. {$mode delphi}
  4.  
  5. uses
  6.   SysUtils, shref;
  7.  
  8. type
  9.  
  10.   TTest = class
  11.   private
  12.     FValue: Integer;
  13.   public
  14.     constructor Create; overload;
  15.     constructor Create(aValue: Integer); overload;
  16.     property Value: Integer read FValue write FValue;
  17.   end;
  18.  
  19. constructor TTest.Create;
  20. begin
  21.   FValue := -1;
  22. end;
  23.  
  24. constructor TTest.Create(aValue: Integer);
  25. begin
  26.   FValue := aValue;
  27. end;
  28.  
  29. var
  30.   GlobRef: TGSharedRef<TTest>;
  31.  
  32. procedure Test;
  33. var
  34.   Ref: TGSharedRef<TTest>;
  35. begin
  36.   WriteLn('begin local proc');
  37.   WriteLn('TTest(Ref).Value = ', TTest(Ref).Value);
  38.   TTest(Ref).Value := 101;
  39.   WriteLn('TTest(Ref).Value = ', TTest(Ref).Value);
  40.   GlobRef := Ref;  // TTest(GlobRef).Value = 101
  41.   Ref.Instance := TTest(nil); //early release
  42.   WriteLn('TTest(Ref).Value = ', TTest(Ref).Value);
  43.   Ref.Instance := TTest.Create(0); //parametized constructor
  44.   WriteLn('TTest(Ref).Value = ', TTest(Ref).Value);
  45.   WriteLn('end local proc');
  46. end;
  47.  
  48. begin
  49.   Test;
  50.   WriteLn('TTest(GlobRef).Value = ', TTest(GlobRef).Value);
  51.   ReadLn;
  52. end.
  53.  
It seems it works?

Thaddy

  • Hero Member
  • *****
  • Posts: 9298
Re: Smart pointers revisited. Let me know what you think.
« Reply #21 on: August 11, 2019, 08:52:48 am »
Yes. Very similar to mine based on Maciej's earlier scetch.in approach.
And just like mine it suffers from ugly hard casting (until we have a default property, that is ).

This is a good solution for FreePascal, can't be used on Delphi, though,
It generattes also slightly less code, it seems.

Nice!
also related to equus asinus.

avk

  • Full Member
  • ***
  • Posts: 169
    • my self-education project
Re: Smart pointers revisited. Let me know what you think.
« Reply #22 on: August 12, 2019, 12:35:56 pm »
Ok, then maybe it makes sense to add it to LGenerics.

Thaddy

  • Hero Member
  • *****
  • Posts: 9298
Re: Smart pointers revisited. Let me know what you think.
« Reply #23 on: August 12, 2019, 12:51:43 pm »
Fine with me.

One note: You can not really use the parameterized constructors on things like TFilestream: that does not work. Although for your TTest it works.
I think for that we need to wait for more complete RTTI if at all possible.
also related to equus asinus.

avk

  • Full Member
  • ***
  • Posts: 169
    • my self-education project
Re: Smart pointers revisited. Let me know what you think.
« Reply #24 on: August 12, 2019, 01:02:20 pm »
Of course, if the class does not has a parameterless constructor, then this simply will not compile.
« Last Edit: August 12, 2019, 02:23:20 pm by avk »

Thaddy

  • Hero Member
  • *****
  • Posts: 9298
Re: Smart pointers revisited. Let me know what you think.
« Reply #25 on: August 12, 2019, 01:21:24 pm »
Of course, if the class does not have a parameterless constructor, then this simply will not compile.
Yes, but it has: TObject has..
also related to equus asinus.

Zoran

  • Hero Member
  • *****
  • Posts: 1469
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: Smart pointers revisited. Let me know what you think.
« Reply #26 on: August 12, 2019, 05:42:16 pm »
Of course, if the class does not have a parameterless constructor, then this simply will not compile.
Yes, but it has: TObject has..

Or not: https://forum.lazarus.freepascal.org/index.php/topic,45366.msg327886.html#msg327886
 ;)

Thaddy

  • Hero Member
  • *****
  • Posts: 9298
Re: Smart pointers revisited. Let me know what you think.
« Reply #27 on: August 12, 2019, 05:45:05 pm »
Yes. But that technique is not applied.
also related to equus asinus.

ASerge

  • Hero Member
  • *****
  • Posts: 1423
Re: Smart pointers revisited. Let me know what you think.
« Reply #28 on: August 12, 2019, 06:49:43 pm »
One note: You can not really use the parameterized constructors on things like TFilestream
Option when the default constructor is not called. For diversity - FPC mode, and as and before, only the size of the pointer.
Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$MODE OBJFPC}
  4. {$MODESWITCH ADVANCEDRECORDS}
  5.  
  6. interface
  7.  
  8. type
  9.   generic TAuto<T:class> = record
  10.   strict private type
  11.     TRef = record Item: T; RefCount: LongInt; end;
  12.     PRef = ^TRef;
  13.   strict private
  14.     FRef: PRef;
  15.     class operator Copy(constref Src: TAuto; var Dst: TAuto);
  16.     class operator Finalize(var Self: TAuto); inline;
  17.     class operator Initialize(var Self: TAuto); inline;
  18.     function GetInstance: T; inline;
  19.     procedure Release;
  20.     procedure SetInstance(const Value: T);
  21.   public
  22.     class operator Explicit(constref From: TAuto): T; inline;
  23.     property Instance: T read GetInstance write SetInstance;
  24.   end;
  25.  
  26. implementation
  27.  
  28. class operator TAuto.Copy(constref Src: TAuto; var Dst: TAuto);
  29. begin
  30.   Dst.Release;
  31.   if Assigned(Src.FRef) then
  32.     InterlockedIncrement(Src.FRef^.RefCount);
  33.   Dst.FRef := Src.FRef;
  34. end;
  35.  
  36. class operator TAuto.Explicit(constref From: TAuto): T;
  37. begin
  38.   Result := From.Instance;
  39. end;
  40.  
  41. class operator TAuto.Finalize(var Self: TAuto);
  42. begin
  43.   Self.Release;
  44. end;
  45.  
  46. class operator TAuto.Initialize(var Self: TAuto);
  47. begin
  48.   Self.FRef := nil;
  49. end;
  50.  
  51. function TAuto.GetInstance: T;
  52. begin
  53.   Result := nil;
  54.   if Assigned(FRef) then
  55.     Result := FRef^.Item;
  56. end;
  57.  
  58. procedure TAuto.Release;
  59. begin
  60.   if Assigned(FRef) then
  61.   begin
  62.     if InterlockedDecrement(FRef^.RefCount) = 0 then
  63.     begin
  64.       FRef^.Item.Free;
  65.       Dispose(FRef);
  66.     end;
  67.     FRef := nil;
  68.   end;
  69. end;
  70.  
  71. procedure TAuto.SetInstance(const Value: T);
  72. begin
  73.   if Assigned(FRef) then
  74.   begin
  75.     if Value = FRef^.Item then
  76.       Exit;
  77.     Release;
  78.   end;
  79.   if Assigned(Value) then
  80.   begin
  81.     New(FRef);
  82.     FRef^.Item := Value;
  83.     FRef^.RefCount := 1;
  84.   end
  85. end;
  86.  
  87. end.
Code: Pascal  [Select]
  1. {$MODE OBJFPC}
  2. {$APPTYPE CONSOLE}
  3.  
  4. uses unit1;
  5.  
  6. type
  7.   TTest = class
  8.   private
  9.     FValue: Integer;
  10.   public
  11.     constructor Create(AValue: Integer); overload;
  12.     property Value: Integer read FValue write FValue;
  13.   end;
  14.  
  15. constructor TTest.Create(AValue: Integer);
  16. begin
  17.   FValue := AValue;
  18. end;
  19.  
  20. type
  21.   TAutoTest = specialize TAuto<TTest>;
  22. var
  23.   GlobRef: TAutoTest;
  24.  
  25. procedure Test;
  26. var
  27.   Ref: TAutoTest;
  28. begin
  29.   WriteLn('begin local proc');
  30.   Ref.Instance := TTest.Create(-1);
  31.   WriteLn('TTest(Ref).Value = ', TTest(Ref).Value);
  32.   TTest(Ref).Value := 101;
  33.   WriteLn('TTest(Ref).Value = ', TTest(Ref).Value);
  34.   GlobRef := Ref;  // TTest(GlobRef).Value = 101
  35.   Ref.Instance := nil; //early release
  36.   Ref.Instance := TTest.Create(0);
  37.   WriteLn('TTest(Ref).Value = ', TTest(Ref).Value);
  38.   WriteLn('end local proc');
  39. end;
  40.  
  41. begin
  42.   Test;
  43.   WriteLn('TTest(GlobRef).Value = ', TTest(GlobRef).Value);
  44.   ReadLn;
  45. end.

PascalDragon

  • Hero Member
  • *****
  • Posts: 740
  • Compiler Developer
Re: Smart pointers revisited. Let me know what you think.
« Reply #29 on: August 13, 2019, 09:19:12 am »
Of course, if the class does not have a parameterless constructor, then this simply will not compile.
Yes, but it has: TObject has..

Or not: https://forum.lazarus.freepascal.org/index.php/topic,45366.msg327886.html#msg327886
 ;)
I have not yet found a Delphi example that would trigger the constructor-constraint of a generic, thus FPC parses, but ignores that constraint (my assumption is that this is from Delphi.NET times).