Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C++ coding styles, ways to create solid code

0.00/5 (No votes)
13 Feb 2001 1  
Samples of C++ coding styles that will help you to create solid code

Introduction

In this article I'll try to give you apt code styles which you can use when writing programs using functions.

C++ code styles

Most C++ programmers agree that multiple exit points are a bad thing as single exit point makes the code look tidy, easy to read, simplifies debugging when having a single breakpoint (at the return statement) and you can be certain to catch the processor before it leaves a function. One more considerable drawback of multiple exit points I came across, during practical work, is code where there are many initalization and clean up sections present. With use of the return operator it is necessary to put uninitializing operators after each failure in code. See Code Style 1 where I illustrate the problem. See the solution I present with a goto Quit operator, Code Style 4, where you have all clean up operations in a special code section, not all over the code.

Code Style 1

BOOL Func(LPOBJECT *ppObject)
{
  LPOBJECT pObject1; LPOBJECT pObject2; LPOBJECT pObject3;
  if(!CreateObject1(&pObject1)) return FALSE; // No uninit code here

  if(!CreateObject2(&pObject2))
  {
    DestroyObject1(&pObject1);  // must remember to destroy first object

    return FALSE;
  }
  if(!CreateObject3(&pObject3))
  {
    DestroyObject2(&pObject2);  // must remember to destroy second object

    DestroyObject1(&pObject1);  // must delete first object AGAIN

    return FALSE;
  }
  etc ...
  (*ppObject) = pObject3;
  return TRUE;
}

Some programmers tolerate the use of the return operator (which causes multiple exit point) for validity checks on parameters or initial conditions like this:

Code Style 2

HRESULT func(*pParam)
{
  if(!pParam) return E_INVALIDARG;
  ...
}

Though I ought to say that multiple return points have their own advantages: they make the code more readable, you can leave the failed function whenever you encounter an error, return the error code at once without using the variable to store it, and avoiding the needless commands which are not necessary as the whole function fails (especially multiple return operators get gid of redundant if/else and assignment statements Code Style 5,Code Style 6). But when using this style you have an increased risk to make a mistake in code, and you considerably increase the amount of times spent debugging your code. This code style, Code Style 3, is safe when there are no other initialization/clean up operations in the function.

Code Style 3

int Funct()
{
  if(!func1()) return(ErrCode_A);
  if(!func2()) return(ErrCode_B);
  if(!func3()) return(ErrCode_C);
  if(!func4()) return(ErrCode_D);
  return(ErrCode_NOERROR);
}

Usually there is no reason in using the "goto Quit" operator when there are multiple exit points in code. Now I'll show you a code style using a goto. As for this operator, many programmers don't see a problem in using 'goto Quit' as it provides a single point of exit which is the most reliable way to create solid code. And it also helps in avoiding memory leaks, plus the code looks tidy. But almost all programmers who allow the use of goto operators use them in only in case of an error during the function's execution to go to the Quit label (the ONLY label in the function). As for my opinion about using of goto operator, I personally don't see a problem with it as long as it's used correctly. Many programmers (especially purists) make difficulties for themselves avoiding a "goto Quit" operator. Use of it removes error handling from the function mainstream and lets you have one common block for error handling. It also keeps conditionals shorter and easier to read. Compare Code Style 1 with Code Style 4 to see how clean up code was moved to a separate section.

Code Style 4

BOOL Func(LPOBJECT *ppObject)
{
  LPOBJECT pObject1=NULL; LPOBJECT pObject2=NULL; LPOBJECT pObject3=NULL;
  BOOL bResult;
  bResult = CreateObject1(&pObject1);
  if(!bResult) goto Quit;
  bResult = CreateObject2(&pObject2);
  if(!bResult) goto Quit;
  bResult = CreateObject3(&pObject3);
  if(!bResult) goto Quit;
  etc ...
  bResult = TRUE;
Quit:
  if(!bResult)
  {
    // tidy up

    if(pObject2) DestroyObject2(&pObject2);
    if(pObject1) DestroyObject1(&pObject1);
  }
  else
  {
    (*ppObject) = pObject3; // pass back data

  }
  return bResult;
}

Now I am going to show you a couple other code styles that do not contain "problematic" goto's or return statements. The Code Style 5 method only works where each call has a unique failure value. In this case each function must return a boolean value to apply this style.

Code Style 5

int Funct()
{
  int iErr = ErrCode_NOERROR;
  if(!func1()) iErr = ErrCode_A;
  else if(!func2()) iErr = ErrCode_B;
  else if(!func3()) iErr = ErrCode_C;
  else if(!func4()) iErr = ErrCode_D;
  switch(iErr)
  {
  case ErrCode_D: CleanUp4();
  case ErrCode_C: CleanUp3();
  case ErrCode_B: CleanUp2();
  case ErrCode_A: CleanUp1();
  }
  return(iErr);
}

Here is another way, Code Style 6, to do what the previous code did. This is the proper way to do it. But, I don't like it as it makes code more difficult to read in my opinion. The indention in the code makes the use of the if..else structure clearer but gets cumbersome as the number of entries grows. Code Style 6 would be ideal for coding if it wouldn't look like a pile of if/else's when you have to call many functions within the main one.

Code Style 6

int Funct()
{
  int iErr = ErrCode_NOERROR;
  if (func1())
    if (func2())
      if (func3())
        if (func4())
          iErr = ErrCode_NOERROR;
        else
          iErr = ErrCode_D;
      else
        iErr = ErrCode_C;
    else
      iErr = ErrCode_B;
  else
    iErr = ErrCode_A;
  switch(iErr)
  {
  case ErrCode_D: CleanUp4();
  case ErrCode_C: CleanUp3();
  case ErrCode_B: CleanUp2();
  case ErrCode_A: CleanUp1();
  }
  return(iErr);
}

There are some solutions with the use of a throw/catch. I like the one for use with a class constructor. Although you can use the same idea for regular functions also.

Code Style 7

#define MyExceptions   long
#define SomeException1 1L
#define SomeException2 2L
CSomeClass::CSomeClass() throw(MyExceptions)
{
  try
  {
    DoSomeInitialization();
    if(SomeError) throw(SomeException1);
    DoMoreInitialization();
    if(SomeOtherError) throw(SomeException2);
  }
  catch(MyExceptions lEx)
  {
    switch(lEx)
    {
    case SomeException2: CleanUpSecondaryInitialization();
    case SomeException1: CleanUpPrimaryInitialization();
    }
    throw(lEx);
  }
}

The following lines show how to create the instance of CSomeClass.

Code Style 8

try
{
  pCSomeClass = new CSomeClass();
}
catch( MyExceptions lEx )
{
  switch( lEx )
  {
  case SomeException2: Actions2(); break;
  case SomeException1: Actions1(); break;
  }
}

The following style removes some of the redundancy represented by nested if/elses in Code Style 6 at the expense of extra checking of the bSuccess variable. From the point of view of operating speed Code Style 6 is faster then Code Style 9 as all "if(bSuccess)" lines will be performed regardless of the fact that some error occured. You might want to add a clean up section and return error codes like in Code Style 6.

Code Style 9

BOOL Funct()
{
  BOOL bSuccess;
  bSuccess = funct1();
  if(bSuccess) {
    bSuccess = funct2();
  }
  if(bSuccess) {
    bSuccess = funct3();
  }
  if(bSuccess) {
    bSuccess = funct4();
  }
  return bSuccess;
}

I think that good code is a reasonable trade-off between good style, speed and robustness. My code samples show many styles of function coding. All of them have their own merits. Make your own decision of coding style that suits your needs. Consider future modifications, also. If you choose the right style, when you later make changes you will not need to change too much code; otherwise you might be forced to rewrite the whole function. Finally, don't try to cut and paste my samples of styles into your code, they won't work in the form I gave you as they are just the ideas, not ready to use snippets.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here