inherited interfaces not seen by queryinterface / supports

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

inherited interfaces not seen by queryinterface / supports

David Emerson
Hi all,

I am a bit perplexed / disappointed to see that the Supports /
QueryInterface functions for interfaces are not behaving the way I would
expect them to behave, when using an interface that inherits from
another interface.

note, _obj_type_ might be TObject, TInterfacedObject, or TComponent.

{$mode objfpc}

type
   i_1 = interface
     ['{6C8CF001-733C-4A2D-B41F-3B3FF1D266B4}']
     procedure do_one;
     end;

   i_2 = interface (i_1)
     ['{E0F0FA05-96C3-4979-A58C-5FB5F1E37214}']
     procedure do_two;
     end;

   t_2 = class (_obj_type_, i_2)
     // i_2 inherits from i_1, compiler requires i_1 implementation
     procedure do_one;
     procedure do_two;
     end;

var
   two : t_2;

It seems that Supports (two, i_1) returns false -- even though Supports
(two, i_2) returns true, and i_2 inherits from i_1. QueryInterface
similarly fails (for COM interfaces)

It appears to me that the only way to get a proper i_1 result from these
functions is to define

   t_2 = class (_obj_type_, i_2, i_1)

This is redundant, and a burden when there is a hierarchy of inherited
interfaces defined in different places.

Is there some other way, some mode or switch or something, that would
make a class definition automatically include interface-supports for
inherited interfaces?

(Also, in the source below there are a couple Access Violation crashes
noted, any ideas why?)

Thanks!
~David.

Free Pascal Compiler version 3.0.0 [2015/12/05] for x86_64 (linux - debian)

Here's my full program:

program inherit_intf;

{$mode objfpc}{$H+}
{$macro on}

{$define corba}
//{$define tcomp}
//{$define tintobj}

{$ifdef corba}
   {$interfaces corba}
   {$define _intf_str_ := 'CORBA'}
{$else COM interface}
   {$interfaces com}
   {$define _intf_str_ := 'COM'}
   {$ifndef tcomp}
     {$define tintobj}
   {$endif}
{$endif}

{$ifdef tcomp}
   {$define _obj_type_ := TComponent}
   {$define _obj_str_ := 'TComponent'}
   {$define _create_param_ := nil}
{$else}
   {$define _create_param_ := }
   {$ifdef tintobj}
     {$define _obj_type_ := TInterfacedObject}
     {$define _obj_str_ := 'TInterfacedObject'}
   {$else}
     {$define _obj_type_ := TObject}
     {$define _obj_str_ := 'TObject'}
   {$endif not tintobj}
{$endif tcomp}


uses
   classes,
   sysutils;

type
   i_1 = interface
     ['{6C8CF001-733C-4A2D-B41F-3B3FF1D266B4}']
     procedure do_one;
     end;

   i_2 = interface (i_1)
     ['{E0F0FA05-96C3-4979-A58C-5FB5F1E37214}']
     procedure do_two;
     end;

   t_2 = class (_obj_type_, i_2)
     procedure do_one;
     procedure do_two;
     end;

   t_3 = class (t_2, i_1)
     end;

procedure t_2.do_one;
   begin
     writeln ('one');
   end;

procedure t_2.do_two;
   begin
     writeln ('two');
   end;

var
   two : t_2;
   three : t_3;
   res : i_1;

begin
   writeln (_intf_str_, ' / ', _obj_str_);
   two := t_2.Create (_create_param_);
   three := t_3.Create (_create_param_);
   writeln ('t_2 supports i_1? ', supports (two, i_1, res)); //
unexpectedly FALSE
   writeln ('t_3 supports i_1? ', supports (three, i_1, res)); // TRUE
   {$ifndef corba}
     writeln ('t_2 QueryIntf i_1? ', S_OK = two.QueryInterface (i_1,
res)); // unexpectedly FALSE
     writeln ('t_3 QueryIntf i_1? ', S_OK = three.QueryInterface (i_1,
res)); // TRUE for TComponent. CRASH - SIGSEGV for COM + TInterfacedObject
   {$endif}
   two := three;
   writeln ('t_3 (t_2 var) supports i_1? ', supports (two, i_1, res));
// TRUE for TComponent. CRASH - SIGSEGV with COM + TInterfacedObject
end.


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inherited interfaces not seen by queryinterface / supports

Graeme Geldenhuys-6
On 2016-10-27 02:13, David Emerson wrote:
>    t_2 = class (_obj_type_, i_2)
>      // i_2 inherits from i_1, compiler requires i_1 implementation
>      procedure do_one;
>      procedure do_two;
>      end;


A common misconception about how interfaces work. In fact, I don't
actually know why FPC and Delphi bother with Interface Inheritance,
because I simply don't see the point.

To make your "t_2" class support both interface, you need to specify
both in the class declaration. Even though i_2 inherits from i_1, both
i_1 and i_2 must be specified in the t_2 class.

Again, I don't know why it is done like this, and works very different
to class inheritance. Delphi 7 works exactly the same in this regard.
Maybe somebody with more knowledge on the subject could shed some light.

Here is a D7 example:

=======================================
program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  IOne = interface
  ['{32271879-1C00-4A02-A9C0-6948844028D6}']
    procedure One;
  end;

  ITwo = interface(IOne)
  ['{F3027177-6638-4984-B842-7D4E70299056}']
    procedure Two;
  end;

  {  If you only specify ITwo here, then the second Supports() call
     for i1.One will never execute. }
  TMyObject = class(TInterfacedObject, IOne, ITwo)
  private
    // IOne interface
    procedure One;
    // ITwo interface
    procedure Two;
  end;

procedure TMyObject.One;
begin
  writeln('One');
end;

procedure TMyObject.Two;
begin
  writeln('Two');
end;

var
  o: TMyObject;
  i1: IOne;
  i2: ITwo;
begin
  o := TMyObject.Create;
  if Supports(o, ITwo, i2) then
    i2.Two;
  if Supports(o, IOne, i1) then
    i1.One;
  i2 := nil;
  i1 := nil;

end.

=======================================


Regards,
  Graeme

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inherited interfaces not seen by queryinterface / supports

Graeme Geldenhuys-6
On 2016-10-27 03:47, Graeme Geldenhuys wrote:
> Again, I don't know why it is done like this, and works very different
> to class inheritance. Delphi 7 works exactly the same in this regard.
> Maybe somebody with more knowledge on the subject could shed some light.

I did some quick internet searching. According to Danny Thorpe (an ex
Borland employee I believe), the reason Delphi works so "weird"
regarding Interface Inheritance, is because Delphi is very Windows
centric, and initially Delphi Interfaces were COM compatible interfaces.
Microsoft made some screw-up in COM, and Delphi followed suite.

  http://stackoverflow.com/a/4380371


"The reason is because COM and ActiveX allow an implementation to
implement a descendent interface (your IChild) but deny the ancestor of
that interface (IParent). Since Delphi interfaces are intended to be COM
compatible, that's where this goofy artifact comes from." -- Danny Thorpe


Now my question to the FPC developers are... For a long time now,
Interface support does not always mean COM or ActiveX support, so why
still does FPC do the same as Delphi? And even worse, why does this same
behaviour happen in FPC's CORBA style interfaces - which has *nothing*
to do with COM or ActiveX???

Here as a FPC (CORBA style interfaces) example of the problem with
Interface Inheritance.

============================================
program Project1;

  {$mode objfpc}{$H+}
  {$interfaces corba}

uses
  SysUtils;

type
  IOne = interface
  ['{32271879-1C00-4A02-A9C0-6948844028D6}']
    procedure One;
  end;

  ITwo = interface(IOne)
  ['{F3027177-6638-4984-B842-7D4E70299056}']
    procedure Two;
  end;

  TMyObject = class(TObject, ITwo) //  <<---- IOne is not specified
  private
    // IOne interface
    procedure One;
    // ITwo interface
    procedure Two;
  end;

procedure TMyObject.One;
begin
  writeln('One');
end;

procedure TMyObject.Two;
begin
  writeln('Two');
end;

var
  o: TMyObject;
  i1: IOne;
  i2: ITwo;
begin
  o := TMyObject.Create;
  if Supports(o, ITwo, i2) then
    i2.Two;
  if Supports(o, IOne, i1) then  // this is never true
    i1.One;
  i2 := nil;
  i1 := nil;
  o.Free;
end.
============================================


In the Stack Overflow link I posted - further down the page - a person
named Jasper mentions that in is QueryInterface implementation bug where
QueryInterface on checks for valid Interface signatures in the top level
of the interface hierarchy, and doesn't actually check for inherited
interfaces.  Anyway, read Jasper's comment to see his exact words.

   http://stackoverflow.com/a/39496815


If nothing else, surely FPC's CORBA style interfaces should not
implement the COM style interface bug?

Regards,
  Graeme

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inherited interfaces not seen by queryinterface / supports

Marcos Douglas B. Santos
On Thu, Oct 27, 2016 at 1:12 AM, Graeme Geldenhuys
<[hidden email]> wrote:

> In the Stack Overflow link I posted - further down the page - a person
> named Jasper mentions that in is QueryInterface implementation bug where
> QueryInterface on checks for valid Interface signatures in the top level
> of the interface hierarchy, and doesn't actually check for inherited
> interfaces.  Anyway, read Jasper's comment to see his exact words.
>
>    http://stackoverflow.com/a/39496815
>
>
> If nothing else, surely FPC's CORBA style interfaces should not
> implement the COM style interface bug?

It is a "bug by design". But, I agree with you.
Actually, I think this is should be corrected in both modes (COM and CORBA).

Best regards,
Marcos Douglas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inherited interfaces not seen by queryinterface / supports

Graeme Geldenhuys-6
On 2016-10-27 12:42, Marcos Douglas B. Santos wrote:
> It is a "bug by design".

The worst kind!


> Actually, I think this is should be corrected in both modes (COM and CORBA).

Indeed, and even if it is fixed in FPC, it shouldn’t break any existing
code either. So it’s a win-win situation.

If nothing else, it definitely should be fixed for CORBA Interfaces, but
I highly recommend to the powers that be, to fix it for both interface
modes.

Regards,
  Graeme

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inherited interfaces not seen by queryinterface / supports

Marcos Douglas B. Santos
On Thu, Oct 27, 2016 at 10:18 AM, Graeme Geldenhuys
<[hidden email]> wrote:
>> It is a "bug by design".
>
> The worst kind!

Yeah, you're right.

>> Actually, I think this should be corrected in both modes (COM and CORBA).
>
> Indeed, and even if it is fixed in FPC, it shouldn’t break any existing
> code either. So it’s a win-win situation.

Win-win situation, I think so.

> If nothing else, it definitely should be fixed for CORBA Interfaces, but
> I highly recommend to the powers that be, to fix it for both interface
> modes.

+1


Marcos Douglas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inherited interfaces not seen by queryinterface / supports

Tony Whyman
In reply to this post by Graeme Geldenhuys-6
On 27/10/16 03:47, Graeme Geldenhuys wrote:
> A common misconception about how interfaces work. In fact, I don't
> actually know why FPC and Delphi bother with Interface Inheritance,
> because I simply don't see the point.
> To make your "t_2" class support both interface, you need to specify
> both in the class declaration. Even though i_2 inherits from i_1, both
> i_1 and i_2 must be specified in the t_2 class.

I must be missing something here because interface inheritance seems to
work fine for me e.g.

i1 = interface
  procedure Do1;
end;

i2 = interface(i1)
  procedure Do2;
end;

TMyObject = class(TInterfacedObject,i2)
public
   procedure Do1;
   procedure Do2;
end;

var intf: i2;
begin
   i2 := TMyObject.Create;
   i2.Do1; {seems to work for me}
End;

I have plenty of examples where an interface is inherited and includes
the inherited methods and properties.

I also have cases where e.g.

var SomeInterface: IUnknown;
begin
    SomeInterface := TMyObject.Create;
end;

that is the inherited interface is extracted from the object. There are
also useful cases, where e.g.

TMyObject1 = class(TInterfacedObject,i1)
public
   procedure Do1;
end;

TMyObject2 = class(TMyObject1,i2)
public
   procedure Do2;
end;

That is you can build an object hierarchy to parallel an interface
hierarchy.

I don't use the "Supports" primitive. - so maybe there is a bug in this
feature but otherwise, what is the problem with interface inheritance?

Tony Whyman
MWA
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inherited interfaces not seen by queryinterface / supports

David Emerson
In reply to this post by Graeme Geldenhuys-6
On 10/27/2016 05:18 AM, Graeme Geldenhuys wrote:

> On 2016-10-27 12:42, Marcos Douglas B. Santos wrote:
>> It is a "bug by design".
>
> The worst kind!
>
>
>> Actually, I think this is should be corrected in both modes (COM and CORBA).
>
> Indeed, and even if it is fixed in FPC, it shouldn’t break any existing
> code either. So it’s a win-win situation.
>
> If nothing else, it definitely should be fixed for CORBA Interfaces, but
> I highly recommend to the powers that be, to fix it for both interface
> modes.
>
> Regards,
>   Graeme
>

+1

Thanks as always

~David


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inherited interfaces not seen by queryinterface / supports

David Emerson
In reply to this post by Tony Whyman
On 10/27/2016 06:01 AM, Tony Whyman wrote:

> That is you can build an object hierarchy to parallel an interface
> hierarchy.
>
> I don't use the "Supports" primitive. - so maybe there is a bug in this
> feature but otherwise, what is the problem with interface inheritance?

Yes, the problem is with "Supports" and "QueryInterface".
All of the examples you gave work fine (I didn't test them but they look
like tests I've done)

The situation where "Supports" is needed looks more like this:

function do_something (t : tobject);
begin
   if supports (t, i_my_interface) then ....


So the situation is that we were passed an object, and we don't know
what it is; but if we learn that it supports a particular interface,
then we want to do something with that interface.

~David.



_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal