Tip o’the Day™ 1: Take Advantage of Consts

The Tip o’the Day™ series is an on-going list of light, but focused tips and suggestions on how to improve the quality of your source code. The idea here is that each entry is small enough to pickup and try immediately.

Conditionals and Consts

Here’s something you might not have thought about doing with your conditionals. I came across this quite some time ago and I gave it a try because I thought it made sense. Now it has become a habit so I do this all the time. It’s not a drastic change from your existing coding habits and it doesn’t require much effort, but it will help you find errors in your code more quickly.

(Preemptive Note: this works just fine with other kinds of constants. I use #define here only to make the narrative clearer.)

The Old Way

What I used to do was this:

#define SOME_CONSTANT   20

if (some_variable == SOME_CONSTANT)
{
    ...
}

And I went on my merry way, and everything seemed to be OK. I’m sure a lot of you do the same thing. It seems fairly benign, right: check to see if a variable is the same as something else?

However, something drastic and very subtle happened that was the catalyst for my new habit. I was debugging some code and I discovered what the problem was…

#define SOME_CONSTANT   20

if (some_variable = SOME_CONSTANT)
{
    ...
}

Do you see what I did? I wanted to test some_variable for equality but instead I set it to the value of SOME_CONSTANT. If that was non-zero, then the if () code block would get executed. So while the code I wrote is entirely legal, it’s not what I intended. After a long debugging session and lots of cursing myself for being an idiot for not spotting it sooner (when you’re skimming code, a missing = is really hard to spot) I fixed the problem by adding another = but I got to thinking about how to prevent that from happening again.

The New Way

How can I keep the readability of the old way but still prevent that subtle bug from being introduced? What I thought to do was turn the problem on its head.

Here’s what I do now:

#define SOME_CONSTANT   20

if (SOME_CONSTANT == some_variable)
{
    ...
}

Why do it this way? Well, if you write SOME_CONSTANT = some_variable, the compiler will give you an error at build time. You are forced to fix it immediately instead of inserting a subtle bug into the codebase at your next commit. Sure, unit testing would probably find the problem before you commit the code, but if you’re maintaining legacy code you might night have the luxury of unit testing. If you never let it get that far in the first place, you’ve saved yourself a ton of potential debugging time.

This works 100% of the time, does exactly what I intended, and works for every compiler, even if that compiler gives a warning for potentially hazardous code. You might be developing in an environment where the compiler isn’t as good (mature) as what you’re normally used to. If I write the same way all the time, I don’t have to waste time thinking about what my environment is and changing my habits to suit.

So again, while the old code is legal, why write code that the reader/maintainter (which might not be you) has to think about? It’s far easier to write it clearly than to be clever. (I’ll be writing about that subject very soon.)

Non-const vs. Non-const

What do you do if you’re testing a variable for equality against another variable? Well, then you just code your conditional as you normally do. ‘Normal’ for me means that I use the code around the conditional to determine the context, like so:

some_variable++;

if (some_variable == some_other_variable)
{
    ...
}

For me, it’s easier to read and understand if the most-recently modified variable is on the left.

Use it for more than just ==

All I’ve shown you is testing for equality. That was just to make the text easier to understand. However, you can still use this technique for the other types of comparisons as well. The constant can just as easily be on the left as the right for less-than/greater-than/not-equal statements. Why use this technique for those? They can turn into <=/>= and back to just = really easily (and just as easily as == could) over the course of development. If the constants are always on the left, you’ll never get caught out by a single = again.

So, there it is. Simple, effective, and with no side-effects (that I know of).

Tip o’the Day™ 1: Take Advantage of Consts