Jonathan Boccara's blog

How to Short-Circuit a Sequence of Operations with a Boolean Value

Published January 28, 2020 - 0 Comments

Daily C++

Interrupting a sequence of operations when one of them fails is a common problem in computer programming. There are plenty of elaborate ways to code that, ranging from exceptions to monads to static exceptions.

One of the techniques to stop such a sequence of operations that is not so sophisticated, but that can be found in existing code, is to use a boolean. This relies on each step producing a boolean flag to indicate if it succeeded or failed, and to stop executing steps at the first failure.

However, simple as it is, this technique can lead to code that doesn’t do with it looks like.

Compact (but incorrect) code that uses a boolean

Here is a simple example to illustrate this technique. We have a class X, that contains three switches that can be activated:

class X
{
public:
    void switchAOn(){ std::cout << "Switch A activated\n"; }
    void switchBOn(){ std::cout << "Switch B activated\n"; }
    void switchCOn(){ std::cout << "Switch C activated\n"; }
};

We also have a process of three steps that can activate the switches of X and return a boolean flag to indicate whether they have succeeded:

bool executeStep1(X& x)
{
    x.switchAOn();
    return true;
}

bool executeStep2(X& x)
{
    x.switchBOn();
    return false;
}

bool executeStep3(X& x)
{
    x.switchCOn();
    return true;
}

To simplify the example, steps 1 and 3 always succeed, and step 2 always fails.

Note that this design contains several flaws. One of them is that the boolean could mean failure rather than success, if we only look at the code. However some code out there uses this pattern, and the point of this example is to illustrate a surprising behaviour associated to this type of design, rather than to encourage it.

Now here is our calling function, that also returns a boolean to indicate its success:

bool f(X& x)
{
    bool success = executeStep1(x);
    success &= executeStep2(x);
    success &= executeStep3(x);
    
    return success;
}

Given that the member functions of X print out a message when they are executed, what do you think this function prints?

When you have guessed, click to unveil the output:

Switch A activated
Switch B activated
Switch C activated

I don’t know what you guessed, but when I encountered that pattern in production code, I would have thought that the output would rather be:

Switch A activated
Switch B activated

Indeed, everybody knows that boolean operators short-circuit, right? Indeed, after executing step 2 in the sequence in f, success is false, then whatever executeStep3 returns, success will remain false, so there is no point running executeStep3 at all, is there?

Except this is wrong. operator&= doesn’t short-circuit in C++ (and believing it did made me waste some time in analysing the code!).

Forcing a short-circuit

The reason why operator&= doesn’t short circuit is because operator& doesn’t short circuit. It is operator&& that short-circuits (and also operator|| and operator,).

The difference between operator& and operator&& in C++ in that operator& is a bitwise AND (it takes two numbers, considers them in base 2, and performs a logical AND on each of their corresponding bits) , and operator&& is a logical AND (taking to boolean numbers and returning true iif both are true).

But for type bool, operator& and operator&& are equivalent! Even then, in C++ operator& doesn’t short-circuit even if operator&& does. And this probably won’t ever change, as there must be quite a lot of code out there that depends (perhaps without being aware of it) on this behaviour.

To make our above code short-circuit, we have to resort to using operator&&:

bool f()
{
    bool success = executeStep1(x);
    success = success && executeStep2(x);
    success = success && executeStep3(x);
    
    return success;
}

The code now outputs:

Switch A activated
Switch B activated

But with this change, the code has become less concise. It contains repetitions of success.

To solve this problem, I was eager to brandish a simple but brilliant idea: let’s use operator&&=! Like operator&& it must short-circuit, and like operator&= it would make the code more concise, right?

Except that &&= doesn’t exist in C++. Oops.

Be wary of boolean return codes

Return codes based on boolean values can be deceptive. On the top of not expressing clearly if they mean failure or success, they can lead, as seen above, to surprising behaviour that can make you waste some time understanding what is going on with a program.

Everything we’ve seen with operator&= applies to operator|=, that could be used if the boolean returned by the steps meant failure.

Be careful if you have to work with boolean return codes! If you can’t refactor a given piece of code to a more robust way to deal with errors, at least be clear about what your functions return.

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin