Experiencing Delphi 2005
published: Sun, 10-Apr-2005 | updated: Thu, 27-Oct-2005
A couple of weekends ago I completed another article for The Delphi Magazine, this one on Dependency Injection and pico containers. For the article I made a good start on a pico container written in Delphi for Win32 (henceforth, called Delphi32). It wasn't all plain sailing, though.
Let me explain. These days, in my real work for Configuresoft, I'm involved in the business and data layers for their ECM product (within the company these are collectively known rightly or wrongly as the middle tier). The code is written in C# and .NET. The opportunities for Delphi coding are nil.
So, the only time I use Delphi these days is for my articles. I'm very much a light-weight Delphi programmer. You'd think, therefore, that I wouldn't be meeting the edge cases head-on where the Delphi 2005 IDE/compiler runs into problems.
For example, the code for this latest article has 600 lines, tops. OK, if you want to flatter me, 700 lines. It's split into 8 units, with one class per unit (which is how I like developing these days). Some of those units (which Delphi 2005 insists on calling "namespaces") are merely for the unit tests, so I'm pulling in the DUnit test framework as well. All in all, not a difficult set of code.
Here's a quick run-down of my experiences with Delphi 2005 as I developed this code.
1. Once during debugging, the debugger crashed so hard that Watson came up asking if I wanted to send the crash data to Microsoft. Delphi, on the other hand was deader than a doornail. I said yes: it somehow amuses me to send info about a Delphi crash to Microsoft. But what the heck? My code isn't that complex. It's a glorified DUnit test program is what it is. So why did the debugger crash?
2. This next one comes right from the Department of Petty Annoyances, the Enough to Make You Scream Out Loud branch. I had some code that was casting an interface pointer to one of another type with the as operator. The object behind this cast correctly implemented both interfaces, so the cast should just, you know, work. The compiler threw up this error on the cast: The operator is not applicable to this operand type. That's it. No indication as to which of the two operands it was talking about, or even the briefest of hints as to the real problem or how to fix it.
The help is useless; indeed worse than useless since it just seemed to say, in a very smarmy tone, that this error comes up most of all when silly C-based developers try and program in Delphi.
The thing is: this error has happened to me before in the same kind of situation. And because I program so infrequently in Delphi, I forget what the solution is. Yes, duh, it's my fault. <Slap on forehead> I should remember what these unintelligible error messages really mean. Fancy me daring to use another language in my job and not using Delphi day in day out, thereby forgetting the lore of Delphi. Or maybe I've evolved away from the race memory.
After some messing around, I work out (yet again) that it was because I hadn't declared GUIDs on the interface declarations. God, I hate Delphi32's implementation of interfaces. It sucks the big moose. It also friggin' doesn't help that the compiler generates these inane error messages that, although factually correct, are absolutely useless. How much more difficult would it be to generate an error message that says "The as and is operators require interface pointer operands to be declared with a GUID." The help could then go on to explain why: as and is use the _QueryInterface method, and this uses the GUID to uniquely identify the interfaces concerned and whether the object being cast implements them.
Either no one cares, or no one uses interfaces in Delphi32 because the implementation is so broken.
3. The automated refactoring support ROCKS. I cannot even imagine writing code these days without two refactorings: Rename Identifier and Extract Method. The Delphi IDE provides these and they just work just great.
Just as an example on how to use the first of these. When I code these days, I hate having to stop to think about the names of variables, classes, etc. I'd rather just type in the first most obvious thing that comes into my head just so that I can continue with the code flow. Later on, as I review the code looking for refactorings, one of the refactorings I do is the "that's a silly name for this variable, I can see more clearly what it's for now, so let's rename it" type.
After some problems with the Declare Variable refactoring I'm now ignoring it (I still think the language should be evolved to use in-line variable declarations as well as the old var blocks). It's a shame that Extract Interface is not there (but given Delphi32's less-than-stellar support for interfaces, perhaps it's a good idea).
4. Talking of this less-than-stellar support, I was annoyed after writing most of the code for the pico container to use interfaces I find out that the property setter routines in the TypInfo unit don't support properties that are interface pointers. How long has Delphi had interfaces? Since Delphi 3? And how long has TypInfo been available? Since Delphi 2? And we still can't do set a property that's an interface. Bloody great, that. Or maybe I'm the only one who's ever wanted to do this. Hallvard Vassbotn, watch out!
Well, I'll be. With regard to item 4, Hallvard has just emailed me to say that there *is* a property setter (and getter) for interface properties in both D7 and D2005. I'm completely puzzled by this: I looked for the damn things when I hit that particular problem. I wrote the code in D2005, so you'd think I'd be searching through the correct TypInfo source file. Dunno, I'm flummoxed. Either I was looking for the wrong keyword or I must be losing it...