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

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

Written by Bob Koon

Topics: Tip o'the Day™

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). Does anyone know of any subtleties or nuances I have overlooked?

6 Comments Comments For This Post I'd Love to Hear Yours!

  1. Noel says:

    It makes sense, but I have to say I’ve never had enough problems with it to force myself to write it this way (which seems backwards from how I think about it).

    However, what I do is use the const keyword *religiously*. Anything and everything that can be const becomes const. I find that it makes the intent much more clear and lets the compiler catch bugs for me. Hmm… maybe that should be my next blog topic ;-b

    • Bob Koon says:

      Yep, that’s pretty much my sub-point: let the compiler find the bugs for you automatically. The way to do that takes several forms, one of which I’ve described here. You pointed out another way which I agree with and do myself, but I consider that a separate topic and I didn’t want to cloud the focus of this post with it.

      Thanks for the comment!

  2. Haiko Schol says:

    One reason not to do it is a decrease in “explainability”. If you work with other developers and someone reads your code, he might not know this trick and wonder why you wrote it that way. Than you have to explain it. After doing that ten times it might get slightly annoying… :)

    As you mentioned, whether (some_variable = SOME_CONSTANT) results in a warning depends on the compiler you’re using. For gcc I usually use -Wall -Wextra -ansi -pedantic, which will generate “warning: suggest parentheses around assignment used as truth value”. Add -Werror to that and the compiler forces you to fix that (and every other warning) too.

    • Bob Koon says:

      A valid point, and it might seem that way, but it has been my experience that this doesn’t need explanation. Indeed, other programmers see it and tend to like it. It has been a rare occurrence where I’ve needed to provide a description.

      Regarding your other point about compiler settings: this is exactly why I changed my source instead of relying on the compiler to handle it for me. I might not be using that particular compiler so then what do I do? I’d rather not have to worry about it because my code style becomes the first line of defense.

      • Alex Curylo says:

        “Indeed, other programmers see it and tend to like it.”

        Heh. Work in Windows shops much? I’ve been doing this for some fourteen years now, and until the iPhone came along I was usually the token Mac port guy in a Windows shop, and I have had numerous excessively contentious disputes about writing my comparisons “wrong”. For some reason, they become even more contentious when I point to the specific unintended assignment in their code that this practice corrected, it seems…

    • Jason says:

      I think explainability is a non-issue. Any competent developer should be able to grasp constants-first-equalites fairly easily (even if they view it as nothing more than an arbitrary switch).

      In fact, if the developer doesn’t understand the concept, they probably also will not understand adding all the gcc flags, either.

Leave a Comment Here's Your Chance to Be Heard!