VCLib Documentation  6.12.2

Further Programming Tips

Further Programming Tips

General Programming Tips

Some tips in here may be more or less helpful depending on the features of the platform the code is running at — for example, on a linux based machine with a memory management unit it may be faster to use a debugger on a coredump instead of interpreting a long error code to find the error location.

Error Code Propagation

In case of errorneous behaviour one can build up an error number which points to the error position by successive prepending to the error code. This needs an error to be always a negative number at the functions, and the nesting should not be too deep. The error code can then be rewinded to the error location.

Drawback of this method is, that the error code prepension must be updated if code is added.

Here is the example code:

I32 succeeds()
{
if(0){ return( 0); }
if(1){ return(+1); }
}
I32 fails()
{
if(0){ return(-1); }
if(0){ return(-2); }
if(1){ return(-3); }
}
I32 calls_fails()
{
I32 rc; //'R'eturn 'C'ode
rc = succeeds();
if(rc<0){ return(-1+10*rc); }
rc = fails();
if(rc<0){ return(-2+10*rc); }
if(0){ return( 0); }
if(1){ return(+1); }
}
void demo()
{
I32 rc;
rc = calls_fails();
if(rc<0)
{
printf("calls_fails() returns Error Nr. %d\n", rc);
}
else
{
printf("calls_fails() succeeds with message Nr. %d\n", rc);
}
}

The example code will output an error with number -32, so one can search back for the last number at the function call_fails(), the -2, which is an error of the fails() routine, and search the fails() routine for the remaining number -3.

Allocation/Deallocation Management

Since C does not provide an automatic Memory Management, and Providing verbose error message is desirable but it disturbs the proessing code, the following technique may be interesting for you.

An example follows directly after the 'verbose' formulation. A word beforehand: The "goto's" behaviour needed for this functionality is controlable, hence no kind of bad style, because it actually makes the code more readable.

  1. Each Pointer to be allocated must be set to NULL first.
  2. Declare an additional variable which provides the actual status of the function.
  3. If an error occurs, set the status variable to a (mostly) unique error code and jump to the end of the function.
  4. There the error code will be interpreted and out-of-main-function-code messages can be provided.
  5. Then memory deallocation is only done if the pointer is not NULL, and the pointer is directly set to NULL afterwards, so this deallocation can also take place anywhere in the middle of the function without destroying this deallocation management.

Here is the example; variable names are verbose for clearness:

void demo()
{
I32 i32Status, i32Result;
U8 *pu8 = NULL;
I8 *pi8 = NULL;
pu8 = byte_malloc( sizeof(U8) * 1000 );
if(NULL == pu8){ i32Status = -99; goto ENDFUNC;}
pi8 = byte_malloc( sizeof(I8) * 1000 );
if(NULL == pi8){ i32Status = -99; goto ENDFUNC;}
i32Result = failsEventually();
if(ERR_NONE != i32Result){ i32Status = -1; goto ENDFUNC; }
i32Result = alsoFailsEventually();
if(ERR_NONE != i32Result){ i32Status = -2; goto ENDFUNC; }
i32Status = 0;
ENDFUNC:
switch(i32Status)
{
case 0:
printf("%s(): Success!\n", __FUNCTION__);
//returns ERR_NONE on Success.
i32Result = ERR_NONE;
break;
case -99:
printf("%s(): Error, Memory Allocation Faiure!\n", __FUNCTION__);
//returns ERR_MEMORY on Memory Allocation Failure.
i32Result = ERR_MEMORY;
break;
case -1:
case -2:
printf("%s(): Error, %s() returned with error code %d!\n", __FUNCTION__,
(-1 == i32Status)?("failsEventually")
:(-2 == i32Status)?("alsoFailsEventually")
:("ERROR"), i32Result);
//returns -1234 if some inner function fail.
i32Result = -1234;
break;
default:
printf("%s(): Error, Unknown Error Code (%d)!\n", __FUNCTION__, i32Status);
//returns -4711 if internal error code is wrong.
i32Result = -4711;
break;
}
if(NULL != pu8){ byte_free(pu8); pu8 = NULL; }
if(NULL != pi8){ byte_free(pi8); pi8 = NULL; }
return(i32Result);
}