Recent

Author Topic: Generics & Interfaces how to properly free instance?  (Read 3969 times)

Mr. George

  • New Member
  • *
  • Posts: 18
Generics & Interfaces how to properly free instance?
« on: July 31, 2021, 12:13:35 am »
Here is a code, that include Interface and implementation.
Can't figure out how to properly free object in this case.
Some suggestions would be appreciated.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode delphi}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes,
  9.   SysUtils,
  10.   Forms,
  11.   Controls,
  12.   Graphics,
  13.   Dialogs,
  14.   //{ rtti }
  15.   //Rtti,
  16.   //TypInfo,
  17.   Generics.Defaults;
  18.  
  19. type
  20.  
  21.   { TForm1 }
  22.  
  23.   TForm1 = class(TForm)
  24.     procedure FormCreate(Sender: TObject);
  25.   private
  26.   public
  27.   end;
  28.  
  29.   ISomeInterface = interface
  30.     function TestFunc(): String;
  31.     procedure Free();
  32.   end;
  33.  
  34.   { TSomeInterfaceImplementation }
  35.  
  36.   TSomeInterfaceImplementation = class(TSingletonImplementation, ISomeInterface)
  37.     function TestFunc(): String;
  38.   end;
  39.  
  40.   { TGeneric }
  41.  
  42.   TGeneric<T> = class
  43.     SomeField: T;
  44.     destructor Destroy(); override;
  45.   end;
  46.  
  47. var
  48.   Form1: TForm1;
  49.  
  50. implementation
  51.  
  52. {$R *.lfm}
  53.  
  54. { TSomeInterfaceImplementation }
  55.  
  56. function TSomeInterfaceImplementation.TestFunc(): String;
  57. begin
  58.   Result := 'ok';
  59. end;
  60.  
  61. { TGeneric }
  62.  
  63. destructor TGeneric<T>.Destroy();
  64. begin
  65.   if Assigned(SomeField) then
  66.     SomeField.Free; // AV here
  67.   // FreeAndNil(SomeField);
  68.   inherited;
  69. end;
  70.  
  71. { TForm1 }
  72.  
  73. procedure TForm1.FormCreate(Sender: TObject);
  74. var
  75.   Gen: TGeneric<ISomeInterface>;
  76. begin
  77.   Gen := TGeneric<ISomeInterface>.Create();
  78.   Gen.SomeField := TSomeInterfaceImplementation.Create;
  79.   Gen.Free;
  80. end;
  81.  
  82. end.
  83.  

rsz

  • New Member
  • *
  • Posts: 45
Re: Generics & Interfaces how to properly free instance?
« Reply #1 on: July 31, 2021, 06:04:30 am »
Hi,

The default COM style interfaces do reference counting and because of that the compiler inserts calls to the reference counting functions in the destructor TGeneric<T>.Destroy, which results in an access violation after the free just before the function ends in fpc_intf_decr_ref (see in Assembler).

So to get the behavior you want, add {$interfaces corba} below mode delphi and don't descend from TSingletonImplementation in TSomeInterfaceImplementation.

See:
https://www.freepascal.org/docs-html/current/ref/refse51.html#x104-1280007.8
https://www.freepascal.org/docs-html/current/ref/refse50.html#x103-1270007.7

Mr. George

  • New Member
  • *
  • Posts: 18
Re: Generics & Interfaces how to properly free instance?
« Reply #2 on: July 31, 2021, 11:39:25 am »
Thanks!

From delphi documentation:
TSingletonImplementation is a base for simple classes that need a basic IInterface implementation, with reference counting disabled.

So, in FPC TSingletonImplementation does not behaves like in Delphi?

With COBRA enabled, how one interface can be casted to another?
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode delphi}
  4. {$interfaces corba}
  5.  
  6. interface
  7.  
  8. uses
  9.   Classes,
  10.   SysUtils,
  11.   Forms,
  12.   Controls,
  13.   Graphics,
  14.   Dialogs,
  15.   //{ rtti }
  16.   //Rtti,
  17.   //TypInfo,
  18.   Generics.Defaults;
  19.  
  20. type
  21.  
  22.   { TForm1 }
  23.  
  24.   TForm1 = class(TForm)
  25.     procedure FormCreate(Sender: TObject);
  26.   private
  27.   public
  28.   end;
  29.  
  30.   ISomeBaseInterface = interface
  31.     ['{FAB63768-8D37-412B-9BB9-5454751EC982}']
  32.     function TestFunc(): String;
  33.     procedure Free();
  34.   end;
  35.  
  36.   ISomeInterface = interface(ISomeBaseInterface)
  37.     ['{FAB63768-8D37-412B-9BB9-5454751EC982}']
  38.     property prop1: String;
  39.   end;
  40.  
  41.   { TSomeInterfaceImplementation }
  42.  
  43.   TSomeInterfaceImplementation = class(ISomeBaseInterface)
  44.   private
  45.     fProp1: String;
  46.   public
  47.     function TestFunc(): String;
  48.     property prop1: String read fProp1 write fProp1;
  49.   end;
  50.  
  51.   { TGeneric }
  52.  
  53.   TGeneric<T> = class
  54.     SomeField: T;
  55.     destructor Destroy(); override;
  56.   end;
  57.  
  58. var
  59.   Form1: TForm1;
  60.  
  61. implementation
  62.  
  63. {$R *.lfm}
  64.  
  65. { TSomeInterfaceImplementation }
  66.  
  67. function TSomeInterfaceImplementation.TestFunc(): String;
  68. begin
  69.   Result := 'ok';
  70. end;
  71.  
  72. { TGeneric }
  73.  
  74. destructor TGeneric<T>.Destroy();
  75. begin
  76.   if Assigned(SomeField) then
  77.     SomeField.Free;
  78.   //FreeAndNil(T(SomeField));
  79.   inherited;
  80. end;
  81.  
  82. { TForm1 }
  83.  
  84. procedure TForm1.FormCreate(Sender: TObject);
  85. var
  86.   //Gen: TGeneric<ISomeBaseInterface>;
  87.   P:   ISomeBaseInterface;
  88. begin
  89.   //Gen := TGeneric<ISomeBaseInterface>.Create();
  90.   //Gen.SomeField := TSomeInterfaceImplementation.Create;
  91.   //Gen.Free;
  92.   P := TSomeInterfaceImplementation.Create;
  93.   with P as ISomeInterface do // <-------------- With COBRA: Class or Com interface type expected
  94.    begin
  95.    end;
  96. end;
  97.  
  98. end.
  99.  
« Last Edit: July 31, 2021, 12:05:16 pm by Mr. George »

rsz

  • New Member
  • *
  • Posts: 45
Re: Generics & Interfaces how to properly free instance?
« Reply #3 on: July 31, 2021, 12:26:54 pm »
You're welcome.

I don't use Delphi (or need to be compatible with it) but the documentation from Embarcadero says that TSingletonImplementation inherits from IInterface which in turn inherits from IUnknown. This is  also the case in Free Pascal. Therefore I believe it is safe to assume that the reference counting functions are also called even though it is "disabled". However if you can test and confirm that Free Pascal behaves differently then this may be a bug and would be good to report.

http://docwiki.embarcadero.com/Libraries/Sydney/en/System.Generics.Defaults.TSingletonImplementation
http://docwiki.embarcadero.com/Libraries/Sydney/en/System.IInterface
http://docwiki.embarcadero.com/Libraries/Sydney/en/System.IUnknown
http://docwiki.embarcadero.com/Libraries/Sydney/en/System.IUnknown_Methods

Mr. George

  • New Member
  • *
  • Posts: 18
Re: Generics & Interfaces how to properly free instance?
« Reply #4 on: July 31, 2021, 12:30:24 pm »
Ok, will check.

What about interface casting when COBRA enabled, is it possible somehow?

rsz

  • New Member
  • *
  • Posts: 45
Re: Generics & Interfaces how to properly free instance?
« Reply #5 on: July 31, 2021, 12:47:07 pm »
Yes, but in your example TSomeInterfaceImplementation does not implement ISomeInterface so you can't cast to it.
I assume you meant to make TSomeInterfaceImplementation implement ISomeInterface instead of ISomeBaseInterface?
Then casting would work and the P var in FormCreate would need to be of type TSomeInterfaceImplementation.
« Last Edit: July 31, 2021, 12:50:44 pm by rsz »

Mr. George

  • New Member
  • *
  • Posts: 18
Re: Generics & Interfaces how to properly free instance?
« Reply #6 on: July 31, 2021, 12:58:29 pm »
Quote
I assume you meant to make TSomeInterfaceImplementation implement ISomeInterface instead of ISomeBaseInterface?
No. ISomeBaseInterface is base interface for few descendant interfaces (in demo i added only one - ISomeInterface).
Why so? I can add method, that will accept ISomeBaseInterface, inside, i will be sure that parameter one of few descendant interfaces.
I can check for example internal field to identify exact descendant implementation and cast to proper interface.
That works without COBRA, but does not work with COBRA.
« Last Edit: July 31, 2021, 01:00:26 pm by Mr. George »

PascalDragon

  • Hero Member
  • *****
  • Posts: 3324
  • Compiler Developer
Re: Generics & Interfaces how to properly free instance?
« Reply #7 on: August 01, 2021, 12:49:57 pm »
Here is a code, that include Interface and implementation.
Can't figure out how to properly free object in this case.
Some suggestions would be appreciated.

As rsz said, COM-style interfaces (the default) are reference counted. As you spezialize your TGeneric<> with ISomeInterface you simply need to do the following: nothing. That's right, your destructor of TGeneric<> can simply be empty. You also don't need to export Free in ISomeInterface (with COM-style interfaces that is a really bad design as you've learned).

Mr. George

  • New Member
  • *
  • Posts: 18
Re: Generics & Interfaces how to properly free instance?
« Reply #8 on: August 01, 2021, 01:42:21 pm »
Code from first post is pretty simple, it indeed can use ref counting and auto destruction.
I use interfaces without reference count feature and auto object destruction in my old project.
Main goal was to describe some requirements for classes implementations and provide ability to pass objects for different methods that must support required interface.
That's why i use TSingletonImplementation.

Anyway, thanks for help.
Currently i already solved issue.

 

TinyPortal © 2005-2018