Joe White’s Blog

Life, .NET, and Cats


Delphi macros: on recipes and power tips

March 31st, 2008

As I blog sporadically about Delphi macros, I’ll have two kinds of posts.

One will be recipes: detailed steps to get something done. Little or no theory — I won’t say much about why these particular steps. But I’ll talk about what the macro does, and when and why to use it. You’ve already seen this with my Fields to Properties macro recipe.

The other sort of post will be power tips. This is where I’ll dig into the theory and the building blocks — where I will get into “why these steps instead of something else”. When you would use Ctrl+Arrow and when something else would be better — that sort of thing. These are the things you can use to build your own power macros, and to tune the recipes to your needs.

Don’t be surprised if you also see some posts about when not to use macros — when some other IDE feature will serve you better. It’s all about the easiest way to get something done.

A complete list of all my macro-related posts — both recipes and power tips — is available through my blog’s “macros” tag.

The first power tips will be coming soon. As always, if you have questions or suggestions, fire away in the comments.

Delphi macro recipes: Fields to properties

March 30th, 2008

Today I’m going to share a Delphi IDE macro that I record and use several times a week: transforming fields to properties.

The basic idea is: You’ve just added seven fields to your class, and you want to make properties for all of them.

Why not use Class Completion?

If you declare the property first, Class Completion will add the field (and, optionally, the setter as well) for you, right? So why use a macro instead?

First and foremost, Class Completion for properties is horribly, horribly broken in Delphi 2006 and in BDS 2007. We use strict private in our code, which means we run up into the Delphi bug that makes Class Completion unusable. It also tends to complete to the wrong class. And I’m sure there are other bugs that I just don’t remember, because I never use it. I’ve learned better than to complete properties.

A macro also gives you more control. It’s easy to tune it to make read-only properties, or read and write the field with no SetXXX method. You can do that with Class Completion too (if you can get it to work), but you have to do extra typing for every property. With a macro, you just cut a swath through the code.

What it looks like

You start by copying your field declarations to the end of the public section (or wherever you want your properties to go). Then the macro transforms those field declarations into property declarations.

For today’s example, we’ll do read-only properties. So this:

FFoo: Integer;
FBar: string;
FBaz: TPoint;

will become:

property Foo: Integer read FFoo;
property Bar: string read FBar;
property Baz: TPoint read FBaz;

Note some limitations. With imagination, you can get around some of these, but the simple recipe I’ll give here won’t deal with anything complex.

  • You need one complete field declaration per line. This won’t deal with FFoo, FBar: Integer;.
  • It won’t turn array declarations into indexed properties, or anything uber-fancy like that. You need to write a method to make that work, and this is simple text transformation, not code generation.
  • This assumes no comments on the same line with the variable declarations.
  • The steps, as written, assume that you follow the Delphi convention of prefixing field names with F. If you have some other convention, alter the steps accordingly.

So, nothing too fancy. But macros make short work of simple, repetitive stuff. Let’s get started.

The setup

Highlight the lines with your field declarations. Copy them. Paste where you want your properties to end up (usually the end of one of the visibility sections, e.g. at the end of the public section).

Now put the cursor back on the first line you pasted. The macro will transform each line into a property declaration, then move to the next line.

The macro

  1. Press Ctrl+Shift+R to start recording.
  2. Press Home to go to the beginning of the line.
  3. Press Ctrl+Right arrow to go to the beginning of the field name.
  4. Press Ctrl+K, T to select the field name.
  5. Press Ctrl+C to copy the field name to the clipboard.
  6. Press Ctrl+Left arrow to go back to the beginning of the field name.
  7. Press Del to delete the F off the beginning of the field name.
  8. Type property and hit the Space bar.
  9. Press End to move to the end of the line, just after the semicolon.
  10. Press Left arrow to move just before the semicolon.
  11. Type Space bar, read, Space bar.
  12. Press Ctrl+V to paste the field name.
  13. Press Down arrow to move to the next line.
  14. Press Ctrl+Shift+R to stop recording.

Of course, with macros, you always do the first one by hand. The first line should now be a property declaration just like you wanted.

Now press Ctrl+Shift+P to play back the macro. The next line will be transformed from a field declaration into a read-only property declaration. Keep hitting Ctrl+Shift+P, and you’re in business.

One last cute trick: If there’s a line you want to skip — maybe you copied some comment lines along with the variable declarations — just hit Down arrow before your next Ctrl+Shift+P.

DUnitLite 0.4: bugfixes and Should.ReferTo

March 24th, 2008

Version 0.4 of DUnitLite is available. It fixes a couple of fairly major bugs (noted below) and adds a minor new feature.

Download it here: DUnitLite 0.4

What is DUnitLite?

For those who don’t remember, DUnitLite is my set of add-ons to DUnit to make assertions more readable, in the spirit of NUnit’s Assert.That(...) assertions (originally from NUnitLite). For example:

Specify.That(Foo, Should.Equal(45));
Specify.That(Bar, Should.Not.Equal(45));
Specify.That(Baz, Should.Be.AtLeast(40));
Specify.That(Quux, Should.Be.OfType(TSpecialQuux));

What’s new in 0.4

  • BUG: When you used TInsulatedTest (or TSpecification), DUnit was unable to count the number of passing tests — it just reported 0 tests run. Fixed.
  • BUG: When you used TInsulatedTest (or TSpecification), the test-suite nodes were labeled “TInsulatedTest” instead of the actual class name. Fixed.
  • NEW: Added Should.ReferTo(...) (reference equality check). Currently only works for objects, not interfaces.

Score one for dogfood

I’m using DUnitLite to write the tests / specifications for Tyler, so I’m quickly running into its bugs and limitations. This is the first time I’ve seriously tried to use DUnitLite in a project (its use of records-with-methods would reliably crash the pre-SR1 Delphi 2006 compiler we had at work at the time, and I never got back to it), so this is the first time I’ve actually noticed these gaping holes.

Fortunately, I wrote DUnitLite, so it’s really easy for me to fix the bugs. Don’t be surprised if you see a few more releases in the near future.

A politician who’s not a cardboard cutout

March 21st, 2008

I had been ready to give up on having a decent Democratic candidate this election. I had just about decided that, by the time either Obama or Hillary had beat the other into submission, they wouldn’t be someone I’d want to vote for.

But now I don’t know.

What happened? I read the text of Obama’s speech this Tuesday, “A More Perfect Union”. (I tried to watch it, but YouTube kept crapping out, so I had to settle for the written word.)

I won’t give a play-by-play (for that, read the speech, which is well worth the time). But there were several things about the speech that made a deep impression on me.

First was Obama’s reaction to Rev. Wright, his former minister who’s apparently been in the media lately, stirring up controversy with some incendiary racial and political comments. Obama didn’t agree with Rev. Wright’s comments, but neither did he distance himself from him, because “the truth is, that isn’t all that I know of the man”. He refused to dissociate himself from a man he’d respected for 20 years, a man he’d watched care for the sick and lift up the poor.

A typical politician would have issued a press release condemning the inflammatory remarks and distancing himself from the Reverend, and then stuck his head in the sand and hoped it would all go away. Obama didn’t do that. He faced the issue and took it on. And what’s more, he took it on with compassion and humanity.

I have to respect a politician who, first, shows that he’s able and willing to handle challenges, even controversial ones, even during election year; and second, who won’t abandon his longtime friends for mere political expediency.

Then there was the fact that he saw both sides of the issue. He talked about the things that contributed to Rev. Wright’s bitterness — poverty, oppression, erosion of families. He didn’t excuse the bitterness, but he asked us to see the reality underlying that bitterness — not to whitewash it or rose-tint it or hope it would go away, but to see what’s really there.

And he also talked about the white side of the issue. Whites who have never felt privileged by their race, who have worked their way up from nothing only to see opportunities given to blacks instead, whites who are told that their fear of urban crime is somehow racist.

Then he said we have a choice: we can sweep the issue of race back under the rug, and ignore it once again; or we can say, “Not this time,” and we can come together to fix the problems that are hurting everyone, blacks included, everyone else included.

“Not this time.” I really like that. As much as we might want to be super-crusaders, working tirelessly for Truth, Justice, and the American Way, in reality very few of us have the energy to toil endlessly for what’s right. We have families, jobs, fears, lives. We can’t honestly say “Never again” and know in our hearts that we mean it, that we’re willing to fight tirelessly for, most likely, the rest of our lives. (There’s that humanity thing again.)

But, even imperfect as we are, we can say, “Not this time.”

It was quite a speech. Of course, at the end of the day, speeches only mean so much:

I remember her comment: “He talks purty, don’t he?”

Her comment was only result.

– “The Moon is a Harsh Mistress”, Robert A. Heinlein

And it’s true: the speech was emotional, but at the end of the day, what does it mean about Obama’s qualifications for President? A President does need to be able to make purty speeches and influence people, but by itself, that’s not enough.

Well, I’d say, there’s one thing it tells us for sure: he’s willing to step up and deal with the tough issues, and he’ll treat the people involved with respect and humanity.

In fact, at this point, I’d say he’s the most human of all the candidates. He’s not just a slick politician, not just a pretty cardboard cutout. He’s not just parroting the party line. He’s a man, with both humility and dreams.

Voting for an actual human for a change, and not a politician? What an idea.

Indecision and pluggability

March 15th, 2008

There are several DirectX libraries for Delphi: (un)DelphiX, Asphyre, Pyrogine. I don’t know their relative strengths and weaknesses well enough to pick one.

So I decided not to decide. Instead, I wrote a pluggable display engine, so you can switch between different libraries at runtime.

I think it’s going to be an interesting strategy. Each time I want to add a new feature, I need to figure out how to implement it for every library. This has the obvious disadvantage of being more work. But on the other hand, I’ll learn all of the libraries really well — and that’s what sold me on the idea. I won’t just be playing with their demo apps, or making toy programs to poke at in isolation; I’ll be implementing real features, features I care about, in each of these libraries, and seeing how they stack up.

Down the road, I may drop some of these libraries, or add more. What’s really cool is that each time I look at the libraries and decide “yeah, I’ll still keep this one” and “I’ll quit supporting that one”, it’ll be an informed choice, from actually learning and using the library, and knowing its strengths and weaknesses for what I’m trying to do.

That said, I’m not very far down that road yet. The only drawing operation I have so far is “filled rectangle”. No fullscreen mode, and no Pyrogine support yet. But I do have smooth animation and frames-per-second display, a trivial demo, a couple of running apps, and a base to start from.

(As well as a few bugs…)

We’re hiring

March 7th, 2008

We’re looking for a good programmer, to join our agile team in Omaha, work with great people in our awesome bullpen, and play with our acres of magnetic whiteboard.

(This job is already posted on the “Jobs” sidebar, but since I have a vested interest in this one — i.e., I’d be working with you — I figured I’d call it out a little more.)

Delphi and/or C# experience would help, but we’re really just interested in someone who’s a good programmer. If you can write solid, maintainable OO code, we’d like to hear from you. If you, furthermore, live and breathe refactoring, grok single-responsibility principle in fullness, have a keen nose for code smells (and test smells), and can’t imagine writing code without automated tests, we’d absolutely love to hear from you.

A few highlights:

  • You’d be working in our open bullpen (those pictures are a bit dated — we have flat-panel LCDs now). Just about the whole team works in the bullpen, so if you have a question — whether for another programmer, QA, or one of the customers — you can usually just lean back and yell.
  • Dual monitors on every development workstation.
  • Fast PCs (4 GB battery-backed RAMdisks, anyone?).
  • Subversion for revision control. Atomic commits kick ass.
  • Management has a clue (programmers help make the release plan, rather than being held to an unrealistic plan made by a non-programmer).
  • Development is done via pair programming (two programmers at one keyboard), so there’s always someone to brainstorm with, knowledge spreads quickly, and code reviews aren’t after-the-fact.
  • Between QA, and our four automated-build machines running over 10,000 tests every time code is committed, feedback is usually pretty quick — it’s rare for a bug to go unnoticed for more than a few days or, more typically, a few minutes, so you don’t have to re-familiarize yourself with the code again.

The job posting goes into a fair bit of detail about the position, and about our team. If you have further questions about the job, feel free to post ‘em here, and I’ll answer what I can. If you want to apply, see the links at the bottom of the job post.

Comments on the survey #3: .NET

February 26th, 2008

We have legacy Win32 code, but are moving toward .NET. Nevertheless, on the survey, I said that CodeGear should focus on Win32 support.

Now, why would I say that? Because our .NET plans do not include Delphi.

There are several reasons for that.

Communication issues

Borland/CodeGear blew it when they promised us a .NET 2.0 version of Delphi, and then changed their minds without telling anybody. As late as February 2007 (and possibly later), CodeGear was still promising that they were “going to” release a .NET 2.0 compiler the previous year, even though they had scrapped those plans long before.

Guys, changing your minds is fine. You’re a business; you’ve got to do what’s right for the business. But if you do change your minds, you need to actually tell your customers. The sooner the better.

Keeping up (or not)

Delphi hasn’t kept up with .NET, and there’s no evidence that that will change. .NET 2.0 shipped in November 2005; Delphi got .NET 2.0 support in September 2007, almost two years later (and how long had .NET 2.0 been available in prerelease forms before its release, precisely so that other vendors could keep up?). And Delphi’s .NET 2.0 support just barely scratches the surface.

RemObjects, the guys behind Chrome (another Object Pascal language for .NET), had the right idea: just do the stuff that adds value (i.e., make a language), and let Microsoft do the grunt-work and heavy lifting involved in writing an IDE. Maybe that’s why Chrome has kept up with, and in some cases gone past, C#’s feature set. In fact, from what I can tell, it looks like Chrome had a production-level release with LINQ support before Microsoft did.

A small company like RemObjects or CodeGear should be able to be nimbler than Microsoft, should be able to release major innovations on a tighter schedule than the Microsoft behemoth. RemObjects has done that with Chrome. CodeGear hasn’t — not for .NET, anyway.

(To be fair, Chrome isn’t currently in our plans either.)

Incompatible frameworks

VCL.NET apparently isn’t compatible with the rest of the .NET world. You need to make a special wrapper before you can use a WinForms control in VCL.NET, and I have yet to see anyone breathe a word about whether it’s possible to go the other way and use VCL.NET controls in WinForms.

That means that VCL.NET is only a viable choice when you’re a group of Delphi programmers who will only ever work with other groups of Delphi programmers. If you’re ever going to work with C# programmers, hire a contractor, look for code on the Internet — or if you already have C# WinForms code of your own — VCL.NET is a non-starter. This may not be a big deal for everyone, but it is for us.

Refactoring tools

I’ve already talked about the lack of refactoring tools for Delphi. Visual Studio, on the other hand, has two big things going for it here. #1, it has a handful of built-in refactorings that actually work; and #2, it has ReSharper.

I’ve already talked at length about how much ReSharper rocks (see my series, “The 31 Days of ReSharper”). I filled 31 days just talking about version 2.5; I haven’t even had a chance to look at all the new stuff in 3.0 or 4.0 yet. Delphi doesn’t have anything that comes close.

(It may sound like I’m contradicting myself here. On the one hand, I’m complaining about VCL.NET locking us in to a single vendor; on the other, I’m singing the praises of ReSharper, an add-in that only works with Visual Studio. There’s one big difference, though: I can still compile my code without ReSharper.)

Buggy IDE

I could make a long, long list of the bugs in the Delphi IDE that slow us down on a daily basis. Visual Studio? Not so much.

Delphi has half-baked features (strict private is fabulous until you use Class Completion or the refactoring tools), regressions from previous versions (missing Help topics jump immediately to mind), minor annoyances that add up on an hourly basis (using the IDE makes the command-line compiler stop working), compiler bugs (like the spurious “unit Foo recursively uses itself” that’s fixed by doing a Build). Just off the top of my head. At least there aren’t as many outright crashes these days; thank heavens for small favors.

Don’t get me wrong: Visual Studio isn’t perfect. We’ve had some problems with it too. But far, far less often than Delphi.

No comparison

Frankly, there’s just no reason to use Delphi for .NET development — not when we could be using a stable IDE and kick-ass refactoring tools.

We cross-compile some of our Win32 code into .NET, and that will continue. But CodeGear, don’t put more effort into .NET for our sake. Not now. It would be too little, too late.

Comments on the survey #2: Refactorings

February 22nd, 2008

The survey had little room for comments, so here are mine, starting with refactorings.

Delphi’s refactorings are crap. They leave you with non-compilable code more often than not.

A few examples of brokenness off the top of my head:

  • Class Completion got broken in CDS 2007. It usually completes stuff into the wrong class.
  • Class Completion mangles your visibilities if you ever try to use strict private or strict protected.
  • Delphi’s refactorings still don’t work across units if you don’t have every single unit explicitly added to your project file, so a Rename will usually break the build.

And that’s just the brokenness, not the suckiness. Extract Method still won’t do functions, but that’s just an annoyance. Automatic variable declarations usually get the wrong type, but that doesn’t break code that already compiled. What’s utterly unacceptable is that there are cases where the “refactorings” actually make previously-working code stop compiling.

Pretty much our entire team just ignores Delphi’s refactorings, because we know they’ll usually break our code. Please, please, please, put some effort toward making the refactorings work. In this day and age, we should not have to refactor by hand.

Comments on the survey #1: Saving in the middle?

February 22nd, 2008

The Delphi survey did not take me the 20 minutes they claimed.

Guys, any chance the next survey could be savable and resumable? I’d love to be able to answer a few questions when I’ve got time, save, and then come back to it later from another computer and pick up where I left off.

If I could save and resume, I would’ve been done with the survey weeks ago. As it is, I had to wait until I had an uninterrupted chunk of time to devote to it.

If it’s important enough to comment, it’s important enough to test

February 14th, 2008

Several people disagreed when I said you should try to write code that doesn’t need comments. I’ll be addressing some of their thoughts — sometimes by saying they were right, but sometimes with constructive suggestions.

I’ll start with a couple of people who suggested that comments should try to explain code that was put in to fix a bug, or to explain the “tricky code” that you sometimes have to write.

Those are both scenarios where a comment isn’t enough. You need an automated test.

Tricky code

The best way to deal with tricky code is by not writing it. But I’ll assume you’re dealing with a case where some manner of tricky code is indeed the best way to get the job done.

Suppose you’re writing a Delphi constructor, and for some reason you need to put some code before the call to inherited Create. (That certainly qualifies as tricky.) Assuming there’s a reason why that’s necessary (and not just sloppiness), you should have an automated test that proves the resulting behavior is correct… something that will pass with the tricky code, and will fail without it. A test that would break if someone else (or you, for that matter) later misunderstood the importance of that ordering, and “fixed” it by moving the code after inherited Create. A test that would pass if someone found a less tricky way to accomplish the same thing.

Bug fixes

If you’re fixing a bug, you should definitely be writing an automated test, if at all possible. That way, if someone later makes a change that breaks the same thing again, you’ll know. As Brian Marick said (and we had posted on our wall for a while), “No bug should be hard to find the second time.”

Then look again

Once you’ve got your automated test, take another look. Is the comment still necessary?

Depending on your audience, maybe it is. As someone else pointed out, if you’re a library vendor, then comments have a lot of value.

But I often find that, with the test in place, the comment can just go away.


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).