- •Introduction
- •Saving Time with This Book
- •Conventions Used in This Book
- •Part II: Working with the Pre-Processor
- •Part III: Types
- •Part IV: Classes
- •Part V: Arrays and Templates
- •Part VI: Input and Output
- •Part VII: Using the Built-in Functionality
- •Part VIII: Utilities
- •Part IX: Debugging C++ Applications
- •Part X: The Scary (or Fun!) Stuff
- •Icons Used in This Book
- •Creating and Implementing an Encapsulated Class
- •Creating a Mailing-List Application
- •Testing the Mailing-List Application
- •Customizing a Class with Polymorphism
- •Testing the Virtual Function Code
- •Why Do the Destructors Work?
- •Delayed Construction
- •The cDate Class
- •Testing the cDate Class
- •Creating the Header File
- •Testing the Header File
- •The Assert Problem
- •Fixing the Assert Problem
- •Using the const Construct
- •Identifying the Errors
- •Fixing the Errors
- •Fixing What Went Wrong with the Macro
- •Using Macros Appropriately
- •Using the sizeof Function
- •Evaluating the Results
- •Using sizeof with Pointers
- •Implementing the Range Class
- •Testing the Range Class
- •Creating the Matrix Class
- •Matrix Operations
- •Multiplying a Matrix by a Scalar Value
- •Multiplying a Matrix by Scalar Values, Take 2
- •Testing the Matrix Class
- •Implementing the Enumeration Class
- •Testing the Enumeration Class
- •Implementing Structures
- •Interpreting the Output
- •Defining Constants
- •Testing the Constant Application
- •Using the const Keyword
- •Illustrating Scope
- •Interpreting the Output
- •Using Casts
- •Addressing the Compiler Problems
- •Testing the Changes
- •Implementing Member-Function Pointers
- •Updating Your Code with Member-Function Pointers
- •Testing the Member Pointer Code
- •Customizing Functions We Wrote Ourselves
- •Testing the Default Code
- •Fixing the Problem
- •Testing the Complete Class
- •Implementing Virtual Inheritance
- •Correcting the Code
- •Rules for Creating Overloaded Operators
- •Using Conversion Operators
- •Using Overloaded Operators
- •Testing the MyString Class
- •Rules for Implementing new and delete Handlers
- •Overloading new and delete Handlers
- •Testing the Memory Allocation Tracker
- •Implementing Properties
- •Testing the Property Class
- •Implementing Data Validation with Classes
- •Testing Your SSN Validator Class
- •Creating the Date Class
- •Testing the Date Class
- •Some Final Thoughts on the Date Class
- •Creating a Factory Class
- •Testing the Factory
- •Enhancing the Manager Class
- •Implementing Mix-In Classes
- •Testing the Template Classes
- •Implementing Function Templates
- •Creating Method Templates
- •Using the Vector Class
- •Creating the String Array Class
- •Working with Vector Algorithms
- •Creating an Array of Heterogeneous Objects
- •Creating the Column Class
- •Creating the Row Class
- •Creating the Spreadsheet Class
- •Testing Your Spreadsheet
- •Working with Streams
- •Testing the File-Reading Code
- •Creating the Test File
- •Reading Delimited Files
- •Testing the Code
- •Creating the XML Writer
- •Testing the XML Writer
- •Creating the Configuration-File Class
- •Setting Up Your Test File
- •Building the Language Files
- •Creating an Input Text File
- •Reading the International File
- •Testing the String Reader
- •Creating a Translator Class
- •Testing the Translator Class
- •Creating a Virtual File Class
- •Testing the Virtual File Class
- •Using the auto_ptr Class
- •Creating a Memory Safe Buffer Class
- •Throwing and Logging Exceptions
- •Dealing with Unhandled Exceptions
- •Re-throwing Exceptions
- •Creating the Wildcard Matching Class
- •Testing the Wildcard Matching Class
- •Creating the URL Codec Class
- •Testing the URL Codec Class
- •Testing the Rot13 Algorithm
- •Testing the XOR Algorithm
- •Implementing the transform Function to Convert Strings
- •Testing the String Conversions
- •Implementing the Serialization Interface
- •Creating the Buffer Class
- •Testing the Buffer Class
- •Creating the Multiple-Search-Path Class
- •Testing the Multiple-Search-Path Class
- •Testing the Flow Trace System
- •The assert Macro
- •Logging
- •Testing the Logger Class
- •Design by Contract
- •Adding Logging to the Application
- •Making Functions Inline
- •Avoiding Temporary Objects
- •Passing Objects by Reference
- •Choosing Initialization Instead of Assignment
- •Learning How Code Operates
- •Testing the Properties Class
- •Creating the Locking Mechanism
- •Testing the Locking Mechanism
- •Testing the File-Guardian Class
- •Implementing the Complex Class
- •Creating the Conversion Code
- •Testing the Conversion Code
- •A Sample Program
- •Componentizing
- •Restructuring
- •Specialization
- •Index
53 Throwing, Catching,
and Re-throwing
Technique Exceptions
Save Time By
Understanding exception handling
Throwing and logging exceptions
Dealing with unhandled exceptions
Re-throwing exceptions
Understanding structured exception handling
C++’s exception handling ability is a feature that differentiates it from virtually all older programming systems. As with Java, C#, and other modern programming languages, C++ offers the ability to jump out
of the middle of a block of code when an exceptional event occurs.
The concept of an exception handling is really quite simple. In older programming languages, when errors occurred, an error code was sent to the calling application, which could — and often did — simply ignore it. Exception handling changes this. It forces the application developer to consider in advance what could go wrong in the lower-level code, and to provide routines to contend with any errors that crop up. Now when an error — that is, an exception — occurs, the program passes control to the appropriate predefined routine. Error handling ensures that errors aren’t ignored; they’re dealt with.
This technique takes a closer look at throwing and catching exceptions. First, we examine throwing an exception and logging it in a generic fashion. Logging errors is important because it allows you to provide a complete debugging log that can be used to see what went wrong when a problem occurs. This will save you time and effort in debugging your application, and results in better code for the end-user.
Throwing and Logging Exceptions
In order to best understand how to use exception handling in your own applications, let’s look at a simple example of throwing exceptions in which those exceptions are logged to an output error file that can be used for debugging purposes. To do this, we will need two different types of classes.
We need a class to hold the information about what went wrong. This class will contain the line number where the error occurred and information detailing the nature of the error.
We need a class that will manage the process of catching the exception and logging the information into an error log.
|
|
Throwing and Logging Exceptions |
313 |
|
The following steps show you how this is done: |
2. Type the code from Listing 53-1 into your file. |
|
||
1. In the code editor of your choice, create a new |
Better yet, copy the code from the source file on |
|||
this book’s companion Web site. |
|
|
||
file to hold the code for the technique. |
|
|
||
|
|
|
||
In this example, the file is named ch53.cpp, |
|
|
|
|
although you can use whatever you choose. This |
|
|
|
|
file will contain the source code for our classes. |
|
|
|
|
LISTING 53-1: THE EXCEPTION HANDLING CLASSES |
|
|
|
|
#include <iostream> |
|
|
|
|
#include <string> |
|
|
|
|
#include <fstream> |
|
|
|
|
#include <stdio.h> |
|
|
|
|
using namespace std; |
|
|
|
|
class ExceptionClass |
|
|
1 |
|
{ |
|
|
|
|
string _message; |
|
|
|
|
string |
_file; |
|
|
|
long |
_line; |
|
|
|
public: |
|
|
|
|
ExceptionClass(void)
{
_message = “Unknown Exception”;
}
ExceptionClass( const char *msg, const char *fileName, long lineNo )
{
_message |
= msg; |
|
_file |
= |
fileName; |
_line |
= |
lineNo; |
}
ExceptionClass( const ExceptionClass& aCopy )
{
_message |
= aCopy._message; |
|
_file |
= |
aCopy._file; |
_line |
= |
aCopy._line; |
}
void setMessage(const char *msg, const char *fileName, long lineNo )
{
_message |
= msg; |
|
_file |
= |
fileName; |
_line |
= |
lineNo; |
}
(continued)
314 Technique 53: Throwing, Catching, and Re-throwing Exceptions
LISTING 53-1 (continued)
virtual string Report(void) const
{
string out;
out = “Exception reported in file “; out += _file.c_str();
out += “ at line “; out += _line; return out;
}
virtual ostream& Report( ostream& out ) const
{
out << “Exception reported in file “ << _file.c_str() << “ at line “ << _line << endl; out << _message.c_str() << endl;
return out;
}
}; |
|
|
|
class ExceptionCatcher |
|
2 |
|
{ |
|
|
|
private: |
|
|
|
string |
_message; |
|
|
ofstream |
_logFile; |
|
|
string |
_fileName; |
|
|
public: |
|
|
|
ExceptionCatcher( void )
{
string msg = “Startup”; LogMessage( msg );
}
ExceptionCatcher( const char *fileName ) : _logFile( fileName )
{
string msg = “Startup”; msg += “ [“;
msg += fileName; msg += “]”; LogMessage( msg );
}
ExceptionCatcher( const ExceptionCatcher& aCopy ) : _logFile ( aCopy._fileName.c_str() )
{
_fileName = aCopy._fileName; _message = aCopy._message; string msg = “Startup”;
msg += “ [“;
msg += _fileName; msg += “]”; LogMessage( msg );
Throwing and Logging Exceptions |
315 |
}
ExceptionCatcher( const ExceptionClass& exception )
{
_message = exception.Report();
}
virtual ~ExceptionCatcher()
{
string msg = “Shutdown”; LogMessage( msg );
}
virtual void LogMessage( string msg )
{
if ( !_logFile.fail() )
_logFile << msg.c_str() << endl;
}
virtual void LogMessage( const ExceptionClass& exception )
{
if ( !_logFile.fail() )
{
exception.Report( _logFile );
}
}
};
void process_option( int x )
{
if ( x < 2 || x > 8 )
throw “Invalid Input to process_option”;
int z = 10 / x;
cout << “Properly processed option “ << x << endl;
}
int func1( int x) throw( ExceptionClass )
{
ExceptionClass ec; try
{
switch ( x )
{
case 0:
cout << “You selected the first option” << endl; break;
case 1:
cout << “You selected the second option” << endl; break;
case 2:
process_option( x );
(continued)
316 |
Technique 53: Throwing, Catching, and Re-throwing Exceptions |
|
|
LISTING 53-1 (continued) |
|
|
|
|
default: |
|
|
|
ec.setMessage( “Invalid Option”, __FILE__, __LINE__ ); |
|
|
|
throw ec; |
|
|
|
} |
|
|
} |
catch ( const char *msg ) |
|
|
|
|
|
|
|
{ |
|
|
|
string sErr = “Unknown Error: “; |
|
|
|
sErr += msg; |
|
|
|
ec.setMessage( sErr.c_str(), __FILE__, __LINE__ ); |
|
|
|
throw ec; |
|
|
|
} |
|
|
} |
return 0; |
|
|
|
|
|
|
int main(int argc, char **argv) |
|
|
|
{ |
if ( argc < 2 ) |
|
|
|
|
|
|
|
{ |
|
|
|
cout << “Usage: ch6_9 <inputs>” << endl; |
|
|
|
cout << “Where: inputs is a series of numbers” << endl; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
ExceptionCatcher catcher(“errors.log”); |
|
|
|
// Process the inputs. |
|
|
|
for ( int i=1; i<argc; ++i ) |
|
|
|
{ |
|
|
|
int iVal = atoi( argv[i] ); |
|
|
|
try |
|
|
|
{ |
|
|
|
func1(iVal); |
|
|
|
} |
|
3 |
|
catch ( ExceptionClass& ec ) |
|
|
|
{ |
|
ec.Report( cout ); catcher.LogMessage( ec );
}
catch ( ... )
{
cout << “Caught an exception” << endl;
}
}
return 0;
}