Tuesday, August 23, 2011

If (!expression) Considered Harmful

Let me begin by saying that I don’t feel any need to apologize for adapting the well- known aphorism, “GOTO Considered Harmful,” because I think this little essay ranks with it in importance.

Since I began programming as an almost daily activity in 1977, I have learned and used, one way or another, and for an equally wide variety of reasons, many programming languages, including the following (in no particular order): Fortran, COBOL, IBM 307 Assembler, Intel/Microsoft Macro Assembler, Perl, Basic (everything from Bill Gates’ original Basic for the Intel 4004 through current day Visual Basic .NET 2010), C, C++, C#, SQL, the DataEase Query Language, and the Microsoft batch language in all its forms. Due to the nature of my work, I often use two or more languages in the span of a single work day, and they can run the gamut from basic DOS batch files to advanced C, back to back.

The advantage of this huge diversity is that I’ve seen and used dozens of idioms and design patterns, some of which appear in only one or a handful of the languages cited above. The case of interest today is the unless idiom, found only in Perl, so far as I know. There is a simple example in the section titled “and, or, & not” of Perl Idioms. Programming Perl, the Bible of Perl, covers it in 4.3. if and unless Statements.

In terms of demonstrating the power for clarification of the unless idiom, the examples usually cited to illustrate it are pretty lame. Nevertheless, since their aim is to demonstrate the idiom, itself, their simplicity is justified. Following are some examples from production Perl scripts.

if ( !$gmatches )

if ( !$_ ) {                    # Is line blank?

if ( !$paramisok ) {            # Warn about invalid lines.

if ( !$appending )

if ( $_ =! /^.U / )             # Skip detail lines that list unencrypted files.

The last one is especially troublesome, because it joins the result of two expressions, both expressed in negative terms, with a logical and operator (&&), which has positive semantics.

Here are a few in C, which has a similar grammar.

if ( !CloseClipboard ( ) )

if ( !IsTextUnicode ( pBOMInfoP6C->lpCharBuff , ( int ) pBOMInfoP6C->dwNBytes , &intUnicodeTestMask ) )

if ( !( utpOutFile = fopen ( pszTestCipherTextFQFN , "wb" ) ) ) {

Wait a minute, I hear you saying. The whole idea of an if statement is to do something if a condition is true.

Exactly! That’s why the not operator (!) is so harmful.

Enter the unless Idiom

Now, let’s rewrite the above expressions, starting with the Perl examples.

unless ( $gmatches )

unless ( $_ ) {                 # Is line blank?

unless ( $paramisok ) {         # Warn about invalid lines.

unless ( $appending )

if ( $_ =! /^.U / )             # Skip detail lines that list unencrypted files.

Next, let’s give the C statements the same treatment.

Unless ( CloseClipboard ( ) )

Unless ( IsTextUnicode ( pBOMInfoP6C->lpCharBuff , ( int ) pBOMInfoP6C->dwNBytes , &intUnicodeTestMask ) )

Unless ( ( utpOutFile = fopen ( pszTestCipherTextFQFN , "wb" ) ) ) {

Hold that thought. None of the above three statements is valid C. Indeed, out of the box, it is invalid in every version of C of which I am aware, which includes Microsoft Visual C++ 6.0 through Visual C# 2010, in addition to the open source standard, GCC.

Unless in C

Please excuse the pun, but the unless idiom can be implemented in C and (and C++).

All you need is a C preprocessor capable of expanding and substituting into a one-line macro.

Let’s have the drum roll, please. Here it comes.

#define Unless(pexpr) if ( !(pexpr) )

Make this one-line macro visible to all of your C and C++ code by including it into a header that you always include, and you can clarify the intent of your code, unless you really need all those obtuse if ( !expressions.

Wait, you say, my compiler doesn’t support macros. There is another way, which is just as straightforward, still fits on one line, and goes into that header that you always include.

__inline Unless(pexpr) { return ( !( pexpr ) ); }

Fight coding horrors, one expression at a time.