Sugaring C++

Sugaring the C-syntax using macros is generally agreed to be a bad idea, albeit one with the best of intentions. By and large we agree too. But we think there is a very small set of situations where, on balance, the sugar is light enough and the benefits in readability is strong enough to justify sugaring.
Guarded jumps are one case where we think that is true and negated conditions are the other. We introduce some simple, safe macros that just work.

Guarded Jumps

Sometimes we see code fragments in production software that looks like this …

for ( int i = 0; i < LIMIT; i++ ) {
    int j = f( i );
    if ( i == j ) 
    {
        break;
    }
    MORE CODE HERE
}

… we prefer the following three macros.

#define break_if( x )      if (x) break
#define continue_if( x )   if (x ) continue
#define return_if( x )     if ( x ) return

This allows us to rewite the above as follows, which we think is simpler and easier to see what's going on.

for ( int i = 0; i < LIMIT; i++ ) {
    int j = f( i );
    break_if( i == j ); 
    MORE CODE HERE
}

Note we do not recommend trying to define macros for guarded gotos or guarded returns with values. The macros we recommend are easy for a new programmer to guess even if they have not seen them before. They only have one argument and so there is no question of which is which. Guarded gotos and guarded returns with values both require two arguments and that makes them less readable and harder to guess.

More Syntactic Sugar - Negated Conditions

There is just one other situation where, with team agreement, we think sugaring macros can be helpful. The C-syntax of ! for boolean negation is too visually slim and the wrong precedence. As a consequence we are always reading code like this:

while ( ! ( has_sub_list( x, y ) ) {
    do_something( x );
}

We recommend defining macros for while_not, and in the same vein if_not, so that this can be written with less brackets and more naturally:

while_not ( has_sub_list( x, y ) ) {
    do_something( x );
}

The macros are easy to write, easy to guess what they mean, and safe.

#define while_not( x )      while ( ! ( x ) ) 
#define if_not( x )             if ( ! ( x ) )
#define until_not( x )        until ( ! ( x ) )

The same reasoning applies to the guarded jump macros. We recommend their obvious complements.

#define break_if_not( x )      if ( ! ( x ) ) break
#define continue_if_not( x )   if ( ! ( x ) ) continue
#define return_if_not( x )     if ( ! ( x ) ) return

Additionally, we recommend using the alternative keywords and, or and not for boolean operators &&, || and !.

We put all our hopefully team-agreed sugared syntax inside the sugar.h (or hpp) header file. Because sugared syntax is intended to be a language extension, the not function does not live inside a namespace.

With these macros under our belt we can take code like this:

while ( ! ( i > 0 && !test( i ) && test2( i, j ) ) ) { ....

And replace it with this more readable alternative - although one may protest that it remains just as opaque even if it is more readable!

while_not ( i > 0 and not test( i ) and test2( i, j ) ) { ...

not, and, or

In C, we might like to define syntactic sugar for the operators &&, || and !. However it is completely superfluous - the C++ team have got there first. You'll see these can be replaced with the reserved words and, or and not.

Up to Syntactic Sugar > Essays > Home
See also Balanced Indentation in C++