Wednesday, January 19, 2005

Interface-to-interface casts

When you already have an interface reference and want to (try and) convert it into another interface reference, you have to use some kind of cast or conversion function. Just as with object-to-interface casts, there are a number of options.

The possibilities are hard-casts, as-casts, is-checks, the Supports function and the QueryInterface method. As we shall see not all of these are available in both Win32 and .NET - and some of them have different behaviour.

Interface as-casts
In Win32, if you try to as-cast to an interface that is not implemented, an exception is raised. In Delphi 8 for .NET, nil is returned instead. The behaviour should be the same on both platforms. This bug has been fixed in Delphi 2005.

Interface hard-casts
The compiler allows hard-cast syntax from an interface reference to another interface in both Win32 and .NET. While the .NET cast is safe and performs a logical conversion (returning nil if it fails), the Win32 cast is completely unsafe and does normally not do what you want. You are basically telling the compiler that the interface reference on the right side already contains a reference of the cast-to interface type (and not the declared type). In general, this type of cast is not currently useful in Win32.

Interface is-checks
For some reason, in Win32, is-checks are not supported on interfaces. To check that an interface reference implements another interface, you have to use Supports or call QueryInterface directly. .NET however, does support is-checks on interface references.

Supports function
Both the Win32 and .NET platforms support (sic) using Supports to check that an interface reference implements another interface - and to return this new interface reference. On .NET the Supports function has the same run-time issues as when casting from Object to Interface (in fact, it is exactly the same Supports function overload that is used - any interface reference is compatible with TObject); it is relatively slow.

QueryInterface
Specific to Win32, all interfaces have a QueryInterface method inherited from the base interface IInterface (or IUnknown). Under the hood, the Supports function (and even the is and as operators) calls QueryInterface. There is nothing stopping you from calling QueryInterface directly, of course, but then you have tied yourself to Win32 and the code needs to be changed to work in .NET.

Code sample
Let's write a small sample that demonstrates all the different ways to cast and check from one interface reference to another.

program TestIntf2Inf;
{$APPTYPE CONSOLE}

uses
SysUtils;

type
IMyInterface = interface
['{99D91C44-BCE7-4D35-A661-DE32E8AE56FC}']
procedure Foo;
end;
IMyInterface2 = interface
['{2E200094-0643-46C8-87AF-AAB0A1F5801D}']
procedure Bar;
end;
INotImplemented= interface
['{BAEE6F63-FF47-4877-9657-443B6D1555FA}']
procedure Zoo;
end;
TMyObject = class(TInterfacedObject,
IMyInterface, IMyInterface2)
procedure Foo;
procedure Bar;
end;

procedure TMyObject.Foo;
begin
Writeln(ClassName, '.Foo!');
end;

procedure TMyObject.Bar;
begin
Writeln(ClassName, '.Bar');
end;

procedure Foo(const I: IMyInterface);
var
MyInterface2: IMyInterface2;
NotImplemented: INotImplemented;
begin
// Win32 suppports as and Supports.
// Hard-cast is unsafe, is does not compile
// .NET suppports as, Supports, Hard-cast and is
MyInterface2 := I as IMyInterface2;
MyInterface2.Bar;
// hard-cast .NET: safe, returns nil on failure
// hard-cast Win32: Unsafe, compiles but may crash/fail
MyInterface2 := IMyInterface2(I);
// .Win32: Calls TMyObject.Foo!
// .NET: Calls TMyObject.Bar
MyInterface2.Bar;
try
NotImplemented := I as INotImplemented;
if not Assigned(NotImplemented) then
writeln('D8 .NET bug: as returns nil'+
' - should raise exception');
NotImplemented.Zoo;
except
{$IFDEF Win32}
on E: EIntfCastError do
{$ENDIF}
{$IFDEF CLR}
on E: InvalidCastException do
{$ENDIF}
writeln('As designed: ', E.ClassName, E.Message);
on E: Exception do
writeln('Bug: ', E.ClassName, E.Message);
end;
// Supports works in both Win32 and
// .NET, but is relativly slow in .NET
if Supports(I, IMyInterface2, MyInterface2) then
MyInterface2.Bar;
{$IFDEF CLR}
// intf is intf is not supported in Win32
if I is IMyInterface2 then
writeln('interface is interface suppported in .NET');
{$ENDIF}
{$IFDEF Win32}
if I.QueryInterface(IMyInterface2, MyInterface2) = 0 then
MyInterface2.Bar;
Writeln('QueryInterface supported in Win32');
{$ENDIF}
end;

var
I : IMyInterface;
begin
I := TMyObject.Create;
try
Foo(I);
except
on E: Exception do
writeln(E.Message);
end;
readln;
end.


This code should compile and run from D7, D8.NET, D2005 Win32 and D2005 .NET. The following is the output in each case.


Output Delphi 8 .NET:
TMyObject.Bar
TMyObject.Bar
D8 .NET bug: as returns nil - should raise exception
Bug: NullReferenceExceptionObject reference not set to an instance of an object.
TMyObject.Bar
interface is interface suppported in .NET


Output Delphi 2005 .NET:
TMyObject.Bar
TMyObject.Bar
As designed: InvalidCastExceptionSpecified cast is not valid.
TMyObject.Bar
interface is interface suppported in .NET


Output Delphi 2005 Win32 and D7:
TMyObject.Bar
TMyObject.Foo!
As designed: EIntfCastErrorInterface not supported
TMyObject.Bar
TMyObject.Bar
QueryInterface supported in Win32


Specifically notice that the D8 as-cast bug has been fixed in D2005 and that Win32 hard-casts have strange effects, calling the wrong method! IMO, this is not a bug, but a consequence of Win32 hard-cast semantics.


When hard-casting on Win32 you are essentially telling the compiler; "Forget about the declared type of this variable and treat it like it was this type instead". You have to know what you're doing. I really cannot see any case were the current Win32 interface-to-interface cast is useful, so maybe Borland could beef up the dcc32 compiler to make it work like it does in dccil in a future version…? Determined hackers would still be able to do a raw binary cast by casting to Pointer first.


Hard-casting on .NET is still safe and normally performs a logical conversion, not a binary re-interpretation.

FastCoder's 2004 New Year's Speech

The fine FastCoder hackers residing over in the borland.public.delphi.language.basm newsgroup have summarised their activity in the past year. The highlights are:

  • John O'Harrow is the contributor of the most and best FastCode challenge entries (winning a Delphi 2005 Pro)
  • Three FastCode routines were included in the updated Win32 RTL in Delphi 2005 (CompareText, Int64Div and FillChar)
  • 16 challenges were completed
  • The best AnsiStringReplace routine is now some 17 times (!!!) faster than the Delphi RTL StringReplace function
  • A new major challenge is writing an improved memory manager (faster, less fragmentation, better multi-CPU performance, using less memory)
  • A 64-bit version of Delphi is desired

Read the whole speech here. Keep up the good work, FastCoders!

Tuesday, December 28, 2004

Delphi vs C#: Destructors

Delphi's implementation of destructors is very different from C#'s. This means that if you are translating C# code samples to Delphi, you have to be careful with how you convert C# destructors. A question from Ray in the delphi.rtl.dotnet newsgroup illustrates this problem:

"Subject: Garbage collector bug in Delphi.NET but not C#?
Hi there
In testing the Garbage collection in Delphi.NET I found the following.
Delphi.NET code

//******************** 
type
TDemo = class
public
class var Instances: integer;
constructor Create; virtual;
destructor Destroy; override;
end;
//futher down
procedure TWinForm.Button2_Click(sender: System.Object; e: System.EventArgs);
var
x: Integer;
a: TDemo;
begin
Text := 'Running';
for x := 1 to 100000 do begin
a := TDemo.Create;
a.Free;
end;
end;

procedure TWinForm.Timer1_Tick(sender: System.Object; e: System.EventArgs);
begin
Text := TDemo.Instances.ToString;
end;

procedure TWinForm.Button1_Click(sender: System.Object; e: System.EventArgs);
var
x: Integer;
a: TDemo;
begin
for x := 1 to 100000 do begin
a := TDemo.Create;
end;
end;

{ TDemo }

constructor TDemo.Create;
begin
inherited;
Inc(Instances);
end;

destructor TDemo.Destroy;
begin
Dec(Instances);
inherited;
end;
//********************

and the C#


//********************
  public class Demo
  {
    public static long Instances;
    public Demo()
    {
            Instances += 1;
    }
    ~Demo()
    {
       Instances--;
    }
  }
//Further down
  private void button1_Click(object sender, System.EventArgs e)
  {
    int Counter;
    Demo aDemo;
    for (Counter = 0; Counter < 100000; Counter++)
    {
      aDemo = new Demo();
    }
  }
  private void timer1_Tick(object sender, System.EventArgs e)
  {
    Text = Demo.Instances.ToString();
  }
//********************

Now the issue I have is with C# it will have 100 000 classes and then a few seconds later 37125 then click button and it's 2500 etc. Works like the help says...


BUT Delphi is "buggy". Button1 creates 100 000 and does NOT free even after multiple clicks, however button2 with the .Free frees instantly. So it works like Win32. Surely this is a bug (Not that I'm complaining I'd rather it free up memory when I say so).


Am I missing something?


Regards Roy"


This is not a bug - a Delphi destructor is not the same as a C# destructor.


A Delphi destructor implements the IDisposable pattern, while a C# destructor overrides the Finalize method of System.Object (a very rarely recommended practice). Look at the generated IL code from both compilers, and you will see the difference.


To make the comparison relevant, either change the Delphi code to override the Finalize method or change the C# code to implement the IDisposable interface. FWIW, the new C++/CLI standard implements C++ destructors as IDisposable implementations too. C# destructors are a mistake, IMO. Don't use them unless you know exactly what you are doing.


This triggered a follow-up question from Ray:



"I got the C# code from a MSCD.NET CD, and according to the demos / trainer material this is recommended? Besides VB.NET worked the same as C# so does "C# destructors are a mistake" hold true for VB.NET?


http://www.delphi.about.com/od/delphifornet/a/aa060104a.htm


"Every class in the .NET Framework inherits a method called Finalize. The bad news is that the GC calls the Finalize method (and therefore if overridden it should be protected) when the memory for the object is about to be freed. Since the method is called by the garbage collector, you have no control over when it is called.


The good news is that in Delphi for .NET, all objects implicitly implement IDisposable and redirect calls to Dispose to the Destroy destructor. This means that you do not need to implement Dispose yourself ... you can safely continue freeing resources (for objects/classes you develop) in the object's destructor "Destroy" method."


The part that threw me off was "you have no control over when it is called."


That's one of the reasons Finalize is generally not recommended. Another is that objects are kept alive much longer than needed.


Finalize is generally only needed for cleaning up unmanaged resources like file and window handles - and that should in 99% of the time be taken care of by small low-level wrapper classes defined in the .NET framework.


99% of the time you do not want to implement C# destructors in application-level code.


99% of the time(1) you do want to implement IDisposable in application-level code. Writing Delphi destructors makes this easy.


This artible by Brian Long is recommended reading:
http://bdn.borland.com/article/0,1410,29365,00.html


(1) The reason for this is that while finalizers are singular and automatically called by the GC (only the class holding references to unmanaged resource handles need finalizers), IDisposable requires a cascading implementation in wrapper objects. For instance, if a class A is implemented in terms of (by having a private reference to) a class B, and class B implements the IDisposable interface, the wrapping class A should also implement the IDisposable interface. This even includes cases where the implementation of A or B might change in the future to implement IDisposable.

Tuesday, November 16, 2004

Object-to-interface casts

There are several ways to check if an object reference implements an interface or not - but there are some platform differences. The as-cast works (almost) identically in both Win32 and .NET.

var 
MyObject: TMyObject;
MyInterface: IMyInterface;
begin
// ...
MyInterface := MyObject as IMyInterface; // may raise EInvalidCast
MyInterface.Foo;
end;

The platform difference is that on Win32, the declared object type must implement the IInterface (or IUnknown) interface (as TInterfaceObject does, for instance), while in .NET any object type will do. If the cast fails, an exception will be raised. This is often undesirable - often you need to treat objects differently depending on if they implement a specific interface or not, and handling exceptions is too slow and cumbersome. On .NET you can cast from any object to any interface, using a hard-cast syntax. If the cast fails, nil is returned instead.

var 
MyObject: TMyObject;
MyInterface: IMyInterface;
begin
...
MyInterface := IMyInterface(MyObject); // .NET specific
if Assigned(MyInterface) then
MyInterface.Foo;
end;

Currently, the Win32 compiler does not support this cast in a generic way. It does support it in the specific case when the cast can be determined to be valid at compile-time. This can be done when the declared object type statically implements the given interface. If this is not the case, the cast fails with a compile-time error; [Error]: Incompatible types: 'IMyInterface' and 'TInterfacedObject' To check if an object supports an interface without raising exceptions, you can first cast it to IInterface then call the QueryInterface method. This only exists on Win32:

 Intf := IInterface(MyObject); 
if Intf.QueryInterface(IMyInterface, MyInteface) = 0 then
MyInterface.Foo;

The Supports routine in the SysUtils unit gives is a more convenient and cross-platform way of doing the same thing:

 if Supports(MyObject,IMyInterface, MyInteface) then 
MyInterface.Foo;

Supports is also available on .NET, but it is implemented in a way that makes it much slower than using the (currently) .NET-specific hard-cast. This is because the .NET version is implemented in terms of the new 'type of interface' construct:

type 
TInterfaceRef = type of interface;
...
function Supports(const Instance: TObject; const IID: TInterfaceRef; out Intf): Boolean;
begin
Result := Instance is IID;
if Result then
Intf := Instance as IID;
end;

The 'type of interface' construct is pretty neat, actually. It allows you to pass any interface-type as a parameter to a method. This is used instead of a TGUID as an identifier or handle to the given interface. The Supports function itself compiles into the following IL:

.method public hidebysig static bool Supports(object Instance, [mscorlib]System.RuntimeTypeHandle IID, object& Intf) cil managed 
{
// Code Size: 50 byte(s)
.maxstack 3
.locals (
bool flag1,
[mscorlib]System.Type type1)
L_0000: ldarg.1
L_0001: call [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle([mscorlib]System.RuntimeTypeHandle)
L_0006: ldarg.0
L_0007: callvirt instance bool [mscorlib]System.Type::IsInstanceOfType(object)
L_000c: stloc.0
L_000d: ldloc.0
L_000e: brfalse.s L_0030
L_0010: ldarg.2
L_0011: ldarg.1
L_0012: call [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle([mscorlib]System.RuntimeTypeHandle)
L_0017: stloc.1
L_0018: ldloc.1
L_0019: ldarg.0
L_001a: callvirt instance bool [mscorlib]System.Type::IsInstanceOfType(object)
L_001f: brtrue.s L_0029
L_0021: ldarg.0
L_0022: ldloc.1
L_0023: call object Borland.Delphi.Units.System::@CreateCastException(object, [mscorlib]System.Type)
L_0028: throw
L_0029: ldarg.0
L_002a: castclass object
L_002f: stind.ref
L_0030: ldloc.0
L_0031: ret
}

Quite a bit of code!


With a little help from Reflector, I've found that it corresponds to something like this in Delphi code:

function Supports(Instance: TObject; IID: RuntimeTypeHandle; out Intf: TObject): boolean; 
var
InterfaceType: System.Type;
begin
Result := System.Type.GetTypeFromHandle(IID).IsInstanceOfType(Instance);
if Result then
begin
InterfaceType := System.Type.GetTypeFromHandle(IID);
if (not InterfaceType.IsInstanceOfType(Instance)) then
raise EInvalidCast.Create(SInvalidCast);
Intf := Instance;
end;
end;

Notice that the code is a little redundant. The precence of both the is-check and the as-check makes the compiler emit calls to GetTypeFromHandle and IsInstanceOfType twice each. The exception will never be raised in this method. So there is a little room for improvement here. I think that passing 'type of interface' parameters as System.Type instead of RuntimeTypeHandle would also make it a little more efficient - in most (all?) cases the call site has a Type reference anyway and first has to convert it into a RuntimeTypeHandle. The site calling Supports yields this IL:

   ldloc.1 
ldtoken Obj2IntfCastNET.IMyInterface
call [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle([mscorlib]System.RuntimeTypeHandle)
pop
ldtoken Obj2IntfCastNET.IMyInterface
ldloca.s obj2
call bool Borland.Vcl.Units.SysUtils::Supports(object, [mscorlib]System.RuntimeTypeHandle, object&)
stloc.0


By contrast, performing the same operation by using the object-to-interface hard-cast, gives this very simple and efficient IL:


    ldloc.s obj2
   isinst Obj2IntfCastNET.IMyInterface
   stloc.2


Can you guess which is faster? No prizes for this one! So you can use the cross-platform Supports function - which is fairly efficient in Win32, but relatively slow in .NET, or the .NET platform specific and efficient hard-cast. Most of the time, the slow .NET version of Supports does not really matter, but for some performance critical points, you might want to use conditional compilation to select the best solution for each platform, like this:

{$IFDEF WIN32} 
if Supports(MyObject,IMyInterface, MyInterface) then
{$ENDIF}
{$IFDEF CLR}
MyInterface := IMyInterface(MyObject);
if Assigned(MyInterface) then
{$ENDIF}
MyInterface.Foo;

I hope that a future version of the Win32 compiler can be extended to support hard-casts from object-to-interface with the same semantics as on .NET. The cast should return nil if the object does not support the interface (yes, this has been reported to Borland). In .NET you can also use the 'is' operator to check if an object implements an interface or not. This is not currently supported in Win32.

 if MyObject is IMyInterface then 
Writeln('Yup');

Friday, November 12, 2004

Machine Code Hacking

I'm not shy from performing the occasional hack, and feel comfortable working in the CPU view of the debugger. Microsoft hacker Raymend Chen seems to be in another league however. He claims he never uses source level debugging (its too mucked up by the optimizer anyway) and here he gives some tips on how to patch machine code bytes while debugging to make the debuggee do what you want. Useful for those low-level debugging sessions ;).

Tuesday, November 09, 2004

Rocket Scientist wanted!

Our company are looking for the best Delphi and C++ programmers out there. From our website: "Our highly qualified team of developers are looking for new colleagues. If you are an experienced developer looking for a challenging position in an informal organization we would very much like to hear from you." The details about the positions are available here (in Norwegian): "Rocket Scientist" - Borland Delphi / C/C++ So send us an email with your resumé - or tips your favorite Delphi or C++ guru... :) Updated: The job advert is now available in English: http://www.infront.no/pdf/jobad_programmer.pdf

Monday, November 08, 2004

Delphi 2005 Reviewer's Guide

Borland has just made a very comprehensive review guide for Delphi 2005 available. It is written by Cary Jensen and can be downloaded here: http://www.borland.com/products/white_papers/ pdf/delphi_2005_reviewers_guide.pdf If you wonder what's new and how the new features look like, check it out.



Copyright © 2004-2007 by Hallvard Vassbotn