Joe White’s Blog

Life, .NET, and Cats


Archive for the ‘Programming’ Category

DUnitLite 0.5 released

Saturday, May 10th, 2008

Version 0.5 of DUnitLite is now available. This is a minor enhancement release.

Download it here: DUnitLite 0.5

What’s new in 0.5

  • Should.ReferTo now works with interfaces, and can mix and match between objects and interfaces. It also correctly works around the interface reference-equality problem.
  • Added Given as an alias for Specify. So if you want to document your assumptions at the beginning of your test, you can use Given.That(...) to improve readability.
  • Added Should.[Not.]Be.False and Should.[Not.]Be.True.
  • Added Should.[Not.]Be.Null and Should.[Not.]Be.Assigned. Both work with either objects or interfaces.
  • Added Should.[Not.]Be.GreaterThanOrEqualTo and Should.[Not.]Be.LessThanOrEqualTo, as aliases for AtLeast and AtMost.

Interfaces and reference equality: beware

Saturday, May 10th, 2008

In Delphi for Win32, you can’t necessarily use the “equals” operator to tell whether two interfaces both point to the same object. The following code:

var
  Foo: IFoo;
  Bar: IBar;
begin
  Foo := MyObj as IFoo;
  Bar := MyObj as IBar;
  Assert(Foo = Bar);

will fail — even though both Foo and Bar point to the same object.

Let’s look at why that happens, and what you can do if you want a comparison that would succeed.

Memory layout of an interface reference

Note: If you don’t care about the technical details, and just want to know how to work around the problem, skip ahead to the “Workaround” section.

Let’s dig into the memory layout of an interface reference:

var
  Foo: IFoo;
begin
  Foo := TFoo.Create;

Memory layout of a Delphi/Win32 interface reference

An interface is a pointer to a pointer to an interface VMT. This means there’s a slot in the object’s instance data, just like any other instance field (except that it’s not visible in code), that automatically holds a VMT pointer. This field is automatically initialized during object construction. This is exactly the same way VMTs for Delphi classes are handled, except that the class’s VMT pointer is always at offset 0 within the block of memory allocated for the instance. Interface VMT pointers have to be at a nonzero offset, since offset 0 is already spoken for; they’re added at the end just like fields. (This will be familiar to those who remember Turbo Pascal objects, whose VMTs didn’t have to be at offset zero.)

And there’s the reason for the problem. Every interface you define gets a VMT all its own (even if it has the exact same methods as its parent interface, it still has its own VMT), so there has to be a slot in the instance data for the IFoo interface VMT, and another slot for the IBar interface VMT.

When you cast the object instance to IFoo, you’re really taking a pointer to the invisible “IFoo VMT pointer” field inside the object. When you cast to IBar, you’re taking a pointer to the “IBar VMT pointer” field. Those two are at different memory locations, so a reference-equality check will fail.

Bonus trivia: Because the interface pointer (which is also what’s passed as the implicit Self pointer when you call a method on the interface) isn’t at the same address as the object, the compiler has to generate a method stub that does a Self-pointer fixup, then call the actual method. The interface VMT points to this thunk method, not to the actual method body. You always wanted to know that, didn’t you?

A failed workaround: changing the variable types

So if the problem is the variables being of different types, let’s just make them both the same type:

var
  Foo: IInterface;
  Bar: IInterface;
begin
  Foo := MyObj as IFoo;
  Bar := MyObj as IBar;
  Assert(Foo = Bar);

The Assert will still fail, even though both variables are of type IInterface! Why?

That’s because the Foo variable still points to the IFoo VMT pointer inside the instance. IFoo descends from IInterface (as all interfaces do), so an IFoo reference is assignment-compatible with an IInterface variable, and is stored as-is in that variable — still pointing to the IFoo VMT pointer.

So even though a reference may be stored in a variable of type IInterface, it does not necessarily point to the IInterface VMT pointer for that class. This becomes a particular problem when you add different interfaces to an IInterfaceList: they’re stored in a list of IInterface variables, but they’re not canonicalized to point to the IInterface VMT pointer. So, for example, TInterfaceList.IndexOf can give you the wrong answer, if you’re not careful about the types of your interface references.

The workaround

The fix is really simple: just as-cast both variables to IInterface. This normalizes them, and puts them on an even footing for comparison.

When you as-cast to an interface type, you get back a pointer to the interface VMT pointer for that exact interface — none of that weirdness with assignment-compatibility and putting an IFoo reference into an IInterface variable: when you cast to IInterface, by golly, you get an IInterface all the way. QueryInterface and SysUtils.Supports will also canonicalize to the interface you specify.

(You could even use something other than IInterface, as long as you know both instances will support it. But every interfaced object can be cast to IInterface, so using IInterface makes for a nice general solution.)

So to revisit the code at the top of the article, the correct way to compare the interfaces would be something like this:

var
  Foo: IFoo;
  Bar: IBar;
begin
  Foo := MyObj as IFoo;
  Bar := MyObj as IBar;
  Assert(Foo as IInterface = Bar as IInterface);

Now, the assertion will pass.

Released: Tyler v0.01 alpha

Sunday, May 4th, 2008

Tyler version 0.01 is now available for download. Currently there’s just a source distribution, but it includes compiled EXEs.

The feature set is pretty sparse at the moment, but what’s there, works. It’s the bare minimum that I decided would be worth releasing. Here’s a brief rundown:

  • Pluggable display libraries. You can select from “compatibility mode” (basic GDI via TCanvas), Asphyre eXtreme, and (un)DelphiX (but see note 1 below). Windowed mode only — no fullscreen support in this version.
  • Graphics. These are primitive — everything is a solid-color square. Walls are light gray, floors are dark gray, the hero is yellow, the wandering NPC is white, and that’s all you get. (Well, except for black.)
  • Movement. You can use the cursor keys to move the hero around the map. There’s nothing yet to make you stop when you run into a wall or the NPC, though.
  • Map editing. TylerEdit.exe lets you edit the map and save your changes. It’s primitive, but it works. (But see note 2 below.)

Currently you can only have one map. Later you’ll be able to have lots of them — a world map, a map for each town, a map for each dungeon level.

Some stuff is hard-coded in this version, like the initial position of the hero, and the position and movement of the NPC. This will be improved in the next few versions.

Note 1: The license agreement for (un)DelphiX does not allow me to redistribute its source code. So, the source distribution is (un)DelphiX-less. But it still compiles: if you download and compile the source distribution, (un)DelphiX support will be automatically disabled. To enable (un)DelphiX, see the notes in the vendor\undelphix directory.

Note 2: The map file format will change in future versions. Any maps you create in this version probably will not work in v0.02 and later.

What’s next

The next few releases will be working toward a major milestone: a finished game. Not a long game — it’ll probably take less than a minute to play from start to finish. And not a very exciting one. In fact, it’ll be pretty stupid. But it’ll be playable, and everything will be editable in the game editor. Stay tuned for more news of… Stupid Quest.

Delphi IDE tricks: Return to previous editor tab

Thursday, April 24th, 2008

In Delphi, Ctrl+Tab moves forward to the next editor tab. In Visual Studio, Ctrl+Tab is more like Alt+Tab, moving to the tab you were most recently using.

It looks like Delphi actually supports something similar to the Visual Studio behavior — just with a different keystroke. If you press Ctrl+B, Delphi pops up the list of files currently open in the editor, sorted by “most recently focused first” — and when the dialog opens, the second item in the list is selected.

Delphi's "Buffer List" window

In the screenshot, my current editor tab is SpriteSpecs.pas. Before that, I was working in Screens.pas, and before that, I had done a Project > View Source to edit Tyler.dpr. That’s not the order the tabs are listed above the editor — Ctrl+B shows them in the order I used them last, just like the Alt+Tab window list or Visual Studio’s Ctrl+Tab list. And notice that, when the dialog opens, the second item is selected by default.

So the equivalent of Visual Studio’s Ctrl+Tab would be Ctrl+B, Enter. The equivalent of holding Ctrl down and pressing Tab twice in Visual Studio would be Ctrl+B, Down arrow, Enter. Not bad. Not bad at all.

The only downside is, Ctrl+B only works when the focus is on the editor window. If the focus is in a docked window, MS Visual Studio is nice enough to let you Ctrl+Tab back to the editor. Delphi isn’t so thoughtful; there doesn’t appear to be any reliable way to use the keyboard to get the focus from a docked window back to the editor. (If the current editor tab is a form or frame, you can hit F12 once or twice. Otherwise, reach for that mouse.)

DUnit tricks: Getting stack traces for test failures

Sunday, April 13th, 2008

DUnit has an option to use JCL to generate stack traces. The idea is that, whenever there’s an exception or a test failure, DUnit will show a stack trace right along with the failure message. The only problem is, it doesn’t work.

There’s no problem getting stack traces for unexpected exceptions. This is really useful; you get the address, unit name, method name, and even the line number of the line that threw an exception, together with the call stack that led to that code getting called. Immensely useful.

The problem is, you don’t get the same thing for test failures — even though Fail and CheckEquals, and even DUnitLite’s Specify.That, operate by throwing exceptions (they’ve got their own exception class, ETestFailure). You should be able to get a stack trace that shows the exact line of code that contained the failing assertion. In fact, we’re using older versions of DUnit and JCL at work, and we get stack traces just fine.

Unfortunately, stack traces for test failures are broken by default in the latest versions of DUnit and JCL. But there’s hope — notice that I said “by default”. Below, I’ll tell you how to fix that default.

Enabling DUnit stack tracing

First of all, here’s how to get DUnit to show stack traces in the first place.

You’ll need to download the source code for both DUnit and the Jedi Code Library. (Recent versions of Delphi ship with an older version of DUnit, but I only tested this with the latest version.)

Add the DUnit, JCL, JCL\Common, and JCL\Windows directories to your project’s search path.

Then make the following changes in Project > Options:

  1. On the Directories/Conditionals page, set “Conditional defines” to: USE_JEDI_JCL
  2. On the Linker page, set “Map file” to “Detailed”.

Now write a test that throws an unexpected exception, compile, and run. Here’s a contrived example, but it’ll give you an idea of what it looks like:

Screenshot of the DUnit GUI showing an exception stack trace

Enabing stack tracing for test failures

We don’t get unexpected exceptions in our tests very often. More often, it’s test failures. And when we have more than one assertion in the same test (not the ideal, but it happens a lot), sometimes it’s hard to know which assertion failed. Or rather, it’s always easy to know, if you have stack traces.

The problem is with JCL’s exclusion list. The latest version of JCL keeps a configurable list of exception types that it shouldn’t generate stack traces for. Seems like a reasonable feature. But the JCL and DUnit teams made three important design decisions, at various points in time:

  1. JCL’s exclusion list, by default, contains one class: EAbort.
  2. JCL ignores not just the classes in the exclusion list, but any of their descendant classes as well.
  3. DUnit’s ETestFailure descends from… yep, you guessed it, EAbort.

Put all three together, and stuff doesn’t work.

But, all that said, it’s easy to work around. Just add the following code to your project file, before you call one of the RunRegisteredTests routines:

uses
  ...,
  JclDebug,
  ...;

begin
  ...
  JclDebug.RemoveIgnoredException(EAbort);
  ...
end.

And bask in the goodness that is stack traces.

Delphi macro power tip: Moving to end-of-word

Thursday, April 10th, 2008

Today’s Delphi-macro power tip: moving the cursor to the end of the current word.

This isn’t an everyday need, but it comes up from time to time, and there’s not an immediately obvious way to do it. But it’s easy if you know the trick.

The big problem here is that Ctrl+Right arrow won’t work, because it moves to the beginning of the next word, not the end of this word. Sometimes you know what you expect the line to look like, and know you can just hit Left arrow twice and be where you want; but then again, sometimes you’d really rather just go to end-of-word and be done with it.

Here’s the trick: Ctrl+K, T, Right arrow, Left arrow.

Ctrl+K T selects the word under the cursor — it’s just like double-clicking. And the cursor is left at the end of the selection. So the cursor is left at the end of the word, right where you want it; all you need to do is clear the selection, which is easily accomplished by hitting Right and then Left.

Using macros as a second clipboard

Wednesday, April 9th, 2008

This isn’t really either a macro recipe or a power tip, but it’s a useful trick.

Sometimes I’ll run into a situation where I want two different things on the clipboard at once. Maybe I’m passing a filename through several layers of methods, and at each layer, I’m typing

; AFileName: string

twice (once in the class declaration, once in the method implementation), and

, AFileName

once (where the value needs to be passed along to the next guy). Wouldn’t it be nice if I had two clipboards, each with its own “paste” keystroke?

Once you think to use a macro as the second “clipboard”, it’s trivial. Select ; AFileName: string and copy it to the clipboard; then start recording a macro, type , AFileName, and stop recording.

Voila — two clipboards. Paste one with Ctrl+V, and the other with Ctrl+Shift+P.

Saving macros for later

Saturday, April 5th, 2008

Several people clued me in on a couple of ways to save macros for later. I haven’t had a chance to try either one yet (so someone correct me if I say something wrong about either), but they both sound great — and both are open-source.

(I’m really curious how they do it… I should look at their code sometime.)

GExperts Macro Library

GExperts includes a dockable Macro Library window. It lets you have multiple macros, lets you assign names and descriptions to them, and automatically saves them when you close the IDE.

I’m not sure where it saves them, though. This would be terrific if we could save them in revision control somehow — otherwise, with eight development machines, the macro I want would inevitably be on one of the other computers.

Keyboard macro manager on CodeCentral

TOndrej wrote a keyboard macro manager that you can compile into a design-time package and register in the IDE.

This one definitely can save the macros to a file, so could probably be used to share macros between computers.

Delphi macro power tip: Start of line

Friday, April 4th, 2008

Today’s Delphi-macro power tip: getting to the start of a line — for several different definitions of “start”.

This is most useful for macros that operate on a single line at a time, but it can sometimes be helpful in other sorts of macros as well.

Example

The Fields to Properties macro recipe starts with Home, followed by Ctrl+Right arrow.

Column 1

What “start of line” means will depend on what your macro does. Sometimes you just want to get your cursor to the beginning of the line. For this, plain old Home is what you want.

First alphanumeric character

Sometimes you want to get the cursor to the first non-blank character on the line. Unfortunately, I don’t know an easy way to do this in all cases.

But often, it’s good enough to get to the first alphanumeric character on the line. This worked well enough for the Fields to Properties macro recipe.

To do this, just do Home followed by Ctrl+Right arrow.

The downside is, if the line starts with punctuation — an open parenthesis, for example — Ctrl+Right arrow will skip right past it. This isn’t always what you want, and I don’t know of a clean and reliable way to get to the first non-whitespace character. But there are several things that can get you close.

Column N

Never underestimate the simplest thing. If, for every line you’ll be operating on, the interesting stuff will always start in column 5, don’t be afraid to do Home followed by pressing Right arrow four times. It sounds silly, but sometimes it’s just what you need.

Correcting after Ctrl+Right arrow

If you know that every line is going to start with an open parenthesis, followed immediately by an alphanumeric character, then simply do Home, Ctrl+Right arrow, and then correct for it with Left arrow to put the cursor before the parenthesis.

If you don’t know that every line will have that open parenthesis, then do whatever you think will get you closest most of the time — probably Home followed by Ctrl+Right arrow — but do it at the end of the macro, not the beginning. Then you can adjust the cursor position as necessary before you hit Ctrl+Shift+P.

Deleting leading whitespace

Okay, I do know of one way to put the cursor just before the first non-space character on the line, even if it’s punctuation. You don’t do it by moving the cursor — instead, you do it by moving the text, by deleting the leading whitespace! If you’re radically rewriting the line, sometimes you can get away with this.

If you know the line has leading whitespace, do Home followed by Ctrl+T.

I love Ctrl+T. It’s called “delete to end of word”, and it deletes everything from the current cursor position, through the end of the current word, plus any whitespace following it. It’s kind of like Ctrl+Shift+Right arrow followed by Delete, with one very interesting exception: unlike Ctrl+Right arrow, Ctrl+T stops at punctuation. So Ctrl+T, at the beginning of a line, will delete only the whitespace.

One catch is that, if you end up with a line that doesn’t have leading whitespace, you’ll delete the first word in the line! To avoid that, add whitespace first (yes, it’s cheesy, but it works): Home, Space bar, Home again (or Left arrow), then Ctrl+T.

Delphi macro power tip: Advance last

Tuesday, April 1st, 2008

Today’s Delphi-macro power tip:

Your macro should usually end with something that advances the cursor.

Important note: This only applies when you trust the macro to do what you want it to do. If you’re not sure you got it right, then you should advance the cursor manually between playbacks. But if you’re pretty confident that you did it right, this tip will speed you up greatly.

Example

Take a look back at the Fields to Properties macro recipe. The last step, before ending the macro recording, was pressing Down arrow.

Why?

You wouldn’t record a macro for something you only do once. The whole point is to do something to the code, then do it again somewhere else.

If you can automate the thing about finding the next “somewhere else”, then playing back is much faster. You can just hit Ctrl+Shift+P repeatedly.

Why at the end?

So it makes sense to put cursor advancement inside the macro. Why at the end, rather than at the beginning?

You can do either, but I’ve found that putting it at the end usually works better, because you can see the next place it’s going to run, before you run it.

Let’s say you’re transforming a long list of field declarations into properties. What if there are comments in the middle of that list? Your macro probably won’t do the right thing if you run it on a comment line. But if you advance at the end, there’s no problem: after you play the macro on the previous line, you’ll see that the cursor is now on a comment line. All you need to do is press Down arrow before you hit Ctrl+Shift+P again.

If you advanced the cursor at the beginning of the macro, the editor window might not have scrolled the next line into view yet (especially true if you’re operating on multi-line constructs). And if you’re moving by an irregular amount (e.g., with F3), you might not even know the next place it’s going to play back. So all you can do is hit Ctrl+Shift+P, realize that it screwed up, hit Undo, figure out where you need to move the cursor so it’ll be in the right spot after it advances, and try again. Advancing at the end is just simpler.

Advancing at the end also makes it easy to tell when your work is done. If you’re operating on a list of fields, you stop hitting Ctrl+Shift+P as soon as you end up on a line that’s not a field anymore. When you advance at the beginning of the macro, it’s harder to tell when you’re done.

Horizontal vs. vertical

When you’re writing a line-oriented macro, you’ll generally have both horizontal and vertical positioning to do. Just because you do your vertical positioning at the end of the macro, doesn’t mean you need to do the horizontal positioning there as well.

It really comes down to confidence. If you know that, for every line you’ll be operating on, Home and Ctrl+Right arrow will get you to the place you want to start, then put the horizontal positioning at the beginning of the macro. But if you want to be able to see the horizontal position before you play back, put it at the end.

Doing it twice

If you do your horizontal positioning at the end of the macro, there is one downside: the horizontal positioning, at least, usually needs to be done twice. You need to get the cursor to the right spot before you start recording, and again at the end of the macro.

If your horizontal positioning was done with Home followed by Ctrl+Right arrow, the sequence would look like this:

  1. Press Home.
  2. Press Ctrl+Right arrow.
  3. Press Ctrl+Shift+R to start recording.
  4. Press Down arrow to move to the next line.
  5. Press Home.
  6. Press Ctrl+Right arrow.
  7. Press Ctrl+Shift+R to stop recording.

If you trust your horizontal positioning, then don’t bother repeating it; just put it at the start of the macro. It makes life a bit simpler.

Not just Down arrow

Ending with Down arrow only makes sense when you’re operating on a line at a time. If you’re writing a different kind of macro, you need a different kind of advancement.

  • If your macro operates on each value in a list (e.g., say you’re changing the Hungarian prefix on each value in an enum), you might end with Ctrl+Right arrow.
  • If your macro operates on a bunch of empty method bodies (e.g., say you’ve just Class Completed a bunch of methods, and want to insert code inside each one to throw an exception), you might end by pressing Down arrow five times — just the right amount to usually get you to the blank line in the next method body. You can always adjust the cursor before the next Ctrl+Shift+P if, for example, the next one is six lines away because its parameter list wrapped onto two lines.
  • If your macro operates on each place a particular method is called, you might end with F3. (Do your Find before you start recording the macro — it doesn’t work properly if you do a Find while you’re recording. But F3 — Find Next — works quite nicely in a macro.)

Joe White's Blog copyright © 2004-2008. Portions of the site layout use Yahoo! YUI Reset, Fonts, and Grids.
Proudly powered by WordPress. Entries (RSS) and Comments (RSS).