Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2011-09

Interface, _AddRef, _Release, etc.

17 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Marcos Douglas

Hi,

A class (TFoo) implements an interface (IFoo), but my class has more methods and properties (in the eg below just one more method) that I need use.

Another object has a property of IFoo type.
So, I created a Foo and assigns my object property.

From this moment I can not longer release my Foo object, because the refcount.

See (attention in the comments below):

program p1;
{$ifdef FPC}
  {$mode objfpc}{$H+}
{$else}
  {$apptype console}
{$endif}
uses  heaptrc,  Classes,  SysUtils;
type
  IFoo = interface
    ['{F71A4131-E0A5-48CB-B563-7BBB079D1085}']
    function GetInfo: string;
  end;
  TFoo = class(TInterfacedObject, IFoo)
  private
    FInfo: string;
  protected
  public
    function GetInfo: string;
    procedure SetInfo(const AValue: string);
  end;
function TFoo.GetInfo: string;
begin
  Result := FInfo;
end;
procedure TFoo.SetInfo(const AValue: string);
begin
  FInfo := AValue;
end;
type
  TObj = class(TObject)
  public
    Foo: IFoo;
  end;
procedure Run;
var
  f: TFoo;  // << type is class, not interface
  o: TObj;
begin
  f := TFoo.Create;
  f.SetInfo('foo');
  o := TObj.Create;
  o.Foo := f;
  writeln(o.Foo.GetInfo);
  o.Free;
//  f.Free; // << I can't use it because it already was freed
end;
begin
  Run;
  writeln('Done');
end.

BUT, if I create my TFoo without refcount should works, right? So, I change my class:

  TFoo = class(TObject, IFoo)
  private
    FInfo: string;
  protected
    function QueryInterface(const iid : tguid;out obj) : longint;stdcall;
    function _AddRef : longint;stdcall;
    function _Release : longint;stdcall;
  public
    function GetInfo: string;
    procedure SetInfo(const AValue: string);
  end;
function TFoo.QueryInterface(const iid: TGuid; out obj): longint; stdcall;
begin
  if GetInterface(iid,obj) then
    Result := S_OK
  else
    Result:=longint(E_NOINTERFACE);
end;
function TFoo._AddRef: longint; stdcall;
begin
  Result := -1;
end;
function TFoo._Release: longint; stdcall;
begin
  Result := -1;
end;

... but, does not work. In Delphi 7 yes, but no in FPC.

See this http://bugs.freepascal.org/view.php?id=15526

Do you think this is right?

Marcos Douglas

Graeme Geldenhuys
to Marcos Douglas
Marcos Douglas wrote 7 lines
procedure Run; var f: TFoo; // << type is class, not interface o: TObj; begin f := TFoo.Create;

This is just wrong.

Show quoted text 14 lines
BUT, if I create my TFoo without refcount should works, right? So, I change my class: TFoo = class(TObject, IFoo) private FInfo: string; protected function QueryInterface(const iid : tguid;out obj) : longint;stdcall; function _AddRef : longint;stdcall; function _Release : longint;stdcall; public function GetInfo: string; procedure SetInfo(const AValue: string); end;

On a side note:
---------------
Why not simply use CORBA interfaces, then you don't need to add the IUnknown or IInterface interface method noise to your class declaration. CORBA interfaces are not reference counted.

eg:

unit myunit;
{$mode objfpc}{$H+}
{$interfaces corba}
interface
type
   IFoo = interface    // NOTE: No IInterface must be specified
     ['{F71A4131-E0A5-48CB-B563-7BBB079D1085}']
     function GetInfo: string;
   end;
   TFoo = class(TObject, IFoo)
     ...
   end;

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/
Zaher Dirkey
to Graeme Geldenhuys

On Thu, Sep 15, 2011 at 12:27 PM, Graeme Geldenhuys <

Show quoted text 14 lines
wrote: On 14/09/2011 23:17, Marcos Douglas wrote: > > procedure Run; > var > f: TFoo; // << type is class, not interface > o: TObj; > begin > f := TFoo.Create; This is just wrong.

1 - If i use the class not Interface is the recount used? 2 - Is that compatible with Delphi?

In 1 i meant, as i expected if i use f:IFoo the refcount counted, but if i use f:TFoo methods of reference not triggered?

Thanks
Zaher Dirkey

Jonas Maebe
to Marcos Douglas: A class (TFoo) implements an interface (IFoo), but my class has more methods and…
Marcos Douglas wrote
BUT, if I create my TFoo without refcount should works, right?

Only in some circumstances. These circumstances depend on when the compiler allocates temporary interface instances, and when these are finalized.

... but, does not work. In Delphi 7 yes, but no in FPC.

It also "works" in FPC if you remove heaptrc. What happens is a) the compiler creates a temporary copy of the interface instance at some point in the code
b) you free the underlying object and heaptrc overwrites its memory with 0xf0f0f0f0
c) at the end of the routine, the compiler tries to finalize the temporary interface instance. This crashes because the interface instance no longer contains valid data

Without heaptrc, the example happens to work because the freed memory of the instance hasn't been reallocated and overwritten yet. It's not safe in general though.

The answer is: the hack with returning -1 from _AddRef and _Release, while documented by Embarcadero, is unsafe because it depends on implementation details. Do not use it. Instead, if you do not want reference-counted interfaces, use {$interfaces corba} instead as mentioned earlier in this thread.

Jonas

Marcos Douglas
to Graeme Geldenhuys: This is just wrong. On a side note: --------------- Why not simply use CORBA interfaces,…
Graeme Geldenhuys wrote 10 lines
On 14/09/2011 23:17, Marcos Douglas wrote: > > procedure Run; > var >   f: TFoo;  // << type is class, not interface >   o: TObj; > begin >   f := TFoo.Create; This is just wrong.

Well, wrong just because the language is so but not because is ilogical, right?

Show quoted text 13 lines
On a side note: --------------- Why not simply use CORBA interfaces, then you don't need to add the IUnknown or IInterface interface method noise to your class declaration. CORBA interfaces are not reference counted. eg: unit myunit; {$mode objfpc}{$H+} {$interfaces corba}

Hm... interesting but what are the advantages?
I want my code compiling in Delphi 7 too, so I can have problems with this?

The TInterfacedObject class is the most used in many codes (like Zeos for example) and it works. I do not want problems, incompatibilities, confusion, etc
http://free-pascal-general.1045716.n5.nabble.com/Corba-Interfaces-and-IInterface-query-td3264311.html

Marcos Douglas

Marcos Douglas
to Jonas Maebe: Only in some circumstances. These circumstances depend on when the compiler allocates…
Jonas Maebe wrote 22 lines
On 14 Sep 2011, at 23:17, Marcos Douglas wrote: > BUT, if I create my TFoo without refcount should works, right? Only in some circumstances. These circumstances depend on when the compiler allocates temporary interface instances, and when these are finalized. > ... but, does not work. In Delphi 7 yes, but no in FPC. It also "works" in FPC if you remove heaptrc. What happens is a) the compiler creates a temporary copy of the interface instance at some point in the code b) you free the underlying object and heaptrc overwrites its memory with 0xf0f0f0f0 c) at the end of the routine, the compiler tries to finalize the temporary interface instance. This crashes because the interface instance no longer contains valid data Without heaptrc, the example happens to work because the freed memory of the instance hasn't been reallocated and overwritten yet. It's not safe in general though.

Ok, now I understood.

Show quoted text 5 lines
The answer is: the hack with returning -1 from _AddRef and _Release, while documented by Embarcadero, is unsafe because it depends on implementation details. Do not use it. Instead, if you do not want reference-counted interfaces, use {$interfaces corba} instead as mentioned earlier in this thread.

I know that is a hack... just was an example.
About CORBA I think did you answer my question before (I asked Graeme about it). But if no have reference-counted how I release a reference, just use ref := nil?
I will search -- and test -- about it, thanks.

Marcos Douglas

Felipe Monteiro de Carvalho
to Marcos Douglas: On Thu, Sep 15, 2011 at 6:27 AM, Graeme Geldenhuys Well, wrong just because the language…
Marcos Douglas wrote 5 lines
>>   f := TFoo.Create; > > This is just wrong. Well, wrong just because the language is so but not because is ilogical, right?

I don't understand why it would be wrong. For me it seams correct and similar to this example:

http://wiki.lazarus.freepascal.org/How_To_Use_Interfaces_to_write_less_code

I want my code compiling in Delphi 7 too, so I can have problems with this?

You can always use {$ifndef fpc} and {$ifdef fpc} to write code specific to Delphi or FPC

Felipe Monteiro de Carvalho
Marcos Douglas
to Felipe Monteiro de Carvalho
Felipe Monteiro de Carvalho wrote 12 lines
On Thu, Sep 15, 2011 at 4:54 PM, Marcos Douglas wrote: >>>   f := TFoo.Create; >> >> This is just wrong. > > Well, wrong just because the language is so but not because is ilogical, right? I don't understand why it would be wrong. For me it seams correct and similar to this example: http://wiki.lazarus.freepascal.org/How_To_Use_Interfaces_to_write_less_code

Yeah, you're right... I didn't see this link. =)
But see it one more time: the R instance of TRealClass is not released... saw? I didn't test but I think if R was released will occur an AV.

> I want my code compiling in Delphi 7 too, so I can have problems with this? You can always use {$ifndef fpc} and {$ifdef fpc} to write code specific to Delphi or FPC

Yes, but if I can do this using only one code is better than use conditionals, don't you?

Marcos Douglas

Marcos Douglas
to Felipe Monteiro de Carvalho: I don't understand why it would be wrong. For me it seams correct and similar to this…
Felipe Monteiro de Carvalho wrote
I don't understand why it would be wrong. For me it seams correct and similar to this example: http://wiki.lazarus.freepascal.org/How_To_Use_Interfaces_to_write_less_code

I'd like to use the same idea of link above, ie, using a real class (and your's own methods and properties) but assign the instance of this object, in an interface. But, like the link, if I can not use Free method on my objects... well, I think this is strange.

What I want was: Two classes completly different but that implement the same interface... and these classes will be instanciated using variables with the same type.

To solve my problem, the only way is create more interfaces -- and more code, unfortunately.
See below. 'a' and 'b' are different objects, different methods, but they share the same methods, ie, GetInfo and SetInfo.

program p1;
{$apptype console}
{$ifdef FPC}
  {$mode objfpc}{$H+}
{$endif}
uses
  heaptrc,
  Classes,
  SysUtils;
type
  IInfo = interface
    ['{F71A4131-E0A5-48CB-B563-7BBB079D1085}']
    function GetInfo: string;
    procedure SetInfo(const AValue: string);
  end;
  IInfoA = interface(IInfo)
    ['{0E816EA4-21A5-49B3-B7E8-396E2F788E4B}']
    procedure SetName(const AValue: string);
  end;
  IInfoB = interface(IInfo)
    ['{0E816EA4-21A5-49B3-B7E8-396E2F788E4B}']
    procedure SetNumber(const AValue: Integer);
  end;
  TInfo = class(TInterfacedObject, IInfo)
  private
    FInfo: string;
  public
    function GetInfo: string;
    procedure SetInfo(const AValue: string);
  end;
  TInfoA = class(TInfo, IInfoA)
  public
    procedure SetName(const AValue: string);
  end;
  TInfoB = class(TInfo, IInfoB)
  public
    procedure SetNumber(const AValue: Integer);
  end;
  TObj = class(TObject)
    Info: IInfo;
  end;

{ TInfo }

function TInfo.GetInfo: string;
begin
  Result := FInfo;
end;
procedure TInfo.SetInfo(const AValue: string);
begin
  FInfo := AValue;
end;

{ TInfoA }

procedure TInfoA.SetName(const AValue: string);
begin
  SetInfo('InfoA :: ' + AValue);
end;
procedure TInfoB.SetNumber(const AValue: Integer);
begin
  SetInfo('InfoB :: ' + IntToStr(AValue));
end;
procedure Run;
var
  a: IInfoA;
  b: IInfoB;
  o: TObj;
begin
  a := TInfoA.Create;
  a.SetName('ZEOS');
  b := TInfoB.Create;
  b.SetNumber(99);
  o := TObj.Create;
  o.Info := a;
  writeln(o.Info.GetInfo);
  o.Info := b;
  writeln(o.Info.GetInfo);
  o.Free;
end;
begin
  Run;
end.
Marcos Douglas
to Marcos Douglas: On Thu, Sep 15, 2011 at 12:12 PM, Felipe Monteiro de Carvalho Yeah, you're right... I…
Marcos Douglas wrote 5 lines
Yeah, you're right... I didn't see this link.  =) But see it one more time: the R instance of TRealClass is not released... saw? I didn't test but I think if R was released will occur an AV.

As I said, if we use R.Free...

W:\dev\tests\interfaces\p2>p2.exe
Using regular interfaces
TBaseObject DoAll !
Got interface OK. Calling it
THook Doing something !
TBaseObject DoAll !
Integer GMax:56
Double GMax: 5.65000000000000E+001

An unhandled exception occurred at $2D414635 :
EAccessViolation : Access violation
$2D414635
$00406802 TOBJECT__FREE, line 165 of W:/md/dev/freepascal/compiler/2.4.5/rtl /inc/objpas.inc

Heap dump by heaptrc unit
59 memory blocks allocated : 1227/1440
55 memory blocks freed : 1107/1320
4 unfreed memory blocks : 120
True heap size : 229376 (112 used in System startup) True free heap : 228832
Should be : 228888
Call trace for block $0006D1B8 size 64
$00408168 REALLOCMEM, line 303 of W:/md/dev/freepascal/compiler/2.4.5/rtl/in c/heap.inc
$00407301 fpc_raiseexception, line 196 of W:/md/dev/freepascal/compiler/2.4. 5/rtl/inc/except.inc
$00412C75 RUNERRORTOEXCEPT, line 369 of W:/md/dev/freepascal/compiler/2.4.5/ rtl/objpas/sysutils/sysutils.inc
$00407C5E HANDLEERRORADDRFRAME, line 901 of W:/md/dev/freepascal/compiler/2. 4.5/rtl/inc/system.inc
$00406802 TOBJECT__FREE, line 165 of W:/md/dev/freepascal/compiler/2.4.5/rtl /inc/objpas.inc
$0040BAA1 EXE_ENTRY, line 376 of system.pp
Call trace for block $00056340 size 24
$00407301 fpc_raiseexception, line 196 of W:/md/dev/freepascal/compiler/2.4. 5/rtl/inc/except.inc
$00412C75 RUNERRORTOEXCEPT, line 369 of W:/md/dev/freepascal/compiler/2.4.5/ rtl/objpas/sysutils/sysutils.inc
$00407C5E HANDLEERRORADDRFRAME, line 901 of W:/md/dev/freepascal/compiler/2. 4.5/rtl/inc/system.inc
$00406802 TOBJECT__FREE, line 165 of W:/md/dev/freepascal/compiler/2.4.5/rtl /inc/objpas.inc
$0040BAA1 EXE_ENTRY, line 376 of system.pp
Call trace for block $000562E0 size 16
$00412A87 RUNERRORTOEXCEPT, line 349 of W:/md/dev/freepascal/compiler/2.4.5/ rtl/objpas/sysutils/sysutils.inc
$00407C5E HANDLEERRORADDRFRAME, line 901 of W:/md/dev/freepascal/compiler/2. 4.5/rtl/inc/system.inc
$00406802 TOBJECT__FREE, line 165 of W:/md/dev/freepascal/compiler/2.4.5/rtl /inc/objpas.inc
$0040BAA1 EXE_ENTRY, line 376 of system.pp
Call trace for block $00056220 size 16
$00401796 main, line 104 of p2.lpr
$0040BAA1 EXE_ENTRY, line 376 of system.pp
$00407924 fpc_initializeunits, line 753 of W:/md/dev/freepascal/compiler/2.4 .5/rtl/inc/system.inc
$0040BAA1 EXE_ENTRY, line 376 of system.pp

W:\dev\tests\interfaces\p2>

Graeme Geldenhuys
to Felipe Monteiro de Carvalho: I don't understand why it would be wrong. For me it seams correct and similar to this…
Felipe Monteiro de Carvalho wrote

Should that example not mention that it will not work with any released FPC version to date? That example will only work with the "still to be released" 2.6.0 and later. Why Because current released FPC versions don't support the 'implements' functionality in classes - it was only added in FPC 2.5.1 (old trunk).

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/
Graeme Geldenhuys
to Marcos Douglas: On Thu, Sep 15, 2011 at 6:27 AM, Graeme Geldenhuys Well, wrong just because the language…
Marcos Douglas wrote
Well, wrong just because the language is so but not because is ilogical, right?

Unless you are well versed with interfaces, I would not go there. Mixing object references and interface references (especially if reference counting is enabled) - you are bound to run into trouble.

Hm... interesting but what are the advantages?

None of the Windows'ism like COM's IUnknown, or Delphi's IInterface (alias to IUnknown) on any non-Windows platforms.

I want my code compiling in Delphi 7 too, so I can have problems with this?

Then stick with Delphi (Windows COM style) interfaces. I don't believe Delphi support any other style of interface (eg: CORBA, XPCOM etc).

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/
Graeme Geldenhuys
to Marcos Douglas: A class (TFoo) implements an interface (IFoo), but my class has more methods and…

You're original problem is exactly why I said mixing class instances and interface instance is looking for trouble. You better know what you are doing.

Attached are two examples of your program. One using CORBA interfaces, the other using non-reference counting COM style interface. Both work under 64-bit FPC 2.4.5 under Linux.

[tmp]$ ./testme
foo
Done
Heap dump by heaptrc unit
20 memory blocks allocated : 599/616
20 memory blocks freed : 599/616
0 unfreed memory blocks : 0
True heap size : 131072
True free heap : 131072

[tmp]$ ./testme_corba
foo
Done
Heap dump by heaptrc unit
20 memory blocks allocated : 599/616
20 memory blocks freed : 599/616
0 unfreed memory blocks : 0
True heap size : 131072
True free heap : 131072

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/
Jonas Maebe
to Graeme Geldenhuys: Should that example not mention that it will not work with any released FPC version to…
Graeme Geldenhuys wrote
Should that example not mention that it will not work with any released FPC version to date?

No.

That example will only work with the "still to be released" 2.6.0 and later. Why Because current released FPC versions don't support the 'implements' functionality in classes - it was only added in FPC 2.5.1 (old trunk).

That is incorrect. Several things have been fixed regarding the "implements" functionality in 2.5.x, but the basics already worked in 2.4.4. The example works fine when compiled with 2.4.4

Jonas

Marcos Douglas
to Graeme Geldenhuys: Should that example not mention that it will not work with any released FPC version to…
Graeme Geldenhuys wrote 9 lines
On 15/09/2011 17:12, Felipe Monteiro de Carvalho wrote: > > http://wiki.lazarus.freepascal.org/How_To_Use_Interfaces_to_write_less_code Should that example not mention that it will not work with any released FPC version to date?  That example will only work with the "still to be released" 2.6.0 and later. Why Because current released FPC versions don't support the 'implements' functionality in classes - it was only added in FPC 2.5.1 (old trunk).

I compiled the code using FPC 2.4.3.

Marcos Dougals

Marcos Douglas
to Graeme Geldenhuys: You're original problem is exactly why I said mixing class instances and interface…
Graeme Geldenhuys wrote 7 lines
You're original problem is exactly why I said mixing class instances and interface instance is looking for trouble. You better know what you are doing. Attached are two examples of your program. One using CORBA interfaces, the other using non-reference counting COM style interface. Both work under 64-bit FPC 2.4.5 under Linux.

Thanks, but...
The testme.pas has the TNonRefCountedObject class, but it isn't used. Both has the {$interfaces corba} directive.

Anyway, corba interfaces looks like solve my "problem" but I did more
tests, using corba:
1- If I change the Run procedure (see comments), how I release [f] variable?
procedure Run;
var
  f: IFoo;  // <<< now is an interface
  o: TObj;
begin
  f := TFoo.Create;
  //f.SetInfo('foo');  // do not exists in IFoo, but OK
  o := TObj.Create;
  o.Foo := f;
  writeln(o.Foo.GetInfo);
  o.Free;
  //f.Free  // <<< how I release this?
end;

I.e. this is the inverse of COM interfaces -- inverse of my problem too -- because I just could instantiate variables of concrete classes and not variables of type interface.
Maybe the union of both... or Am I saying a nonsense?

Marcos Douglas