Recent

Author Topic: const interface parameter  (Read 6064 times)

ufm

  • Newbie
  • Posts: 2
const interface parameter
« on: August 11, 2017, 01:35:40 pm »
Hi!

For example, we have T2 0 interfaced class and IT2 - interface.

function tmt(const a: IT2): boolean;
...
tmt(T2.Create()) - do not operate with reference counter (I.e. destructor will be do not executed).

But if rewrite tmt to
function tmt(a: IT2): boolean;
all work as planned.

Can anyone help me - this behavior it's a bug or feature?

WBR,
    Fyodor.

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: const interface parameter
« Reply #1 on: August 11, 2017, 02:08:38 pm »
...do not operate with reference counter...
Can anyone help me - this behavior it's a bug or feature?
According docs:
Quote
This is particularly important and visible when using refcounted types. For such types, the (invisible) incrementing and decrementing of any reference count is omitted when const is used. Doing so often allows the compiler to omit invisible try/finally frames for these routines.
This behaviour is by design
Use the variable of  the interface type.

ufm

  • Newbie
  • Posts: 2
Re: const interface parameter
« Reply #2 on: August 11, 2017, 05:18:26 pm »
This behaviour is by design
Use the variable of  the interface type.

Understand. Thnx.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1314
    • Lebeau Software
Re: const interface parameter
« Reply #3 on: August 11, 2017, 08:11:39 pm »
function tmt(const a: IT2): boolean;
...
tmt(T2.Create()) - do not operate with reference counter (I.e. destructor will be do not executed).

Delphi has the exact same problem.  It is a gotcha in how 'const' bypasses reference counting.  Neither compiler (Delphi or FreePascal) is smart enough to recognize when an interfaced object is constructed directly in a const parameter, which would require a local variable to hold the object so it gets refcounted and destructed correctly.  People have complained about this issue in Delphi for years, and BorInCoDero has acknowledged it but refuses to fix it.  Sad that it exists in FreePascal as well, especially if it is "by design" - it is broken design!
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: const interface parameter
« Reply #4 on: August 11, 2017, 08:17:25 pm »
Sad that it exists in FreePascal as well, especially if it is "by design" - it is broken design!
I might be wrong here, so grain of salt and all that, but I am under the impression that constref was created for this purpose exclusively.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Wosi

  • New Member
  • *
  • Posts: 21
Re: const interface parameter
« Reply #5 on: August 11, 2017, 09:42:33 pm »
FWIW, in case you don't want to create a separate variable for the new object you can cast it to the interface type to activate the reference counting:
Code: Pascal  [Select][+][-]
  1. tmt(T2.Create() as IT2)

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: const interface parameter
« Reply #6 on: August 11, 2017, 10:04:44 pm »
...that constref was created for this purpose exclusively.
In the context of this topic, const and constref do not differ.

For fans to create objects without binding to variables: you can query interface explicitly:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$APPTYPE CONSOLE}
  3.  
  4. type
  5.   ITest = interface(IInterface)
  6.   ['{B52EF14B-23C5-40C8-8FFE-04D046842060}']
  7.   end;
  8.  
  9.   TTest = class(TInterfacedObject, ITest)
  10.   public
  11.     destructor Destroy; override;
  12.   end;
  13.  
  14. destructor TTest.Destroy;
  15. begin
  16.   Writeln('TTest.Destroy');
  17.   inherited;
  18. end;
  19.  
  20. procedure UseRef(const ARef: ITest);
  21. begin
  22.   // We use ARef, otherwise the compiler excludes it from the code
  23.   Writeln('UseRef: ', PtrInt(Pointer(ARef)));
  24. end;
  25.  
  26. procedure Test;
  27. begin
  28.   Writeln('Test');
  29.   UseRef(TTest.Create as ITest);
  30. end;
  31.  
  32. begin
  33.   Test;
  34.   Writeln('press Enter...');
  35.   Readln;
  36. end.
Quote
Test
UseRef: 17257160
TTest.Destroy
press Enter...

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1314
    • Lebeau Software
Re: const interface parameter
« Reply #7 on: August 11, 2017, 10:24:03 pm »
FWIW, in case you don't want to create a separate variable for the new object you can cast it to the interface type to activate the reference counting:
Code: Pascal  [Select][+][-]
  1. tmt(T2.Create() as IT2)

Yes, and that works in Delphi as well. Just note that the interface reference is not cleared when tmt() exits, like one would expect, but rather when the *calling* code exits!  This is because the compiler is creating a hidden local variable that acts like any other variable, eg:

Code: Pascal  [Select][+][-]
  1. var
  2.   ...
  3.   hiddenTemp: IT2;
  4. begin
  5.   ...
  6.   hiddenTemp := T2.Create() as IT2;
  7.   tmt(hiddenTemp);
  8.   ... // <-- hiddenTemp is not released here!
  9.   ...
  10. end; // <-- hiddenTemp is released here instead!
  11.  

So be aware of that.

This is not always intuitive to interface beginners (or even long-time interface users who don't use 'const' alot).
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: const interface parameter
« Reply #8 on: August 12, 2017, 03:25:07 am »
thank you for your insight.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: const interface parameter
« Reply #9 on: August 12, 2017, 12:13:10 pm »
It's a known and undocumented bug that will not get fixed until it's fixed in Delphi (fpc follows Delphi to the letter including the bugs).
Never use const with ref counted interfaces.
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: const interface parameter
« Reply #10 on: August 12, 2017, 12:56:57 pm »
It's a known and undocumented bug that will not get fixed until it's fixed in Delphi (fpc follows Delphi to the letter including the bugs).
Never use const with ref counted interfaces.

Oh? Explain the bug, plz.... Because there isn't any.... Some people mix classes with interfaces as was initially the case here, but that is programmer error.
A const interface parameter needs to increase and decrease refcount inside a procedure or function body, that's not a bug.
It needs to have a valid reference at all times.
Furthermore: FPC behaves differently than Delphi regarding release moment inside a procedure or function body in this case.
And that is implementation detail AND is documented AND usually does not matter.

Just curious: WHY do you think it is a bug?  :D :D It isn't... Also not in other languages....that support COM interfaces....
« Last Edit: August 12, 2017, 02:21:32 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: const interface parameter
« Reply #11 on: August 12, 2017, 06:56:15 pm »
Never use const with ref counted interfaces.
I do not agree. I always use const for reference types. Moreover, I recommend doing this, precisely because of the optimality that is described in the documentation. But I do not recommend creating objects without binding, at which an error occurs.

Ondrej Pokorny

  • Full Member
  • ***
  • Posts: 220
Re: const interface parameter
« Reply #12 on: August 12, 2017, 06:57:56 pm »
Yes. I was bitten by it as well. But I share the opinion that it is a feature. A good one, actually ;) You just have to be aware of that. Don't use const if you want to have reference counting.

Btw. const params for strings have a similar problem (code is Delphi, FPC doesn't support abstract methods yet):

Code: [Select]
program Project1;

type
  TAbsProc = reference to procedure(const S: string);

procedure DoSomething(const S: string; const aProc: TAbsProc);
begin
  Writeln(S);
  aProc(S);
  Writeln(S);
end;

var
  S: string;
begin
  S := 'a';

  DoSomething(S,
    procedure(const T: string)
    begin
      S := T+T;
    end
    );
end.

versus:

Code: [Select]
program Project1;

type
  TAbsProc = reference to procedure(const S: string);

procedure DoSomething(S: string; const aProc: TAbsProc);
begin
  Writeln(S);
  aProc(S);
  Writeln(S);
end;

var
  S: string;
begin
  S := 'a';

  DoSomething(S,
    procedure(const T: string)
    begin
      S := T+T;
    end
    );
end.

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: const interface parameter
« Reply #13 on: August 12, 2017, 08:39:02 pm »
Btw. const params for strings have a similar problem (code is Delphi, FPC doesn't support abstract methods yet):
Have the similar (identical) feature, not problem. It is true for every reference counted type by necessity: keeping a reference... The difference between passing by value and const is the implicit try/finally, not the refcount....

(2. on OSX FPC supports blocks already. That's basically similar to what you call "abstract methods", they are actually not abstract, but anonymous methods)
« Last Edit: August 12, 2017, 08:47:18 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

dr2e

  • Newbie
  • Posts: 6
Re: const interface parameter
« Reply #14 on: December 30, 2023, 08:41:07 am »
Delphi has the exact same problem.  It is a gotcha in how 'const' bypasses reference counting.  Neither compiler (Delphi or FreePascal) is smart enough to recognize when an interfaced object is constructed directly in a const parameter, which would require a local variable to hold the object so it gets refcounted and destructed correctly.  People have complained about this issue in Delphi for years, and BorInCoDero has acknowledged it but refuses to fix it.  Sad that it exists in FreePascal as well, especially if it is "by design" - it is broken design!

Just an example of why it's a good design:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. type
  4.    IMyInterface = interface
  5.       procedure Hello;
  6.    end;
  7.  
  8.    TMyClass = class(TInterfacedObject, IMyInterface)
  9.       procedure Hello;
  10.       destructor Destroy; override;
  11.    end;
  12.  
  13. procedure InterfaceCall(I: IMyInterface);
  14. begin
  15.    I.Hello;
  16. end;
  17.  
  18. procedure ConstInterfaceCall(const I: IMyInterface);
  19. begin
  20.    I.Hello;
  21. end;
  22.  
  23. procedure TMyClass.Hello;
  24. begin
  25.    writeln(#9'Hello world!');
  26. end;
  27.  
  28. destructor TMyClass.Destroy;
  29. begin
  30.    writeln('Destroy');
  31.    inherited Destroy;
  32. end;
  33.  
  34. var
  35.    A: TMyClass;
  36.    I: Integer;
  37. begin
  38.    A := TMyClass.Create;
  39.  
  40.    writeln('const');
  41.    for I := 0 to 10 do
  42.       ConstInterfaceCall(A);  { It's working }
  43.  
  44.    writeln('not const');
  45.    for I := 0 to 10 do
  46.       InterfaceCall(A);  { It's not working }
  47. end.

(Yes, it was a long time ago, but documentation is scarce and private opinions are often misleading.)

 

TinyPortal © 2005-2018