Delphi macros: on recipes and power tips

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

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

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

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

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

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.