Tuesday, December 07, 2004

Going Back to Flint Axes

I had a little project to do last night: a small GUI application, only needed to run on Windows, with just a few bells and whistles--nothing too complicated. Just out of curiosity, I decided to go to the Wayback Machine and code it in straight, honest-to-Petzold Win32 and C. Not C++, not Microsoft's C-in-C++'s-clothing MFC, but real C, with slash-star comments and all.

Now, just for background, for a task like this, the nearest tool in my toolbox is usually Python with wxWidgets. The second tool in the toolbox would be either C++ with MFC (if I were really in a hurry) or C++ using ATL/WTL (if it needed to be clean and presentable). Third choice would be Java, but only if I needed to incorporate something that was already written in Java. I haven't done any straight C in about 8 years.

But C and Windows (3.1, actually) are where I cut my teeth, and recently we've been having some discussions at work about "the way things work" under the covers of the Windows CE operating system we're using. The constraints of CE (32 processes, 32MB address space, small display) have put me in mind of my first programming gig (Windows 3.1, 8MB of memory on a good day, 800x600x256 display, reboot three times a day for luck).

So back to C I went, just as an exercise to see how much C and Win32 I'd forgotten. And I was surprised by what I missed and didn't miss. What I missed most:
  1. Declaration of variables at point of use (C++), rather than at the top of a block (C), or not at all (Python). This was the big one. I've become spoiled.
  2. Default arguments (C++, Python) and function overloading (C++). This one really surprised me... I didn't realized I used them that much.
  3. Typed allocation and deallocation (C++). Casting malloc() just looks wrong now.
  4. Reasonably rich string handling (C++/STL or Python). string.split(), my kingdom for string.split()!
  5. Support for Windows Common Controls: I forgot how much inane drudgery is involved in creating a simple new-style toolbar and rich edit control when you have to do it by hand.
  6. Productivity, productivity, productivity. I put about 9 hours into the project, and I'm about 75% done. Most of the time was spent remembering all the minutiae of the Win32 API (that's right--you have to manually call LoadLibrary() on the RichEdit DLL, or else the OS doesn't realize it exists...). I would probably have been done in 2 hours had I used C++/MFC, or about 4 with either C++/ATL or Python/wxWidgets. Yes, usually Python is far more productive than using C++ with either toolkit, but what I was doing played right into the strengths of both MFC and Microsoft's tool support. On the other hand, it would have been about the same amount of time in Java, just because I've decided that next time I have to do a client in Java, I'm going to learn SWT.
But I was more surprised by what I didn't miss:
  1. Classes. Only once in the whole process did I really miss wrapping up data and code together.
  2. A message handling framework. Microsoft hasn't updated their message cracker support since 1999, according to the header file comments, but I think the resulting code is actually clearer than that of most object-oriented GUI frameworks.
  3. Safety features (type checking or garbage collection). I created only two bugs that would have been caught by either of these techniques--one was because I forgot that strncpy() doesn't always NULL-terminate strings; the other was the predictable C memory leak. It helped that I enabled maximum compiler warnings and used forward declarations, though.
And the biggest surprise? The #1 thing I missed (declaration of variables at point of use) forced me to write better-factored code. I broke things up into shorter, clearer functions just so I wouldn't have to look at massive declaration lists. That kept the functions to a well-defined, single purpose, which led to self-descriptive names and access to only the data they really needed. Nice.

Moreover, the C code I wrote last night was far and away better than the C code I wrote 8 years ago--all that exposure to industrial-strength, server-side C++, Java, and Python (as well as bits of experimenting with Lisp) has helped.

But one thing I can't deny: ending up with a 30k, statically-linked executable, dependent on no DLLs other than those of the OS, made me smile.

No comments: