Thursday, April 20, 2006

With Syntax Like This…

…who needs enemies? I wrote this code for a GUI_ScrollBar, a derivative of GUI_Control that overrides a function, calls the base, then refreshes itself. Ignoring all of the sins of a design like this…

void GUI_ScrollBar::SetPageSize(float inPageSize)
{
GUI_Controll:SetPageSize(inPageSize);
Refresh();
}

What does this code do? It loops forver and overruns the stack. Here's a hint: two L's and one colon, not one L and two colons. Now here's the cute part…there is no entity GUI_Controll with two L's anywhere. Why does it compile? This is an exercise left to the reader, one that may make you want a drink.

Tuesday, March 28, 2006

fork + execve - so happy together!

A long time ago in the 70s when everyone smoked a lot of dope, someone thought it would be cute to break the application launch APIs into two pieces. First you call fork to clone yourself (but this is really fast because the OS is lazy) and then execve turns one copy into something new. Cute!

I just learned two interesting things about Mac OS X tonight:

1. Apparently fork without execve won’t always work.

2. execve without fork doesn’t work so well either.

In the first case, you can read here about why forking doesn’t always work. In Apple’s defense, I don’t blame them for this - fork is supposed to be fast, and cloning all of the apps resources wouldn’t be. Oh yeah, some of those resources might be owned in some exclusive mode. Historically fork was a way to use processes like threads. Or are threads processes? I don’t know…it’s 2:30 AM and my brain hurts.

In the second case, frankly I have no idea why execve won’t work on its own. I get error 45:

#define ENOTSUP 45 /* Operation not supported */

My speculation is that execve requires a “clean” process of some type, e.g. a forked process and not a regular one.

Either way the moral of the story is clear: apparently no one has tried to use these two functions separately since they made disco illegal, so neither should you. Fork even if you don’t need to, then kill off the parent.

Tuesday, February 14, 2006

So that’s where HINSTANCES come from

I realize that this is probably totally trivial for real Win32 developers, but it took me long enough to find this that I’ll blog it and help Google guide future developers.

RegisterClassEx requires an HINSTANCE…I always thought of this as “that thing that is passed to WinMain. Now I realize - it’s a reference to the DLL that the Window Class function is in

So if you’re like me and want to register a class from library code without having that HINSTANCE from WinMain and you’re not using DLLs, you can just use GetModuleHandle(NULL) and call it a day. Easy!

Saturday, February 11, 2006

var-args and macros? Sure!

I didn’t realize until recently that this works:

#define WED_ThrowPrintf(fmt, …) \
throw wed_error_exception(__FILE__, __LINE__, fmt, __VA_ARGS__)
The reason I like var-args and macros together: they make thorough error reporting relatively easy in C++ without a lot of code.

if (err != noErr) WED_ThrowPrintf(”Unable to open document %s (OS Error %d: %s)”, file_name.c_str(), err, OSTranslate(err));

C++ makes almost anything possible, but does it make it easy? I’m a lazy human programmer; the easier a subsystem is to use, the more I’ll use it. So it makes sense to make good practices easier. The easier it is to encode a lot of context information into an error report, the more likely I am to do it, and the more likely a field-report of an error will contain useful diagnostic information.

Friday, February 10, 2006

What makes a professional?

“My definition of an expert in any field is a person who knows enough about what’s really going on to be scared.”PJ Plauger

It doesn’t get any simpler than that. You can always spot the inexperienced programmers in a meeting saying “Oh yeah that can be done. That’s easy!” That’s when you know to be terrified.

Saturday, February 04, 2006

Don’t Trust Error Messages

I just spent some time chasing an error message that I was getting from a Windows Media component when I tried to open a Windows Media Stream. The error was NS_E_BAD_CONTROL_DATA which is defined as:

The server received invalid data from the client on the control connection

This is a very cryptic message because when dealing with COM objects, the terms “Server” and “Client” can have many many meanings. In this case, I had no real clue whether it was referring to a conventional network Server and Client or a COM server and client. That threw me off for a bit.

I ended up finding the problem and it wasn’t even remotely close to what the error claimed. The problem was that my destination folder where I planned on recording the stream TO didn’t exist. Yeah it’s a stupid mistake but what does that have to do with reading the stream? Remember it was WMASFReader that died not WMASFWriter.

In any case, it’s important to remember that sometimes errors will occur and they will not be caught for quite some time or sometimes not at all. If their effects cascade throughout your program, you may find them causing errors that don’t make sense. If you could peel back the layers of the onion enough to see Microsoft’s underlying code, maybe the error and how it got there would make sense however on the outside, they’ll often seem unrelated and can be VERY misleading.

Do NOT fixate on what the error SAYS. Work backwards and find out what triggered it.

Thursday, February 02, 2006

Your Friend the Priority Queue

One of the things I like about the STL is that it makes easy in C++ higher level data structures that make programs better but would otherwise be too cumbersome to use. For example, when was the last time you coded a red-black tree in C? But std::map is pretty easy.

Here’s my extremely unsound and imprecise definition of a priority queue: a priority queue is a data structure where you can rapidly insert “todo” items, find the most important one, reprioritize an item, and remove a finished item. Ideally I’d like all of these tasks to run in logarithmic time or better.

One way to hack out a priority queue using the STL is like this:

struct my_item;
typedef multimap queue_type;
struct my_item {
queue_type::iterator self;
xint more_stuff;
};

The idea is simple: map already gives us logarithmic time to locate the first item, insert and remove an item. The struct has a pointer to its own iterator allowing it to reprioritize by adding and removing itself, like this:

void reprioritize(my_item * item, float new_prio, queue_type * a_queue)
{
a_queue->erase(my_item->self);
my_item->self = a_queue->insert(queue_type::value_type(new_prio, item));
}

I can’t say that this is terribly good C++ but I can say that it works quite well. An examples of priority queues used to create X-Plane scenery:

We use a greedy algorithm to build our triangulations from elevation data. Basically we find the locatoin on our mesh that is the most wrong and fix it by inserting a new point into our mesh. We then re-evaluate the mesh and fix the next-worst point. We repeat until we’ve added too many points or the worst point isn’t that bad. Here’s where the priority queue comes in: when we insert a point, the error between our local mesh and the ideal elevations change, but only for a few points near the insert. So we want to rapidly reprioritize those points without having to reconsider the others. With a 1.5 million potential points and perhaps a few hundred thousand inserts, the priority queue is critical. With a good priority queue this algorithm takes a few seconds; without it a few hours. (This algorithm comes from a paper - I wish I could find it but I did not record the URL in my code. When I find it I will post a link.)

When should you think priority queue? Well, any time you have to evaluate a whole series of conditions at specific events, and the time of the events is known in advance, a priority queue can help. It may be necessary to replace polling logic (e.g. “is it time to flush the queue now”) with solid predictions (e.g. “I know that this queue is 25 bytes away from being full and that one is 13 bytes from being full”).

The STL provides another approach to priority queues: tucked away in the algorithms section are routines like make_heap, is_heap, push_heap, and pop_heap. A heap is basically an array of data that’s sorted just enough that the smallest item is in front. It’s faster to preserve these relaxed requirements than to do a full sort. I haven’t utilized heaps in my code yet, so I can’t comment on heap vs map performance.