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