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 - [email protected]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal