Recent

Author Topic: [RESOLVED] Smart Pointers, again revisited...  (Read 841 times)

Bogen85

  • Hero Member
  • *****
  • Posts: 572
[RESOLVED] Smart Pointers, again revisited...
« on: February 04, 2023, 05:03:56 pm »
See earlier discussion here: https://forum.lazarus.freepascal.org/index.php/topic,46306.0.html

I have adapted Aserge's implementation for my own use, but it is not really in a form for others to consume.

However, for my own usage is has been working very well. I instantiate classes using the mechanism wherever I need, pass around those instances as needed, and I don't have to working around freeing them, as that is handled automatically. The destructors run at the expected times, never too early, never to late.

I was wondering if there is any thought or planning being considered to offer generalized smart pointer implementations in the FCL in some upcoming release of Free Pascal.

Right now I'm asking because of this thread: https://forum.lazarus.freepascal.org/index.php/topic,62148.0.html where OP zelda16bit asked for an example of smart pointer use in Free Pascal.
« Last Edit: February 07, 2023, 06:54:04 pm by Bogen85 »

Bogen85

  • Hero Member
  • *****
  • Posts: 572
Re: Smart Pointers, again revisited...
« Reply #1 on: February 04, 2023, 07:33:24 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.

Agreed (and I replied here rather than the 3+ month old topic).

There are a lot of things I don't use smart pointers for and instead use existing managed types like dynamic arrays that already do automatic cleanup.
Or advanced records rather than classes (which is what smart pointers leverage)

Also the defer mechanism I came up with that also uses advanced records.
https://forum.lazarus.freepascal.org/index.php/topic,55154.0.html
https://github.com/bogen85/fpc-defer

Thaddy

  • Hero Member
  • *****
  • Posts: 12898
Re: Smart Pointers, again revisited...
« Reply #2 on: February 04, 2023, 08:02:53 pm »
You did not come up with that. I did. Three-four years ago. Also Marco Cantu and Barry Kelly.
But the principle is about the same. Just do not claim it.

The proof is on this forum btw. With multiple ( I forgot AVK and HNB, sorry) implementations that are all correct.
So revisited is really a well chosen wording...
« Last Edit: February 04, 2023, 08:23:03 pm by Thaddy »
Who is responsable for that!! The caller or the callee.. Out! ya'll, NOW. In UTC time, please, so maybe Yesterday or tomorrow.

Bogen85

  • Hero Member
  • *****
  • Posts: 572
Re: Smart Pointers, again revisited...
« Reply #3 on: February 04, 2023, 08:14:06 pm »
You did not come up with that. I did. Three-four years ago. Also Marco Cantu and Barry Kelly.
But the principle is about the same. Just do not claim it.

The proof is on this forum btw. With multiple ( I forgot AVK and HNB, sorry) implementations that are all correct.
So revisisted is really a well chosen wording...

I'm not trying to claim anything... I apologize if anything I said implied that...
I tried to make it clear that what I'm using is based on yours and others work on that...
I apologize if I did not make that clear...

In no way am I claiming anything....

See earlier discussion here: https://forum.lazarus.freepascal.org/index.php/topic,46306.0.html

I have adapted Aserge's implementation for my own use, but it is not really in a form for others to consume.

However, for my own usage is has been working very well. I instantiate classes using the mechanism wherever I need, pass around those instances as needed, and I don't have to working around freeing them, as that is handled automatically. The destructors run at the expected times, never too early, never to late.

I was wondering if there is any thought or planning being considered to offer generalized smart pointer implementations in the FCL in some upcoming release of Free Pascal.

Right now I'm asking because of this thread: https://forum.lazarus.freepascal.org/index.php/topic,62148.0.html where OP zelda16bit asked for an example of smart pointer use in Free Pascal.


I was asked for examples (and my usage is not in a way others can consumed), so I pointed to your discussion on this topic. I never shared how I specifically use them.

I apologize if anything I said there implied I came up with this (and that any of it would have implied that I'm claiming it...)
« Last Edit: February 04, 2023, 08:18:12 pm by Bogen85 »

Thaddy

  • Hero Member
  • *****
  • Posts: 12898
Re: Smart Pointers, again revisited...
« Reply #4 on: February 04, 2023, 08:19:25 pm »
No big deal. Actually sorry I got a bit angry. Wrong Fish on the plate...
Who is responsable for that!! The caller or the callee.. Out! ya'll, NOW. In UTC time, please, so maybe Yesterday or tomorrow.

Bogen85

  • Hero Member
  • *****
  • Posts: 572
Re: Smart Pointers, again revisited...
« Reply #5 on: February 04, 2023, 08:21:43 pm »
Also the defer mechanism I came up with that also uses advanced records.
https://forum.lazarus.freepascal.org/index.php/topic,55154.0.html
https://github.com/bogen85/fpc-defer

Which the idea was based on yours and other's examples.
Once again, I apologize.
It was not my intent to claim it as my own.
I apologize for it coming across that way. I will try to be more careful in the future, so as to not make this mistake again.
« Last Edit: February 04, 2023, 08:28:50 pm by Bogen85 »

Thaddy

  • Hero Member
  • *****
  • Posts: 12898
Re: Smart Pointers, again revisited...
« Reply #6 on: February 04, 2023, 08:24:59 pm »
Still strungling with a fish bone... ;)
Who is responsable for that!! The caller or the callee.. Out! ya'll, NOW. In UTC time, please, so maybe Yesterday or tomorrow.

Bogen85

  • Hero Member
  • *****
  • Posts: 572
Re: Smart Pointers, again revisited...
« Reply #7 on: February 04, 2023, 08:26:43 pm »
No big deal. Actually sorry I got a bit angry. Wrong Fish on the plate...

That is ok, I understand.

Thaddy, if it were not for you I would not be active in Pascal again.
There were many things holding me up from using Pascal instead of all the other language choices I have.
Summer of 2022 scouring through the forums for answers, your replies to others (spanning close to 10 years) covered everything I had concerns about, and gave me how to do things in Pascal in a satisfactory way that I thought was not possible compared to my other programming language choices.


zelda16bit

  • Full Member
  • ***
  • Posts: 115
Re: Smart Pointers, again revisited...
« Reply #8 on: February 04, 2023, 08:35:24 pm »
I asked for an example.
Since this topic has been created, I take advantage and ask about a problem I have with the release of objects.

You can create a class that handles a list of objects and add itself to that list and free it in that same class, so that later the classes that inherit from it do not have to be freed since the list of the parent class is freeing it. .

To understand more, I show where I commented on this and nobody gave me a solution.

https://forum.lazarus.freepascal.org/index.php/topic,61820.0.html

Warfley

  • Hero Member
  • *****
  • Posts: 1071
Re: Smart Pointers, again revisited...
« Reply #9 on: February 04, 2023, 10:40:08 pm »
You can create a class that handles a list of objects and add itself to that list and free it in that same class, so that later the classes that inherit from it do not have to be freed since the list of the parent class is freeing it. .

This is neither possible in FPC as reference counting requires interfaces or managed records and cannot be build on classes alone, more is this even very useful. Such an inheritance based model would mean that this type of class would always be reference counted. What if you don't want that? Another problem is that this only works with newly created classes that do not need to inherit from anything else, it would for example not work for TStringList which already inherits from TStrings. A generic composite record like discussed here allows to make any class reference counted and also at all points individually rather than having only some classes which are always reference counted while others can't be

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:

Agreed (and I replied here rather than the 3+ month old topic).

There are a lot of things I don't use smart pointers for and instead use existing managed types like dynamic arrays that already do automatic cleanup.
Or advanced records rather than classes (which is what smart pointers leverage)

Also the defer mechanism I came up with that also uses advanced records.
https://forum.lazarus.freepascal.org/index.php/topic,55154.0.html
https://github.com/bogen85/fpc-defer

This is basically the dynamic C++ introduced with C++11 with their shared vs unique pointer. Shared pointer have reference counter to be used from multiple locations while unique pointer have always the lifetime of the owner (unique pointer as class member gets automatically destroyed when the class gets destroyed, as variable when the variable goes out of scope)

What's also quite useful would be scope managed stack objects, e.g. a file stream that automatically gets closed when the variable goes out of scope
Code: Pascal  [Select][+][-]
  1. var FS: TManagedFileStream;
  2. begin
  3.   FS.open(filename);
  4.   FS.write(...);
  5. end; // end of function automatically closes file
Or like we have arrays and strings, also other data structures like maps or sets that don't require create/destroy.

A few years ago I've build all of that using managed records, lists, sets, dictionaries, file streams and much more. But then I found out about a bug in FPC which can cause double finalize for managed records, which basically made all of this work unusable
The record types that did not rely on finalize I put in a separate library, but all of the cool stuff sadly never worked

Probably the best solution for this would be to build it using interfaces instead. The problem here is that generic interfaces unlike records do not provide operator overloading, which has a bit less potential for some solutions
« Last Edit: February 04, 2023, 10:44:53 pm by Warfley »

circular

  • Hero Member
  • *****
  • Posts: 3961
    • Personal webpage
Re: Smart Pointers, again revisited...
« Reply #10 on: February 04, 2023, 11:49:37 pm »
Which the idea was based on yours and other's examples.
Once again, I apologize.
It was not my intent to claim it as my own.
I apologize for it coming across that way. I will try to be more careful in the future, so as to not make this mistake again.
It did not seem to me that you were claiming that.

The subject is complex and that it is better not to take things personally. To the contrary we can value each person contributing as it improves our understanding.

This is basically the dynamic C++ introduced with C++11 with their shared vs unique pointer. Shared pointer have reference counter to be used from multiple locations while unique pointer have always the lifetime of the owner (unique pointer as class member gets automatically destroyed when the class gets destroyed, as variable when the variable goes out of scope)
Indeed, that's not new conceptually. I suppose that generics at last allow to do that in Pascal and that's good news.  :)

Quote
What's also quite useful would be scope managed stack objects, e.g. a file stream that automatically gets closed when the variable goes out of scope
Indeed. I am not sure though that there would be a generic way to do that, unless there is a common name for a method on a record/object to call in order to dispose of it.
Conscience is the debugger of the mind

Warfley

  • Hero Member
  • *****
  • Posts: 1071
Re: Smart Pointers, again revisited...
« Reply #11 on: February 05, 2023, 10:56:32 am »
Indeed. I am not sure though that there would be a generic way to do that, unless there is a common name for a method on a record/object to call in order to dispose of it.
Back then I built this using generic finalizers, so for handling classes you would use the class finalizers which would call free on that class. For objects there was the object finalizers which calls final and for all other types there was the finalize finalizers that would call finalize.
Of course if the user required something more special, they could easily create their own finalizers

If I find that code anywhere (sadly on my my private gitlab instance was outdated so it got lost there) I could post what I built but I wouldn't bet on it.
As I said it generally worked really well except for the managed record fpc bug
« Last Edit: February 05, 2023, 11:01:12 am by Warfley »

Bogen85

  • Hero Member
  • *****
  • Posts: 572
[RESOLVED] Re: Smart Pointers, again revisited...
« Reply #12 on: February 07, 2023, 06:53:15 pm »
Please continue this discussion here https://forum.lazarus.freepascal.org/index.php/topic,62213.0.html, I'm marking this topic/thread resolved.

 

TinyPortal © 2005-2018