Recent

Author Topic: Is there any way to implement automated? reference counting?  (Read 1746 times)

egsuh

  • Hero Member
  • *****
  • Posts: 1789
I mean, something like following:


Code: Pascal  [Select][+][-]
  1. TMyClass = class
  2.      RefCount: integer;
  3.      ...
  4. end;
  5.  
  6. var
  7.     A, B, C : TMyClass;
  8.  
  9. begin
  10.     A := TMyClass.Create;
  11.     B := A;
  12.     C := B;
  13.     showmessage(InttoStr(c.refcount));   // to show 3
  14.  
  15.     C := nil ;
  16.     showmessage(InttoStr(a.refcount));   // to show 2
  17.  
  18.     B := nil;
  19.     A := nil;  // At this stage, it is freed automatically, hopefully.
  20. end;
  21.  
     

 

Khrys

  • Sr. Member
  • ****
  • Posts: 434
Re: Is there any way to implement automated? reference counting?
« Reply #1 on: August 12, 2025, 08:48:50 am »
Yes, automatic reference counting can be implemented using record management operators.
A simpler solution, though, would be to use COM interfaces, which have built-in reference counting.

What is your use case, exactly?

egsuh

  • Hero Member
  • *****
  • Posts: 1789
Re: Is there any way to implement automated? reference counting?
« Reply #2 on: August 12, 2025, 09:04:49 am »
Quote
Yes, automatic reference counting can be implemented using record management operators.
A simpler solution, though, would be to use COM interfaces, which have built-in reference counting.

What is your use case, exactly?

Neither of them... strictly speaking, I'd like to implement reference counting to my generic class type.

cdbc

  • Hero Member
  • *****
  • Posts: 2771
    • http://www.cdbc.dk
Re: Is there any way to implement automated? reference counting?
« Reply #3 on: August 12, 2025, 09:35:42 am »
Hi
Maybe there's something for you in the implementation of 'TInterfacedObject', you should definitely have a 'LookSee'...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Thaddy

  • Hero Member
  • *****
  • Posts: 19126
  • Glad to be alive.
Re: Is there any way to implement automated? reference counting?
« Reply #4 on: August 12, 2025, 11:58:07 am »
Quote
Yes, automatic reference counting can be implemented using record management operators.
A simpler solution, though, would be to use COM interfaces, which have built-in reference counting.

What is your use case, exactly?

Neither of them... strictly speaking, I'd like to implement reference counting to my generic class type.
And how would you implement a compile time GUID for generic classes? You can't call CreateGUID on a compile-time constant.

I have a solution, but that solution means that all specializations share the same interface.
If that is not an objection I can give you the code here. The solution I have is code that adds an IUnkown interface, addref and release to any class with a simple constructor create. It works in trunk only, because the 3.2.2 compiler did not fully define TObject before the generic specialization.
I had an idea along those lines and Warfley kindly implemented the missing code in trunk: TObject is now fully defined before specialization/construction.
So I could write this:
Code: Pascal  [Select][+][-]
  1. unit genericinterface;
  2. {
  3.   The ability to add a COM interface to existing classes
  4.   that do not inherit from TInterfacedObject.
  5.  
  6.   Currently limited to classes with a simple constructor create.
  7.   An enhanced version is available on request:
  8.   thaddydekoning[at]gmail[dot]com
  9.  
  10.   Copyright (c) 2024,2025 Thaddy de Koning
  11.  
  12.   M.I.T. licensed.
  13.  
  14.   Permission is hereby granted, free of charge, to any person obtaining a copy of this
  15.   software and associated documentation files (the "Software"), to deal in the Software
  16.   without restriction, including without limitation the rights to use, copy, modify, merge,
  17.   publish, distribute, sublicense, and/or sell copies of the Software, and to permit
  18.   persons to whom the Software is furnished to do so, subject to the following conditions:
  19.  
  20.   The above copyright notice and this permission notice shall be included in all copies or
  21.    substantial portions of the Software.
  22.  
  23.   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  24.   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  25.   PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  26.   FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  27.   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  28.   DEALINGS IN THE SOFTWARE.
  29. }
  30. {$if fpc_fullversion < 30301}{$error needs fpc 3.3.1 or higher }{$ifend}
  31. {$mode delphi}{$interfaces COM}
  32.  
  33. interface
  34. uses sysutils, classes;
  35.  
  36. type
  37.   { Our own little interface }
  38.   IInterfaced = interface
  39.    ['{ADEAD83C-06E1-41A5-A40B-C19D06FE8B9F}']
  40.   end;
  41.  
  42.   TInterfaced<T:class,constructor> = class(T,IInterfaced)
  43.   strict private
  44.   class var
  45.     frefcount:integer;
  46.   strict protected
  47.     { winapi is cross platform: the compiler chooses the right calling convention, also on e.g. Linux }
  48.     function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;
  49.       out obj) : longint;winapi;
  50.     function _AddRef : longint;winapi;
  51.     function _Release : longint;winapi;  
  52.   public
  53.     procedure AfterConstruction;override;    
  54.     procedure BeforeDestruction;override;
  55.     class constructor create;
  56.     class function NewInstance : TObject;override;
  57.     class property refcount:integer read FRefcount;
  58.   end;
  59.  
  60. implementation
  61.  
  62.   function TInterfaced<T>.QueryInterface(
  63.   {$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;out obj) : longint;winapi;
  64.   begin
  65.      if GetInterface(iid,obj) then
  66.       result:=S_OK
  67.      else
  68.       result:=longint(E_NOINTERFACE);
  69.   end;
  70.  
  71.   function TInterfaced<T>._AddRef : longint;winapi;
  72.   begin
  73.     _addref:=InterlockedIncrement(frefcount);
  74.   end;
  75.  
  76.   function TInterfaced<T>._Release : longint;winapi;
  77.   begin
  78.     Result := InterlockedDecrement(frefcount);
  79.     if Result = 0 then
  80.       Destroy;
  81.   end;
  82.  
  83.   procedure TInterfaced<T>.AfterConstruction;
  84.   begin
  85.     InterlockedDecrement(frefcount);
  86.   end;
  87.  
  88.   procedure TInterfaced<T>.BeforeDestruction;
  89.   begin
  90.     // should not happen, does not warrant exception use
  91.     if frefcount <> 0 then
  92.       halt(204);
  93.   end;
  94.  
  95.   class function TInterfaced<T>.NewInstance : TObject;
  96.   begin
  97.     result:= inherited NewInstance;
  98.     TInterfaced<T>(result).frefcount := 1;
  99.   end;
  100.  
  101.   class constructor TInterfaced<T>.create;    
  102.   begin
  103.     FRefcount := 0;
  104.   end;
  105. end.
Example:
Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. uses sysutils, classes, genericinterface;
  3. type
  4.   TInterfacedStringlist = TInterfaced<TStringlist>;
  5. var
  6.   List:IInterfaced;
  7.   SList:TStringlist;
  8. begin
  9.   List :=TInterfacedStringlist.create; // also creates the TStringlist instance
  10.   { with.......}
  11.   with list as TInterfacedStringlist do
  12.   begin
  13.     Add('some text');
  14.     writeln(Text);
  15.   end;
  16.   { alternatively }
  17.   Slist := list as TInterfacedStringlist; // no need to create, just assign to TStringlist variable
  18.   slist.add('some more text');
  19.   writeln(slist.text);  
  20. end.// no leaks
That is about the closest you can get without deriving from TInterfacedObject.
I believe I already showed this before on the forum.
The technique that is used is also called "boxing".

For an alternative look at the threads for AutoFree, which is releated.
The code is - in principle - threadsafe, but people can make accidents.

« Last Edit: August 12, 2025, 04:27:49 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Thaddy

  • Hero Member
  • *****
  • Posts: 19126
  • Glad to be alive.
Re: Is there any way to implement automated? reference counting?
« Reply #5 on: August 12, 2025, 05:34:58 pm »
Modified your example in the request:
Code: Pascal  [Select][+][-]
  1. Program examplecom;
  2. {
  3.   The ability to add a COM interface to classes
  4.   that do not inherit from TInterfacedObject.
  5.  
  6.   Allows automatic memory management (ARC style)
  7. }
  8.  
  9. {$mode delphi}
  10. uses
  11.   sysutils, classes, genericinterface;
  12. type
  13.   TInterfacedStringlist = TInterfaced<TStringlist>;
  14. var
  15.   a,b,c:IInterfaced;
  16.   d,e,f:TStringlist;
  17. begin
  18.   a := TInterfacedStringlist.create;
  19.   b := a;
  20.   c := b;
  21.   d := a as TStringlist;  // just assign, no create;
  22.   e := b as TStringlist;
  23.   f := c as TStringlist;
  24.   { with.......}
  25.   with a as TStringlist do
  26.   begin
  27.     Add('some text');
  28.     writeln(Text);
  29.   end;
  30.   f.Add('some more text');
  31.   writeln(TInterfacedStringlist.refcount);// refcount is a class property....
  32.   writeln(f.text);  
  33. end.// no leaks

This is analog to your TMyObject.
« Last Edit: August 12, 2025, 06:16:34 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

cdbc

  • Hero Member
  • *****
  • Posts: 2771
    • http://www.cdbc.dk
Re: Is there any way to implement automated? reference counting?
« Reply #6 on: August 12, 2025, 06:49:58 pm »
Hi
Them be some mighty fine gems, you're unleashing here Thaddy  8-) Me Likey  8)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Warfley

  • Hero Member
  • *****
  • Posts: 2054
Re: Is there any way to implement automated? reference counting?
« Reply #7 on: August 12, 2025, 06:54:26 pm »
The easiest way is probably to use a COM interface. For my STAX project I had the situation that the generators might not have a clear lifetime and ownership, so I neeeded reference counting. So I just build them using interfaces: Link

The issue is that you need to define the public interface twice, once for the interface and once for the class. But if you don't mind about that they are an easy way to deal with it

Thaddy

  • Hero Member
  • *****
  • Posts: 19126
  • Glad to be alive.
Re: Is there any way to implement automated? reference counting?
« Reply #8 on: August 12, 2025, 07:02:57 pm »
The issue is that you need to define the public interface twice, once for the interface and once for the class. But if you don't mind about that they are an easy way to deal with it
Or alternatively use a soft cast with as.

There is a a way around that with my Auto() code but that still needs adjusting the compiler somewhat.
This is reported.
« Last Edit: August 12, 2025, 07:13:05 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

BeniBela

  • Hero Member
  • *****
  • Posts: 959
    • homepage
Re: Is there any way to implement automated? reference counting?
« Reply #9 on: August 13, 2025, 11:55:26 pm »
The issue is that you need to define the public interface twice, once for the interface and once for the class. But if you don't mind about that they are an easy way to deal with it
Or alternatively use a soft cast with as.

"as" is extremely slow.

you should never use that

you could define a method that returns "self"

Thaddy

  • Hero Member
  • *****
  • Posts: 19126
  • Glad to be alive.
Re: Is there any way to implement automated? reference counting?
« Reply #10 on: August 14, 2025, 12:45:13 pm »
As just depends on how queryinterface is implemented. It is certainly not always slow  and in the most comfortable case needs to be called only once. See my examples and Warfley's remark. At  one point it must be used somewhere anyway. There is no way around that. After that, you can also use hardcasts, but I prefer the double declaration of interface and instance variables. (for now)
« Last Edit: August 14, 2025, 12:47:19 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

 

TinyPortal © 2005-2018