Just finished a miserable three day programming marathon, the sort of disaster that makes you question your compiler, your sanity, and whether mankind was intended to program. The explanation was a simple error – but one I’ve never made before.
I’m in the middle of a big project, refactoring the core of Tinderbox and moving it to a new platform. For the most part, this has gone swimmingly, and I think I’ll hit my August 1 milestone in June. Since I’m moving and testing things object by object, I occasionally need to make temporary test jigs and placeholder objects that do very little (or nothing) except stand where the real object would be.
I once went to a dress rehearsal in Göteborg. At one point, a fellow came on stage and explained that, tomorrow night, the King would say a few words, and so he stood in for the King and this would help prevent dancers from crashing into the Sovereign. These objects are just like that.
So, I have this complex family of objects called
ExportElements that are made by the
ExportElementParser. But I don’t have an
ExportElementParser yet, or any actual
ExportElements. But I’ve got things that expect to receive an
ExportElement, and some of those can be tested without having an actual working
ExportElements to give them. So I write up a little class that pretends to be an
ExportElement, and if you need to use the test objects, you can
All this is canonical, by-the-book, test driven development. I wrote the dummy object weeks ago, used it in a few places, forgot all about it.
Last Friday, it was time to hook up
ExportElementParser, the factory that makes various kinds of
ExportElements. They come in about 28 flavors. I was expecting it to be tricky. It went better than I hoped. There were snags — file system issues, some old syntax questions, a few references to things that weren’t implemented yet and had to be patched up with their own imaginary objects. But soon it compiled. And the tests ran.
So, now it was time to test the whole thing. Crash! That’s not too bad: things had been going too well, it was time for a problem. So, I broke things down into smaller steps. They worked – and then Crash! So, I went back a bit. It still crashed! All sorts of things would crash occasionally. It looked like a corrupt heap. I did diagnostics. I did tests. It didn’t make sense.
At one point, I refactored code from
ExportElementParser, where it crashed, to my test class, where it didn’t. That felt like a compiler error. Restart the machine: no help.
At one point, I fixed a crash by changing sequence of declarations in the .h file. That’s Twilight Zone stuff. I was getting very unhappy with LLVM, with XCode, with the world. My Mirra Chair broke. I spilled coffee on my notes. I lost a lot of sleep.
And then, Monday noon, I saw it:
The Parser was still using the fantasy version of
ExportElement , while everything else was using the real version. And since the different versions have different sizes and layouts in memory, the parser would corrupt the heap every time it touched an
ExportElement . Yet, the objects were legitimate objects — not the droids you’re looking for, but droids — and so all the wonderful safety netting of the modern OS was pretty much disarmed. “Don’t know why you’re doing that, fella,” says Mac OS X, “but it’s a free country!”
Moral: if your #include files are inconsistent, you’re going to be in a very bad place. Somehow, in — can it be? — 25 or 30 years of programming in C-style languages, I’ve never made this mistake before. I can’t imagine why not: I make all the other mistakes. Yikes.