“goto” – think of slimmer functions

I confess my writing lacks theoretical rigor.  I was never a Computer Science student and I am not an academician. So I lack requisite research as well as formal training to deal with some of the issues. I also risk repeating what the research literature might have proven decades ago.

However, I also want to note that I am not alone. I have seen people from all vocations joining the great software talent drag. None of us gave basic thought to make our programs better. I hope to serve my crowd.

I request reader community to point me to rigorous places and prove or disprove me wherever they feel the need. 

For simpletons like me, goto statements are considered harmful.

Where you can goto
However, one of my friends, who have written many more lines of code of C, insists that goto statements are actually useful in one circumstance. He points out the following condition:

int foo (…) {

/* resource allocation */

/* condition1 */ 

/* need return */

/* condition 2 */

/* some code */

/* free resources */

return <return value>;

In this case, if we return in the action block of "condition 1", we risk resource leaks. Instead, according to his experience, it makes sense to jump to the "free resources" block, we are sure all allocated resources are free.

Let us understand that he does not insist that goto is necessary. He says goto is very convenient in this case.

How to avoid even this goto

To say it bluntly, I consider programs like above badly designed.

The "resource allocation no matter what" and freeing up of those resources should ideally be pushed to constructors and destructors of this object or the objects that collaborate with this object.

If you are working with procedural paradigm, you could have designed functions similar to *nix device driver interface and pushed all unconditional allocations and deallocations to equivalent of "create" and "destroy" functions of this object. As an alternate, you could have pushed them in similar functions of other objects and used their pointers in the code.

Deeper problem when you needed goto

goto might contribute to bad flow of the code. However, there is a deeper problem too.

Let me introduce two fuzzy concepts in my mind: depth of a program and width of a program.

"Depth of a program" is the inherent algorithmic terseness or the "stuff". Most of us would recognize this very easily. The books of Data Structures, Algorithms or Encoding are full of "stuffed" algorithms. Even most device drivers fall into this category. Such programs are characterized by:

  1. Well known, small number of inputs and outputs
  2. Most data manipulation is done using inputs
  3. Most statements in the code are dependent on the previous statement in the code

"Width of a program" is the informal measure of decoherence or the "fluff". Not many books talk about this. Why? Because such programs are "practical" and can't be represented in books satisfactorily. Moreover, *all* the languages lack the construct that can underscore the fluff. Also, the concept falls in the negative learning – "what not to do".

To elucidate my point, consider an imaginary construct "|||" – that means "execute left or right in any order. Consider the following piece of code:

int foo ( int *a, int b, int c) {

x = *a;

y = *a + b;

z = b * c;

*a =  x + z / y;

return *a;

Using the rudimentary (and insufficient) notation we defined earlier, the same code can be re-written as:

int foo ( int *a, int b, int c) {

x = *a; ||| y = *a + b; ||| z = b * c;

*a =  x + z / y;

return *a;

}

Did you see that the code got wider? IMHO that should be the measure of decoherence or "fluff". The wider the code is on average, the worse the functional decomposition is.

In limiting case, you may end up with multiple return paths joined by ||| – essentially, the function may be effectively split into many!

Let us consider our sympathetic case to goto. The fact that there were many returns from the function also told us that the code was decoherent or fluffier. If proper functional decomposition is applied to achieve slimmer functions, we will not be forced to use goto to avoid resource leaks.

0

Advertisements

2 comments on ““goto” – think of slimmer functions

  1. partha says:

    Bhushit,
    In my childhood days i always read that “goto” is a monsterous evil in programming and even my professors declined to give marks for using goto in code. but i could site 2 example where goto becomes quite handy.

    Just to elaborate on you point where you can ‘goto’

    case 1:
    I read somewhere that to avoid ‘tai recursion’ the compiler transform the recursive function using goto.
    NOTE here that the compiler does this and not application programmers.

    say for example, i want to print a list recursively

    void printlist (List *node)
    {
    if (node->next != NULL)
    {
    PrintElement (node);
    PrintList(List->next);
    }
    }

    Now the compiler inturn reformats the code internally to
    void printlist (List *node)
    {
    TOP:
    if (node->next != NULL)
    {
    PrintElement (node);
    goto TOP;
    }
    }
    The possible reason for this is that the code during execution need not save the context of the previous call to the function recursively in another stack frame and can operate on the same stack.

    case 2:
    consider a piece of code where in a function you have n number of memory allocations [consider a case where you do not have any chance to use automatic variable]. Now there are also m number of returns that the function does on different conditions. So you need to clean up your memory before the function returns. a goto jump is more favourable then calling a function to free the memory. (simply because you do not want to use another stack frame). goto will help it to keep the code clean and return.

    func (void *arg)
    {
    elem *data1, *data2, *data3;

    data1 = malloc (sizeof (elem));
    data2 = ……
    …..

    if (data1->value)
    {
    /* error condition*/
    goto end;
    }
    /* same for data 2 and data3*/

    goto end:
    free (data1);
    free (data2)

    return;
    }

    The case 2 is widely used in kernel codes where they want to keep the stack to a minimum size.

  2. bhushit says:

    Partha,
    As I have already stated, I am actually learning by blogging. Thanks for the tail recursion example. I believe that should be a genuine case of goto use.

    Whatever happens inside compilers is not of much a concern because it is automatic code translation – either it works or it does not. No human being is expected to look into once debugged. So we are still sticking with principles of compassionate programming.

    However, I don’t recommend it in general. Tail recursions are replaceable with iterations. Substituting recursion with iterations may make it easier to debug.

    The other one (m mallocs and n exits) is clearly an outcome of bad design as I argue earlier.

    Keep posting!
    -Bhushit

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s