0 Background

Let’s start with code as usual,

if (x == 0)
{
    //....
    // Operations
    if (y == 0)
    {
        if (FuncA())
        {
            if (FuncB())
            {
                if (FuncC())
                {
                    if (FuncD())
                    {
                        if (FuncE())
                        {
                            // Operations
                        }
                        else
                        {
                            // Resource release
                            return -1;
                        }
                    }
                    else
                    {
                        // Resource release
                        return -1;
                    }
                }
                else
                {
                    // Resource release
                    return -1;
                }
            }
            else
            {
                // Resource release
                return -1;
            }
        }
        else
        {
            // Resource release
            return -1;
        }
    }
    else
    {
        // Resource release
        return -1;
    }
} 

How about that? I mean, how do you feel when you are reviewing, maintaining or try to dig something out of this code?

To tell the truth, I will try to place my monitor in vertical because maybe I can view this function completely once. Unfortunately, it is not enough, and I am planning to make an order on amazon for a much bigger monitor. Finally, the monitor has arrived, and I place it in vertical. Then the another problem comes, the horizon is not enough because of the god damn alignment!

Yes, it is not my problem, and of course it is not my monitor’s! The issue is in the code, the arrow code. There are too many ‘if’ blocks nesting! I bet the author himself flashes the idea of suicide when he reviews this his own code several years later.

1. Where This Kind of Code Exists

Does this really happen? I mean, we do not need a solution for a problem that never exists. But unfortunately, the answer is yes. Sometime we need to deal with some hardware resource or network resource, before the operation, all the resources need to be initialized. What’s more, if any initialization fails, the operation should be terminated and the former resources need to be release. If we have no plan or consideration of this ‘arrow code’ issue, here comes the code mentioned above.

2.’goto’ Solotion

In fact, we are not going to talk many solutions in this article. There are solutions deal with this, like http://c2.com/cgi/wiki?ArrowAntiPattern. Today, I’d like to introduce one unique solution, with ‘goto’.

Not all the programming language has the key word ‘goto’, I am a Cpp programmer, I have it. If you do not, please stop and consider the solutions mentioned in the articles listed above.

To tell the truth, ‘goto’ is a sensitive keyword to use, or even talk about. So many codes act really negative to this keyword, considering to keep the code clear in logic. I have to admit that there are so many cost/risk using ‘goto’ in the code, and I do not think it is a good coding style with it, but, I think it is really OK if we only have one ‘Exit’ or something similar in one function to ‘goto’.

Personally,  I think ‘goto’ along with ‘Exit’ label is really an efficient solution to deal with this kind of arrow code.

In fact, the solution is really light-weighted. All the resource release should be the same – check, then release – wherever the initialization fails at, which means, they all could be put in the ‘Exit’ block before return. Initialization only needs to be implemented step by step, and, log print then goto ‘Exit’ if it fails.

3. Implementation

First of all, an ‘enum’ for fail return code is really helpful,

enum RC
{
    RC_OK   = 0;
    RC_A    = -1;
    RC_B    = -2;
    RC_C    = -3;
    RC_D    = -4;
    RC_E    = -5;
    ....
}

Then, every sub-function to help initialize should be defined return value as this enum.

RC FuncA(..)
{
    RC rc = RC_OK;
    // Detail Implementation
    // ...

    return rc;
}

At last, initialize step by step, log print and then goto ‘Exit’.

RC Initialize(..)
{
    RC rc = RC_OK;

    rc = FuncA();
    if (rc != RC_OK)
    {
        //Log if needs..
        goto Exit;
    }

    rc = FuncB();
    if (rc != RC_OK)
    {
        //Log if needs..
        goto Exit;
    }

    rc = FuncC();
    if (rc != RC_OK)
    {
        //Log if needs..
        goto Exit;
    }

    rc = FuncD();
    if (rc != RC_OK)
    {
        //Log if needs..
        goto Exit;
    }

    rc = FuncE();
    if (rc != RC_OK)
    {
        //Log if needs..
        goto Exit;
    }

    //Operations
    //...

Exit:
    if (rc != RC_OK)
    {
        // Log if needs
        // Resource release
    }
    return rc;
}

					
Advertisements