This thread, and my own tests, showed that you need to assign GUIDs to
interfaces to have reliable "is", even for CORBA interfaces.

I'm wondering, would it be possible to improve here things on the
compiler side (at least only in FPC-specific modes or under some
modeswitch), to make things safer?

Propositions:

1. The question "X is IMyInterface", when "IMyInterface" does not have
a GUID, could fail to compile.

  Just like right now "Supports(X, IMyInterface)" does not compile
when "IMyInterface" does not have a GUID. This is better -- the
presence of GUID is checked at compile-time.

  Right now, the "X is IMyInterface" seems like a trap, when the
interfaces can have no GUIDs. Two interfaces without GUIDs are treated
as equal, by the "is" operator. The example code that started this
thread shows how bad are the consequences. I made my own little
example, attaching.

2. I assume "as" operator has the same problem? So "X as IMyInterface"
would benefit from the same safeguard.

3. How about going further, and just making the GUID required at every
interface declaration? Since it's necessary to have reliable "is",
"as", "Supports"...

4. And how about going even more further, and just generate an
internal GUIDs (random, or based on a memory address) when no GUID is
explicitly specified? This way "is", "as", "Supports" always just
work. And it removes the need to do 1., 2., 3. above, of course.

  Is there a drawback to doing this, that I don't see?

Regards,
Michalis

P.S. I have added to
http://michalis.ii.uni.wroc.pl/~michalis/modern_pascal_introduction/modern_pascal_introduction.html
a section documenting it.
{$mode objfpc}{$H+}{$J-}
{$interfaces corba}

{ Simple test that without GUIDs, the "is" operator on interfaces
  returns "true" when a class implements *any* interface.
  Most likely because "is" simply compares GUIDs, and "no GUID" equals
  something like "GUID all filled with zeros".
  This means that GUIDs are necessary, for both CORBA and COM interfaces,
  if you want reliable "is". }

uses SysUtils, Classes;

type
  IMyInterface = interface
    procedure Shoot;
  end;

  IMyUnrelatedInterface = interface
    procedure Eat;
  end;

  TMyClass1 = class(IMyInterface)
    procedure Shoot;
  end;

  TMyClass2 = class(IMyUnrelatedInterface)
    procedure Eat;
  end;

  TMyClass3 = class
    procedure Shoot;
  end;

procedure TMyClass1.Shoot;
begin
  Writeln('TMyClass1.Shoot');
end;

procedure TMyClass2.Eat;
begin
  Writeln('TMyClass2.Eat');
end;

procedure TMyClass3.Shoot;
begin
  Writeln('TMyClass3.Shoot');
end;

procedure UseThroughInterface(I: IMyInterface);
begin
  Write('Shooting... ');
  I.Shoot;
end;

var
  C1: TMyClass1;
  C2: TMyClass2;
  C3: TMyClass3;
begin
  C1 := TMyClass1.Create;
  C2 := TMyClass2.Create;
  C3 := TMyClass3.Create;
  try
    if C1 is IMyInterface then
      UseThroughInterface(C1 as IMyInterface);
    { VERY WRONG: "C2 is IMyInterface" is evaluated as "true",
      and the "I.Shoot" call inside "UseThroughInterface" calls
      the "TMyClass2.Eat" method. }
    if C2 is IMyInterface then
      UseThroughInterface(C2 as IMyInterface);
    if C3 is IMyInterface then
      UseThroughInterface(C3 as IMyInterface);
  finally
    FreeAndNil(C1);
    FreeAndNil(C2);
    FreeAndNil(C3);
  end;
end.
_______________________________________________
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply via email to