Friday, April 27, 2007

Psst, a special price for you, my friend...

An item that may interest you is available at eBay now, click here.

PS: I still have another Delphi 2007 licence.

Tuesday, April 17, 2007

DN4DP#7: Inlined routines

This post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book. Last time we covered the new for in loop and the pattern for introducing enumeration support to your own classes. This post includes the section on the inlining support of the compiler.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

"Inlined routines

From Chapter 4 we know that the .NET Just-In-Time (JIT) compiler will automatically perform optimizations, including inlining small and simple methods at call sites. In addition to this JIT inlining, Delphi now supports explicit inlining of non-virtual routines, both in .NET and Win32.

function InlineMeToo(const Value: integer): integer; inline;
begin
Result := Value * 200 div 300;
end;

The inline directive is just a hint to the Delphi compiler that it should try to expand the code inline whenever the routine is called, at compile time. The exact rules of what and when it can be inlined differ slightly between the two platforms.

In .NET, Delphi inlining occurs at the IL level. This means that the IL code generated at the call site must obey CLR limitations and rules regarding member visibility. This limitation is mitigated by the less constrained JIT inlining that occurs at runtime.

The Win32 inlining support was made more aggressive in Delphi 2006 - now even methods that access private members can be inlined. Assembly (BASM) code cannot be inlined.

The rest of the inlining restrictions are common for both platforms and the most important ones are


  • no inlining across package boundaries
  • the inlined routine cannot access implementation section identifiers
  • the call site must have access to all identifiers used in the inlined routine


Note The last point means that unless the call site unit uses the units required by the routine, the routine cannot be inlined. When this happens, the compiler emits a hint like this

[Pascal Hint] InlinedRoutinesU.pas(14): H2443 Inline function 'InlineMe' has not been expanded because unit 'RequiredUnit' is not specified in USES list

To resolve the issue, add the missing unit name to the call site's uses clause.


The {$INLINE ON/AUTO/OFF} compiler directive can be used both at the definition and the call site. The OFF mode turns off all inlining. The default ON setting tries to inline routines explicitly marked inline. AUTO additionally tries to inline all small routines (consisting of less than 32 bytes of machine or IL code).




Caution Be careful with inlining too much code, the potential code size increase may actually reduce performance.




"


Update [May 12th 2007]:


In addition to the inline restrictions mentioned above I should have included the issue of implementation order.


If the inlined method is called from the same unit, the implementation of the inline method must have been "seen" by the compiler. In other words, the inlined implementation should preceed the call.

For example:

type
Foo = class
procedure A; inline;
procedure B;
end;

procedure TFoo.B;
begin
A; // note: Call to A is *not* inlined here
end;

procedure TFoo.A;
begin
// code
end;

Change this to:

type
Foo = class
procedure A; inline;
procedure B;
end;

procedure TFoo.A;
begin
// code
end;

procedure TFoo.B;
begin
A; // note: Call to A *is* inlined here
end;

Wednesday, April 11, 2007

Subversion in Delphi's Tools menu

Joe White writes about how to add Tools menu items with Subversion commands
here.

For some reason, the Submit button didn't work on his Comments page (running IE6), so I'll just write my comment here:

Nice, thanks!

I've been meaning to do the same thing for a while, but kept postponing it. Shame about the space-before-macro requirement in the Tools menu - you should probably log it in QC.

I didn't have Ruby installed so I wrote a simple online .Bat file instead:

"c:/program files/tortoisesvn/bin/tortoiseproc.exe" /command:%1 /path:%2 /notempfile

Then I create the Tools items with:
Program: c:\windows\system32\cmd.exe
Parameters: /C C:\SvnPas\Utils\Batch\SvnCmd.Bat diff $EDNAME $SAVEALL

Works fine!

Sunday, April 08, 2007

Hack#16: Published field RTTI replacement trick

We're back to fixing the interesting (learning-wise) problem of the flickering TProgressBar on Windows Vista. We have already looked at two relatively dirty and intrusive hacks that either overwrites the TClass reference inside each progress bar instance or overwrites the dynamic method table pointer of the original TProgressBar VMT. There are other related code page overwrite hacks that we may look at in the future (overwriting a virtual method slot, for instance), but this time we will look at a much simpler and (IMO) more elegant solution; tricking the compiler to replace the RTTI it generates for the component field on the for with a fixed version of TProgressBar.

As you know (if you have read the published fields and details articles), the IDE inserts component fields into the unnamed published section of your form class declaration as you drop components and controls on to the form at design time. Since the fields are published, the compiler generates RTTI for them - including the name, type and offset of the field.

For instance, creating a new form and dropping a couple of labels, a button and a progress bar on it, the IDE has generated the following code.

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls;

type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Button1: TButton;
ProgressBar1: TProgressBar;
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

end.

The published field RTTI includes the type of each field - encoded as a TClass reference. But how does the compiler know what TClass to use? By using the normal Pascal scoping rules, of course. TLabel and TButton both refers to the classes defined in the StdCtrls unit. For TProgressBar the compiler first checks the StdCtrls unit, but failing to find any TProgressBar class there, it looks in ComCtrls unit and finds it there.


Here lies the clue to our trick - we can simply add another unit to the interface uses clause, one that contains a fixed version of TProgressBar. We just have to make sure this unit is listed after the unit that contains the original TProgressBar (ComCtrls in this case). Let's get to it.


First we write (yet another version of) the fixed TProgressClass and put it inside an aptly named unit.

unit HVProgressBarVistaFix;

interface

uses Messages, ComCtrls;

type
TProgressBar = class(ComCtrls.TProgressBar)
private
procedure WMEraseBkgnd(var Message: TWmEraseBkgnd);
message WM_ERASEBKGND;
end;

implementation

procedure TProgressBar.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
DefaultHandler(Message);
end;

end.

Notice two things; the class must have the same name as the original class 'TProgressBar' and to avoid a compiler error message like



[Error] HVProgressBarVistaFix.pas(8): Type 'TProgressBar' is not yet completely defined


The class we inherit from has to be explicitly qualified with the unit it comes from 'ComCtrls.TProgressBar'.


With this simple unit in hand, we can fix the progress bar flickering simply by referencing the HVProgressBarVistaFix unit from all forms that uses progress bars (search your code for TProgressBar). Make sure it is listed last in the uses clause of the interface section.

uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, HVProgressBarVistaFix;

This ensures that the compiler inserts the TClass reference of our fixed version of the TProgressBar class into the published field RTTI tables of the form. When the streaming system reads the .dfm at runtime it uses these published field tables (read the details here) to find the TClass reference (or TComponentClass reference, to be exact) and uses it (and the virtual constructor of TComponent) to create the component. Because we have replaced the ComCtrls.TProgressBar class reference with our fixed version in HVProgressBarVistaFix.TProgressBar, it is our class that will be created.


With this simple kind of hack you can introduce any number of fixes, overriding virtual, dynamic or message methods, for instance. Look ma - no dirty hands! ;).

Tuesday, April 03, 2007

DN4DP#6: Enumerating collections

This post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book. Last time we showed how it is now possible to override the meaning of language operators. This time we'll cover the new for in loop and the pattern for introducing enumeration support to your own classes.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

"Enumerating collections

To make it easier and more convenient to enumerate over the contents of collections the traditional for statement has been extended into a for in statement. In general the for in syntax is

var
Element: ElementType;
begin
for Element in Collection do
Writeln(Element.Member);
end;

ElementType must be assignment compatible with the type of the actual elements stored inside the collection. The collection must implement the enumerator pattern or be of an array, string or set type. You only have read access to the Element itself, but you can change any properties and fields it references.

All .NET collections and most VCL container classes like TList and TStrings implements the required pattern, so now you can transform old code like

var
S: string;
i: integer;
begin
for i := 0 to MyStrings.Count-1 do
begin
S := MyStrings[i];
writeln(S);
end;
end;

into the simpler, less error prone, but equivalent

var
S: string;
begin
for S in MyStrings do
writeln(S);
end;

To enable for in for your own collection classes, you need to implement the enumerator pattern. This involves writing a GetEnumerator function that returns an instance (class, record or interface) that implements a Boolean MoveNext function and a Current property. In .NET you can also achieve this by implementing the IEnumerable interface. In Win32 these methods must be public.

type
TMyObjectsEnumerator = class
public
function GetCurrent: integer;
function MoveNext: Boolean;
property Current: integer read GetCurrent;
end;
TMyObjects = class
public
function GetEnumerator: TMyObjectsEnumerator;
end;

The EnumeratingCollections project demonstrates the differences between the old manual enumeration loops and the new for in loops. It also includes an example of how to write your own classes that support for in enumeration."



Copyright © 2004-2007 by Hallvard Vassbotn