Monday, June 19, 2017

Go To Statement Considered Helpful (part 4: Tail Recursion)

One other place where goto can be useful is in implementing tail recursion. As you should recall, tail recursion is when a function returns with a call to itself. For example:

foo(int a)
{
    ...
    if ( recurse )
    {
        return(foo(a+1)); // tail recursion
    }
}

Most compilers will optimize that into a goto to the top of the function. However, in some cases the compiler will fail you, and you have a runtime fault when the stack overflows. Perhaps you turned off optimization to aid in debugging. Perhaps the compiler isn't as smart as it should be. Who knows? If you're running with a limited stack (and stacks are always limited), don't trust your compiler. Replace the recursion with a goto as below:

foo(int a)
{
  tail_recursion:
    ...
    if ( recurse )
    {
        a=a+1;
        goto tail_recursion; // return(foo(a)) without recursion
    }
}

Now you won't blow away your stack if the optimization settings change.

Of course, you can implement that by putting the body of the function inside a while(1){...; break;} loop, and then replace the goto with a continue. However, doing that is just as much of a hack as a goto. It also adds an unnecessary indention level, and it is definitely harder to read than the simple goto.

Unlike the previous examples, I'm not hiding the goto here in a macro. I am using a label name that should tell any experienced programmer what I'm doing. If I were to hide this in a macro, it would be less clear what's going on.

Now you might object that previously I've said to trust the compiler to optimize your code and don't worry about whether it looks like you're adding a few extra instructions.  I still stand by that, but this isn't a matter of trusting optimization for a few instructions.  This is a matter of your program not crashing.  I ran into this exact situation in real code.  When the correctness of your code requires optimization, you need to force the code to always be optimized, which is exactly why we need the goto in this case.

No comments:

Post a Comment