Control Flow

The organization and structure of source code is just as important as the code itself.  For a long time I’ve tried (and seen) many different ways of handling control flow.

This article will discuss a number of different approaches, I already know my favourite, but I think there is possibly a role for a new scope operator that helps.

Consider the following problem, you have 5 actions as part of your pipeline processing.  Each action should only be initiated if the previous action was successfully completed.  This scenario will occur throughout your code.  I’ll discuss a number of alternatives to solving this:

int iErrorStatus = UNKNOWN_ERROR;
iErrorStatus = Action1();

if ( SUCCESS == iErrorStatus )
{
  iErrorStatus = Action2();
}

if ( SUCCESS == iErrorStatus )
{
  iErrorStatus = Action3();
}

...

return iErrorStatus;

As you can see, it doesn’t take long before this starts to get rather unreadable.  Especially when you factor in any dynamic resources you have allocated; and therefore need to release.

An alternative to this, is to put the action call inside a return code test.  This works, but has massive indentation issues:

if ( SUCCESS == Action1() )
{
  if ( SUCCESS == Action2() )
  {
    if ( SUCCESS == Action3() )   
    { 
      ...
    }
  }
}

...

return iErrorStatus;

 
Another option is to have multiple function exit points.  This is my least favourite, but I put it here as some people seem to like it.  The reason I like it the least, is that having multiple exit points, will undoutably mean duplicated resource management (i.e. releasing dynamic memory).  And I’ve seen more bugs with missed deallocations using this method, than anything else!

int iErrorStatus = UNKNOWN_ERROR;

iErrorStatus = Action1();

if ( SUCCESS != iErrorStatus )
{
  return iErrorStatus;  
}

iErrorStatus = Action2();

if ( SUCCESS != iErrorStatus )
{
  return iErrorStatus;  
}

iErrorStatus = Action3();

...

return iErrorStatus;

Next is the misunderstood ‘goto’.  I really like it, but can understand why some people don’t.  It is almost like it is too powerful, but if used correctly can very elegantly solve the problem:

int iErrorStatus = UNKNOWN_ERROR;
iErrorStatus = Action1();

if ( SUCCESS != iErrorStatus )
{
  goto EndOfFunc;
}

iErrorStatus = Action2();

if ( SUCCESS != iErrorStatus )
{
  goto EndOfFunc;
}

iErrorStatus = Action3();

...

EndOfFunc:

return iErrorStatus;

Very similar to the multiple returns, but it now provides a single place to implement your clean up logic.

Other approaches start to miss-use the language.  I almost like the following, but it is too prone to problems:

do
{
  int iErrorStatus = UNKNOWN_ERROR;
  iErrorStatus = Action1();

  if ( SUCCESS != iErrorStatus )
  {
    break;
  }

  iErrorStatus = Action2();

  if ( SUCCESS != iErrorStatus )
  {
    break;
  }

  iErrorStatus = Action3();

  ...
}while( false );

Finally, an additional scope operator that behaved like the aforementioned do-while trick, but without any concerns about the accidental repeats that come with a while-loop.  For example:

scope
{
  if ( SUCCESS != Action1() )
  {
    break;
  }

  if ( SUCCESS != Action2() )
  {
    break;
  }

  if ( SUCCESS != Action3() )
  {
    break;
  }

  ...
}

return iErrorStatus;

Let me know what you think.

One thought on “Control Flow

  1. Scott Langham

    Hey, cool blog. I can’t resist getting my two cents in on a code style discussion.

    C is pretty poorly designed for dealing with stack based resources that need releasing. I like your scope idea, but if the language needs changing, some other things could be improved at the same time. C++, or some other language with destructors or garbage collection would be best!

    Everyone’s got their own preference, but if we’re staying with C then I think the most straightforward solutions are: when no resource releasing is required, get out of there and return on the first error. And, when resources need releasing then the goto idea looks simplest. I don’t think it’s necessary to mandate every function needs to have extra plumbing for the sake of consistency if there’s no clean-up needed.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>