Lazarus

Programming => General => Topic started by: J-23 on October 28, 2020, 10:14:44 pm

Title: Bug TFPGInterfacedObjectList??
Post by: J-23 on October 28, 2020, 10:14:44 pm
Hello,

I haven't used TFPGInterfacedObjectList for a long time and now I need such a list.

Everything works fine theoretically, but I think I ran into a bug in the FPC.

I have a code: (Work OK)
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, fgl;
  9.  
  10. type
  11.   ITest = interface(IUnknown)
  12.   ['{1416EB7E-39B0-4134-BA20-F363D0B8C476}']
  13.    procedure Test;
  14.   end;
  15.  
  16. type
  17.   TListInterface = specialize TFPGInterfacedObjectList<ITest>;
  18.  
  19. type
  20.  
  21.   { TTestClass }
  22.  
  23.   TTestClass = class(TInterfacedObject, ITest)
  24.     procedure Test;
  25.   end;
  26.  
  27. type
  28.  
  29.   { TForm1 }
  30.  
  31.   TForm1 = class(TForm)
  32.     procedure FormCreate(Sender: TObject);
  33.     procedure FormDestroy(Sender: TObject);
  34.   private
  35.     FListInterface: TListInterface;
  36.     FTestClass: TTestClass;
  37.   public
  38.      procedure ExecuteInterface(Test: ITest);
  39.   end;
  40.  
  41. var
  42.   Form1: TForm1;
  43.  
  44. implementation
  45.  
  46. {$R *.lfm}
  47.  
  48. { TTestClass }
  49.  
  50. procedure TTestClass.Test;
  51. begin
  52.  
  53. end;
  54.  
  55. { TForm1 }
  56.  
  57. procedure TForm1.FormCreate(Sender: TObject);
  58. begin
  59.   FListInterface := TListInterface.Create;
  60.  
  61.   FTestClass := TTestClass.Create;
  62.   FListInterface.Add(FTestClass);
  63. end;
  64.  
  65. procedure TForm1.FormDestroy(Sender: TObject);
  66. begin
  67.   FListInterface.Free;
  68. end;
  69.  
  70. procedure TForm1.ExecuteInterface(Test: ITest);
  71. begin
  72.  
  73. end;
  74.  
  75. end.                                                          
  76.  

But the code below causes an error. (I think he shouldn't)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, fgl;
  9.  
  10. type
  11.   ITest = interface(IUnknown)
  12.   ['{1416EB7E-39B0-4134-BA20-F363D0B8C476}']
  13.    procedure Test;
  14.   end;
  15.  
  16. type
  17.   TListInterface = specialize TFPGInterfacedObjectList<ITest>;
  18.  
  19. type
  20.  
  21.   { TTestClass }
  22.  
  23.   TTestClass = class(TInterfacedObject, ITest)
  24.     procedure Test;
  25.   end;
  26.  
  27. type
  28.  
  29.   { TForm1 }
  30.  
  31.   TForm1 = class(TForm)
  32.     procedure FormCreate(Sender: TObject);
  33.     procedure FormDestroy(Sender: TObject);
  34.   private
  35.     FListInterface: TListInterface;
  36.     FTestClass: TTestClass;
  37.   public
  38.      procedure ExecuteInterface(Test: ITest);
  39.   end;
  40.  
  41. var
  42.   Form1: TForm1;
  43.  
  44. implementation
  45.  
  46. {$R *.lfm}
  47.  
  48. { TTestClass }
  49.  
  50. procedure TTestClass.Test;
  51. begin
  52.  
  53. end;
  54.  
  55. { TForm1 }
  56.  
  57. procedure TForm1.FormCreate(Sender: TObject);
  58. begin
  59.   FListInterface := TListInterface.Create;
  60.  
  61.   FTestClass := TTestClass.Create;
  62.   ExecuteInterface(FTestClass);
  63.   FListInterface.Add(FTestClass);
  64. end;
  65.  
  66. procedure TForm1.FormDestroy(Sender: TObject);
  67. begin
  68.   FListInterface.Free;
  69. end;
  70.  
  71. procedure TForm1.ExecuteInterface(Test: ITest);
  72. begin
  73.  
  74. end;
  75.  
  76. end.                                                                
  77.  

The only difference is this line
Code: Pascal  [Select][+][-]
  1. ExecuteInterface(FTestClass);
Title: Re: Bug TFPGInterfacedObjectList??
Post by: J-23 on October 29, 2020, 03:08:00 am
Eh ... memory plays tricks :)

Code: Pascal  [Select][+][-]
  1. TInterfaceList
Title: Re: Bug TFPGInterfacedObjectList??
Post by: ASerge on October 29, 2020, 09:56:09 pm
The only difference is this line
Code: Pascal  [Select][+][-]
  1. ExecuteInterface(FTestClass);
The "ExecuteInterface" and "FListInterface.Add" methods are expected an interface parameter, but you pass the class parameter. An implicit conversion is made, but you do not control the lifetime, so the object (this is the interface) by the reference counter "dies" on the first call.
There are different solutions.
1. Skip a reference counter in methods:
  procedure ExecuteInterface(const Test: ITest);
2. Explicit add reference:
  ExecuteInterface(FTestClass as ITest);
3. Use a variable of the desired type (the safest solution):
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   LTest: ITest;
  4. begin
  5.   FListInterface := TListInterface.Create;
  6.   FTestClass := TTestClass.Create;
  7.   LTest := FTestClass;
  8.   ExecuteInterface(LTest);
  9.   FListInterface.Add(LTest);
  10. end;
TinyPortal © 2005-2018