Using Tables to Improve Code Layout colorful lead photo

Code layout is a surprisingly subtle problem. Or, maybe not so surprising if we remember that typesetting is its own profession, with hundreds of years of history behind it. We simply haven't had very long to figure out what "good code layout" really is. Even something as simple as braces and indents is still widely discussed.

One reason the topic is so sensitive is because it's really an emotional problem: when layout isn't "right," it's distracting and annoying. It has an immediate visceral impact.

There's one construct that has always irked me; even though I can't entirely explain why. I call it "nested parallel ifs", a phrase I just made up. Here's an example:

void prioritize(bool customer_is_big, bool bug_is_life_threating) {
    if ( customer_is_big) {
        if ( bug_is_life_threating ) {
            // execute super high priority code
        } else {
            // execute medium priority code
        }
    } else {
        if ( bug_is_life_threating ) {
        // execute high priority code
    } else {
        // execute low priority code
        }
    }
}

The abstract scenario is that you have several bool or enum typed variables, and you need to execute different code for each combination. For n Booleans, that means you have to handle 2^n cases. I'll be using the minimal scenario of two Booleans, four cases for my examples, but I'm sure you can imagine how ugly this would get with three or more Booleans splitting into eight or more cases.

There are several things about this that bother me:

  • The inner if (bug_is_life_threating) is duplicated.
  • the outer else clause is quite far away from the condition. You have to carefully determine nesting depth and scroll up to figure out the exact scenario for which the code in the else block is executed.
  • The nesting order is arbitrary, yet it is baked into the structure of the code, so it's tedious to change the nesting order later.

All this is just post-hoc rationalization, of course. In fact, I just find it hard to read. Like I said, it's mostly visceral.

A weak solution

Now, the first thing I considered was to use a switch statement instead:

void prioritize(bool customer_is_big, bool bug_is_life_threating) {
    switch(   int(customer_is_big) | ( int(customer_is_big) <<  1)   ) {
      case 3:
        // execute super high priority code
        break;
      case 2:
        // execute medium priority code
        break;
      case 1:
        // execute high priority code
        break;
      case 0:
        // execute low priority code
        break;
    }
}

This removes the duplicate code and arbitrary nesting order, but has many other problems. Most obviously, mapping the case numbers to the different cases is opaque and possibly error prone. So let's try again.

A better solution

Now, doesn't this expression:

    int(customer_is_big) | ( int(customer_is_big) <<  1 )

remind you of something? It looks an awful lot like way the subscripts of a multi-dimensional array are resolved to addresses. Perhaps that would be more obvious in this equivalent form:

    int(customer_is_big) + int(customer_is_big)*2

Can we find a way to use multi-dimensional array subscripts to handle that icky bit of math for us? That would give us an elegant notation for getting from the Booleans to the element of an array. The answers is Yes; we can function pointers! Function pointers are a classic way to dynamically select code. The pieces are fitting together quite nicely. Here's a summary of where we are now:

Use a multi-dimensional array of function pointers to dispatch directly to the appropriate function.

Let's give that a try:

void prioritize(bool customer_is_big, bool bug_is_life_threating) {
    typedef void (*handlerFunction)();
    handlerFunction handler[2][2] = { {
        low_priority,
        high_priority
        }, {
        medium_priority,
        super_high_priority
    } };
    handler[int(customer_is_big)][int(bug_is_life_threating)]();
}

void low_priority() { /* execute low priority code */ }
void medium_priority() { /* execute low priority code */ }
void high_priority() { /* execute low priority code */ }
void super_high_priority() { /* execute low priority code */ }

Now, that has some very nice features. the dispatch logic is entirely contained in the initialization of the function pointer array, and each case is handled in a separate function (enforcing good factoring). The technique scales well to more cases; you simply add more dimensions the array. It also handles enumerations with the same syntax, although you'll need to be know and use the underlying integer value of the enum, which could be a problem.

The main quibble I have with this is that it's still not entirely clear which function maps to which case. So, I suggest breaking the array initializer out over several lines, to form a kind of table:

void prioritize(bool customer_is_big, bool bug_is_life_threating) {
    typedef void (*handlerFunction)();
    handlerFunction handler[2][2] = { 
      // MINOR BUG           LIFE THREATNING BUG 
        {low_priority,       high_priority},          // SMALL CUSTOMER
        {medium_priority,    super_high_priority}     // LARGE CUSTOMER
    };
    handler[int(customer_is_big)][int(bug_is_life_threating)]();
}

And that's my final answer.

Make code, not war

I think this is a pretty neat solution, because (A) we can define and format the dispatch table in a very natural and intuitive way, and (B) the dispatch itself happens in a single line of code. Not one machine operation, because there'll be some work behind the scenes to resolve the array subscripts to an address, but it's still pretty fast. Advantage (B) is C/C++ specific, but we could get advantage (A) in any modern language.

Of course, not everyone's going to agree that this is a 'neat' solution. I'll be some people would say that there wasn't any problem there to solve. That's ok; like I said before, layout is a very emotional issue for programmers. In my experience, the divide isn't between programmers who prefer one layout style over another, but between programmers who care about making their code readable and those that don't. It doesn't take much work to lay your code out nicely, but it's non-zero. So let me leave you with my Thought of the Day:

The most important aspect of readable code is thinking about code readability.

- Oran Looney April 29th 2007

Thanks for reading. This blog is in "archive" mode and comments and RSS feed are disabled. We appologize for the inconvenience.