Click Here!
home account info subscribe login search FAQ/help site map contact us


 
Brief Full
 Advanced
      Search
 Search Tips
[an error occurred while processing this directive]
Previous Table of Contents Next


10.1 Handle runtime errors in my programs?

Problem

I want to be able to handle errors that occur in my programs and either recover from them or report them and exit my program gracefully. How can I do this in C and C++ programs?

Technique

This How-To will discuss the basic method used to handle runtime errors in C programs—returning error codes from functions or methods.

When an error is encountered in a typical function, the best thing to do is to handle the error and proceed. However, this might not be possible. For example, if you allocate some memory using malloc, calloc, or new, and your system is out of memory, you might not be able to continue execution of your program. In some cases, you might be able to free some memory that is not being used and continue.

But what if you are calling a function that runs into an error such as a problem allocating memory or in accessing a database? If that function does not return a code to indicate the success or failure of the function, how will callers of the function be able to determine what action to take?

Worse yet, what if the function allocating the memory or accessing the database just decides to shut down the application if an error occurs? This could be disastrous.

Most applications must be able to recover from errors. Many real-time systems, such as point-of-sale systems in stores, access databases to get up-to-the-minute information, such as product prices or information. Therefore, it is important for functions that make up an application to return values that indicate the success or failure of the function. Using these return codes, callers of this function will have the chance to recover from an error. In a case in which recovery is not possible, at least the caller will be able to write to a log file and shut down the application gracefully.

Steps

1.  Analyze each function in your application and determine the set of return codes for each function. For example, in the point-of-sale system, a function that adds records to a database (addRecord for example) might return the values listed in Table 10.1.
Table 10.1 Return Values from the addRecord Function
Return Value Description
-1 Database could not be opened.
-2 Record could not be added.
0 Record added successfully.
1 Duplicate record added
2.  Trap error conditions in your functions. This means it might be necessary to test the return values of other functions your function is calling. For example, in the addRecord function, one necessary step that might need to be taken is opening the database. If the database cannot be opened, the caller might wish to attempt this operation again on a backup database. Therefore, the addRecord function must tell the caller that an error has occurred. The following is code that would appear in the addRecord function. This code tests the opening of a database and returns -1 as indicated in Table 10.1.
if (open(databaseName) < 0)
   return -1;
3.  Maintain return values throughout your function. Your function might perform many actions. Because it’s best to return from a function in only one place, return error and success values only at the end of your function. The addRecord function will open the database, search for an existing record, and add the record to the database. This application allows duplicate records to be added to the database. However, if a duplicate record is found, addRecord tells the caller by returning a success code of 1 as indicated in Table 10.1. The following is the entire code for the addRecord function. This function just happens to be a method in the ProductDatabase class.
int ProductDatabase::addRecord(DatabaseType dbType,
			            const ProductRecord& record)
{
   int retVal = 0;
   bool openedByMe = false;

   // Open the database if it’s not already open.
   if (_hDatabase == NULL)
   {
      if (open(_databases[dbType]) < 0)
      {
	 retVal = -1;
      }

      openedByMe = true;
   }

   if (retVal == 0 && _hDatabase != NULL)
   {
      if (findProduct(record.prodNumber()) == 0)
	 retVal = 1;

      // Attempt to add the record;
      //
      if (fprintf(_hDatabase, “%ld %s\n”, record.prodNumber(),
      ⇒record.prodName()) == 0)
	  retVal = -2;

      // Close the database.
      if (openedByMe)
	 close();
   }

   return retVal;
}
4.  Handle the error returned from the function. The following code calls the addRecord function and then tests the return value. It then displays a message to tell the user what happened.
void main()
{
   ProductDatabase db(“c:\\databases\\primary.db”,
		         “c:\\databases\\secondary.db”);

   Product product(“Joe’s Best Detergent”);

   int retVal;
   retVal = db.addRecord(ProductDatabase::Primary, product);

   if (retVal == -1)
   {
      cout << “Error opening Primary database.”
	     << endl;

      retVal = db.addRecord
      ⇒(ProductDatabase::Secondary, product);

      if (retVal == -1)
      {
	 cout << “Error opening Secondary database.”
	      << endl;
      }
   }

   if (retVal != -1)
   {
      if (retVal == -2)
      {
	 cout << “Error adding record number “
	      << product.prodNumber() << endl;
      }
      else if (retVal == 1)
      {
	 cout << “Added duplicate record number “
	      << product.prodNumber() << endl;
      }
      else
      {
	 cout << “Added record number “
	      << product.prodNumber() << endl;
      }
   }
}


Previous Table of Contents Next


Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-1999 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permision of EarthWeb is prohibited.