Recent

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

Bogen85

  • Hero Member
  • *****
  • Posts: 595
Re: Smart pointers revisited. Let me know what you think.
« Reply #60 on: September 25, 2022, 10:58:32 pm »
Well, I'll need to study your update (TAuto) more ASerge.

I like it how I can set the instance pointer directly with your example.

It works, but I have to do more checking to prevent Access violations compared to Thaddy's example (Auto)

Overall I like how Auto works, but I'd prefer to have a way to use procedure/property with it to set the instance pointer as well directly from a constructed object so that I don't need to have an additional pointer wrapper to allow for that.

I see that TAuto is using a record finalize member which I assume is a akin to a destructor, where as Auto is using a destructor in the nested TFreeTheValue to accomplish something similar.

I just need to learn advanced records and IInterface and TInterfacedObject ...
« Last Edit: September 26, 2022, 07:14:50 am by Bogen85 »

Bogen85

  • Hero Member
  • *****
  • Posts: 595
Re: Smart pointers revisited. Let me know what you think.
« Reply #61 on: October 05, 2022, 07:21:02 pm »
Well, I'll need to study your update (TAuto) more ASerge.

I like it how I can set the instance pointer directly with your example.

It works, but I have to do more checking to prevent Access violations compared to Thaddy's example (Auto)

I was using Thaddy's Auto for a while but I had to wrap classes with parameterized creates in a separate pointer holder.
That was working but had added overhead was a bit awkward.

After getting use to doing it that way I revisited ASerge's and was able to resolve all my access/initialization problems.

I understand ASerge's TAuto unit a lot more than Thaddy's Auto unit.

Also, it is simpler for me to use. I just had learn a few things using Auto before I revisited TAuto.

Thanks ASerge!  ;D
« Last Edit: October 05, 2022, 07:25:14 pm by Bogen85 »

SymbolicFrank

  • Hero Member
  • *****
  • Posts: 1313
Re: Smart pointers revisited. Let me know what you think.
« Reply #62 on: October 10, 2022, 03:01:20 pm »
Ok, let's make an overview. Uses objpash.inc.

We have classes, that have a virtual method table (record TVmt), static methods and class methods, like:

Code: Pascal  [Select][+][-]
  1. class function NewInstance: TObject; virtual;

If you call the constructor, NewInstance is called in the background.

The difference between class methods and other methods is, that the latter get an invisible first parameter, Self, that points to the instance. While class methods are regular functions. NewInstance is just a function that returns a TObject.

After the TObject, we see the definition of IUnknown. This basically extends your classes with two new functions: _AddRef and _Release, which are used for reference counting.

The basic class that implements both, is TInterfacedObject. The main difference with a TObject is, that it adds a property RefCount and overrides NewInstance and Destroy, to add to or substract from that reference count.

Why does it do that and how does that help us with the memory management?

How it actually works isn't explained in this source file. To understand that, we have to read between the lines and fill in the blanks that are hidden inside the compiler. Like. how it calls NewInstance automatically. Every time you assign an instance to a variable, the compiler calls _AddRef (if available). And every time you assign something else to that variable, or it goes out of scope, it calls _Release. See:

Code: Pascal  [Select][+][-]
  1. function TObject.GetInterface(const iid : tguid; out obj) : boolean;

(For the implementations, see 'function fpc_intf*' in compproc.inc. It's hard to show the code where this is used, because the compiler uses offsets into the VMT instead of function names to call the _AddRef and _Release methods. To see it happen, use the debugger.)

The result of all that is, that if there are no variables anymore that reference an instance, it is deleted.

Or, in other words: instead of inheriting from class(TObject), use class(TInterfacedObject) to create your own ones and call the inherited destructor as usual. Like this:

Code: Pascal  [Select][+][-]
  1. type
  2.   TMyClass = class(TInterfacedObject)
  3.     MyVar: string;
  4.     constructor Create(ThisVar: string); virtual;
  5.     destructor Destroy; override;
  6.   end;
  7.  
  8. constructor TMyClass.Create(ThisVar: string);
  9. begin
  10.   inherited Create;
  11.   MyVar := ThisVar;
  12. end;
  13.  
  14. destructor TMyClass.Destroy;
  15. begin
  16.   inherited Destroy;
  17. end;
  18.  
  19. var
  20.   m, n: TMyClass;
  21. begin
  22.   m := TMyClass.Create('Bla');
  23.   n := m;
  24.   m := TMyClass.Create('Zomg!');
  25.   // Do stuff
  26.   // Don't Free them!
  27. end;

That should be enough.

But, there's a lot of existing classes you didn't write yourself, where that doesn't work. What do you do?

Well, the easiest way is probably to inherit a new class, that adds and implements an IUnknown interface, like TInterfacedObject does. Or use a smart pointer. The most interesting one is probably this one as posted earlier, which should now work on trunk.

Bogen85

  • Hero Member
  • *****
  • Posts: 595
Re: Smart pointers revisited. Let me know what you think.
« Reply #63 on: October 10, 2022, 03:52:43 pm »
But, there's a lot of existing classes you didn't write yourself, where that doesn't work. What do you do?

Well, the easiest way is probably to inherit a new class, that adds and implements an IUnknown interface, like TInterfacedObject does. Or use a smart pointer. The most interesting one is probably this one as posted earlier, which should now work on trunk.

I'll need to look at that one. I'm using a very short class function (a constructor wrapper) I added to TAuto instantiate existing classes fairly seamlessly, and that has been working well.

I need to post my TAuto additions and slight modifications (does not change it's core functionality in any way, and TAuto only provided the core functionality anyways) but I'm not ready to do that quite yet.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Smart pointers revisited. Let me know what you think.
« Reply #64 on: October 11, 2022, 07:32:01 am »
The difference between class methods and other methods is, that the latter get an invisible first parameter, Self, that points to the instance. While class methods are regular functions.

Wrong. There are normal class methods and there are static class methods. The later are always marked with  a static modifier at the end. Those (and only those) behave like regular functions or procedures that just happen to be inside a class scope and can be assigned to normal function pointers (at least in main, I think 3.2.x didn't allow that yet).
Regular class methods however do also have that hidden Self parameter, but it won't contain the class instance, but the class type. Regular class methods can also be virtual which means that a sub class can override them. This makes them a very powerful feature (something I deeply miss here and then in C++) and is the reason why NewInstance can be reimplemented by sub classes to use some different allocation mechanism.

SymbolicFrank

  • Hero Member
  • *****
  • Posts: 1313
Re: Smart pointers revisited. Let me know what you think.
« Reply #65 on: October 11, 2022, 10:00:14 am »
Thanks for making that clear  :)

I don't really use that very much.

Ryan J

  • Jr. Member
  • **
  • Posts: 82
Re: Smart pointers revisited. Let me know what you think.
« Reply #66 on: October 16, 2022, 04:49:31 am »
The [box] syntax originated from the current lack of default properties other that array properties.

I just stumbled across this post and thought I'd share my feature branch at https://gitlab.com/genericptr/free-pascal/-/tree/default_record_property so maybe one day some progress can be made on this and we don't forget about it. Sven (@PascalDragon I think?) agreed that smart pointers are the best option for ARC in FPC and I think approved of a default property If I recall correctly so I made this branch which implements a default property for records with a class field.

If the compiler devs ever want to try to implement I'll come back to it but it's just parked here for now.

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Smart pointers revisited. Let me know what you think.
« Reply #67 on: October 16, 2022, 10:26:13 am »
That is already recorded.
Both AVK and me wrote a proper implementation. Just waiting for a water tight implementation for the default property, which was more or less also implemented in a fork by Maciej. (and as far as I know almost correct, but abandoned?)

Both mentioned implementations work from 3.2 onwards. It is just the sugar candy that does not work yet.

Look up the discussion on this forum.
« Last Edit: October 16, 2022, 10:30:10 am by Thaddy »
Specialize a type, not a var.

Ryan J

  • Jr. Member
  • **
  • Posts: 82
Re: Smart pointers revisited. Let me know what you think.
« Reply #68 on: October 17, 2022, 03:58:47 am »
That is already recorded.
Both AVK and me wrote a proper implementation. Just waiting for a water tight implementation for the default property, which was more or less also implemented in a fork by Maciej. (and as far as I know almost correct, but abandoned?)

Both mentioned implementations work from 3.2 onwards. It is just the sugar candy that does not work yet.

Look up the discussion on this forum.

I'm sorry, there is already a default property developed? Can you show me where it is so I can study it? I didn't get any guidance from Sven on this so I have no idea what would be allowed or not or if I implemented it correctly.

The one I did was pretty complete I think (including for..in loops) but it's scoped was constrained to just class fields since using other types affect far too much of the compiler and was likely to get rejected anyways.

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Smart pointers revisited. Let me know what you think.
« Reply #69 on: October 17, 2022, 11:14:52 am »
Maciej, a.k.a HNB on this forum did it. He is not good with the compiler people anymore.
(But he contributed many things to Lazarus and FPC: anchor docking resp rtl-generics and many fixes)
« Last Edit: October 17, 2022, 11:18:00 am by Thaddy »
Specialize a type, not a var.

Ryan J

  • Jr. Member
  • **
  • Posts: 82
Re: Smart pointers revisited. Let me know what you think.
« Reply #70 on: October 18, 2022, 08:38:52 am »
Maciej, a.k.a HNB on this forum did it. He is not good with the compiler people anymore.
(But he contributed many things to Lazarus and FPC: anchor docking resp rtl-generics and many fixes)

yes I spoke with him around the time he did the excellent record management operators and we discussed the default property then. At that time he hadn't implement it so I assumed it was left undone. Not sure where I can find his implementation anyways and since he's not going to contribute to FPC anymore it may be hard to get what he has working or finished.

Honestly even if the compiler devs accept this it will be years before it's reviewed and merged in. Personally I have an extended RTTI patch which is in the pipeline for review but it's been well over a year since I hard back from them on that so I expect many years for the default property unfortunately.

Bogen85

  • Hero Member
  • *****
  • Posts: 595
Re: Smart pointers revisited. Let me know what you think.
« Reply #71 on: February 04, 2023, 05:06:00 pm »
I started a continuation of this topic/thread here: https://forum.lazarus.freepascal.org/index.php/topic,62163.0.html
As this thread is over 3 months old, please continue any discussion on the above topic/thread.

circular

  • Hero Member
  • *****
  • Posts: 4195
    • Personal webpage
Re: Smart pointers revisited. Let me know what you think.
« Reply #72 on: February 04, 2023, 06:54:00 pm »
I find that often I just need my local variables to be freed, without reference counting. And ideally I would like not to add any more code than with arrays.

Here is a record to handle that for classes having a constructor without parameters:
Code: Pascal  [Select][+][-]
  1. unit Local;
  2.  
  3. {$modeswitch advancedRecords}
  4.  
  5. interface
  6.  
  7. type
  8.   { LocalClass }
  9.  
  10.   generic LocalClass<T: class> = record
  11.   private
  12.     type TSelf = specialize LocalClass<T>;
  13.     var FInstance: T;
  14.     class operator Initialize(var ASelf: TSelf); inline;
  15.     class operator Finalize(var ASelf: TSelf); inline;
  16.     function GetInstance: T; inline;
  17.     procedure SetInstance(const AInstance: T); inline;
  18.   public
  19.     function Give: T;
  20.     property _: T read GetInstance write SetInstance;
  21.   end;
  22.  
  23. implementation
  24.  
  25. class operator LocalClass.Initialize(var ASelf: TSelf);
  26. begin
  27.   ASelf.FInstance:= nil;
  28. end;
  29.  
  30. class operator LocalClass.Finalize(var ASelf: TSelf);
  31. begin
  32.   ASelf.FInstance.Free;
  33. end;
  34.  
  35. procedure LocalClass.SetInstance(const AInstance: T);
  36. begin
  37.   if Assigned(FInstance) then
  38.      FInstance.Free;
  39.   FInstance := AInstance;
  40. end;
  41.  
  42. function LocalClass.GetInstance: T;
  43. begin
  44.   if FInstance = nil then
  45.      FInstance := T.Create;
  46.   result := FInstance;
  47. end;
  48.  
  49. function LocalClass.Give: T;
  50. begin
  51.   result := FInstance;
  52.   FInstance := nil;
  53. end;
  54.  
  55. end.

It can be used like that:
Code: Pascal  [Select][+][-]
  1. uses Classes, Local;
  2.  
  3. type
  4.   StringList = specialize LocalClass<TStringList>;
  5.  
  6. var
  7.   list: StringList;
  8.   taken: TStringList;
  9.  
  10. begin
  11.   list._.Add('Hello');
  12.   list._.Add('Alice');
  13.   list._.Sort;
  14.   WriteLn(list._.CommaText);
  15.  
  16.   taken := list.Give;
  17.   // display 0 because list is reset
  18.   writeln(list._.Count);
  19.  
  20.   taken.Add('Bob');
  21.   // display Alice,Bob,Hello
  22.   WriteLn(taken.CommaText);
  23.   taken.Free; // need to free it
  24.  
  25.   list._.Add('Alice');
  26.   // clear the list
  27.   list._ := nil;
  28.   list._.Add('Mirror');
  29.   // display Mirror
  30.   WriteLn(list._.CommaText);
  31. end.
Conscience is the debugger of the mind

 

TinyPortal © 2005-2018