CSC/ECE 517 Fall 2009/wiki1b 3 expHandle

From Expertiza_Wiki
Jump to navigation Jump to search

Exception-handling constructs have been around since the early 1970s, and sometimes it seems that all languages handle exceptions in pretty much the same way. Is that really true, or are there nuances of difference between the major o-o languages such as Smalltalk, C++, Objective C, Java, C#, Perl 5, Python, and Ruby? Look up how exceptions are handled in each of these languages, highlight and explain differences in their features.

Introduction

Exception Handling

Every functionality or use case in any application can have multiple outcomes. Depending on the result of the operation performed we have a happy flow, error flow or exception flow. The exception flow is triggered when some unexpected or unusual flow is initiated. This can be due to failure of external system failure or some unexpected user inputs. This can also be due to bad coding practices followed by the developer. Every language has some support for this kind of exception scenario. Either the exceptions are handled, thrown or initiated in a method. The mechanism differs in different languages, like in some languages like Java the entire cause of exception is made available till the point the exception is interpreted into a response, in languages like C’s atoi (ASCII to Integer) function the response if zero (0) even if the parsing fails, thus making it difficult to distinguish between a correct parsing of 0 or a parsing failure error.

Scope

Here let’s try and understand the way Exception Handling is implemented in various languages and then compare them based on

1) Ease of development

2) Simplicity on understanding

3) Resources freeing up (You will definitely want your code to close/release the file it tried to open or close a db Connection to the pool)

4) Error handling and transporting

Process of Exception Handling

Exception Handling in Perl 5

Legacy Perl (Perl 5.5 and earlier) is being discussed about and illustrated here. The newer advances and modules added in Perl 6 and the fix done on the issues (memory leak fixes for instance) is not been described here. Perl technically has a support for error handling rather than exception handling. They eval function is used to check for errors and the based on the status of "$@" variable the error handlers where written. The below code sample shows a sample implementation of this:

eval{
   #do some process 
   # do some more code
 };
 if ($@ == err1) {
   errorHandler1($@);
}if ($@ eq err2){
   errorHandler2($@);
}

The code eval function in case of an exception populates the $@ variable with the error message and terminates the execution of the script and transfers the control to line immediately after the eval block. In case of success, the $@ variable is assured of having a null string value The issue with this way of implementation is that the value variable $@ which is a scalar variable, can be easily changed either knowingly or accidently by the programmer. To add to it, the $@ variable is a global variable so the error information is available even in places where there exception should not have been transmitted as an exception. Also in case if one of the error code values is not checked for then that error goes unhandled and the correct information is not processed. This way of error handling also fails to give the location/trace of the exception making it difficult to ascertain the root cause. There was another way of implementation in Perl 5.005. In this way one could create a custom exception class and also invoke its method using the same syntax as in the earlier case.

eval{
   ....
 };
 if ($@) {
   if ($@->isa('MyFileException')) {  # Specific exception handler
     ....
   }
   else { # Generic exception handler
     ....
   }
 }

This also left some of the issues still unaddressed which are listed below:

  • eval blocks can be used for both, building dynamic code snippets as well as for exception handling. Hence can be confusing to look at and figure out the purpose on an eval block
  • No built in provision for cleanup handler i.e. the finally block. Hence may involve repetitive lines of code of same purpose.
  • Stack trace if required, needs to be maintained by writing custom code.

To handle the issues, the CPAN (Comprehensive Perl Archive Network) added two modules Error.pm and Exception::Class. The Error.pm is used in approximately 80 CPAN distributions and Exception::Class in 60 CPAN distributions. Following is the code example illustrating the use of the Error.pm module.

 use Error qw(:try);
 try{
   some code;
   code that might thrown an exception;
   more code;
   return;
 }
 catch Error with {
   my $ex = shift;   # Get hold of the exception object
   handle the exception;
 }
 finally {
   cleanup code;
 };

This implementation style is similar to JAVA. The first code line indicates that the particular class will be using the Error.pm module and the :try implies that the various modules for performing exception handling will be referenced and used. In this case the try block executes the code and exception if any will be caught in corresponding catch block. The catch block catches the corresponding exception. It internally invokes the ISA operation. The order of the catch block is important in the sense while catching the child exception class’s catch block should be first and then up the hierarchy to parent. There are two arguments that are sent to the catch block, one the exception and the second one isa scalar reference. If the scalar reference is set in the catch block, then the try block will treat the return from the catch block as if there is no error that has occurred. If the scalar variable is not set and the error is not thrown from catch, then the try block will return the result returned by the catch block. The finally block is the missing clean up block. The finally block is executed during the successful try processing and during an error scenario. There are two more blocks in the error handling mechanism “except” and “otherwise” block. The except block if found by the try block will be used to process the errors. The return value from this block should be a HASHREF or a list of key-value pairs, where the keys are class names and the values are CODE references for the handler of errors of that type. The otherwise block will catch any error that occurs in the try block. The only argument passed to this block is the type of error being processed. The exception handling mechanism provides a throws clause that allows one to raise an exception. The clause can be invoked from anywhere in the code. In case the try block is missing in the calling hierarchy, then the program will be terminated.

The key things here are:

  • OO-ish kind of error handling
  • The try block calls the catch block and the catch block returns the result to the try block.
  • The additional two blocks except and otherwise.

Exception Handling in Ruby

Like any other OO-language, Ruby too has an exception handling mechanism based on OO-ish concepts i.e. creation of an exception object of a particular class and allowing it a method to transfer the exception up the calling hierarchy. The exception handling in ruby contains a begin clause just like the try block where the code that can throw the exception is written, followed by the rescue clause to rescue the code from the exception with an ensure clause to ensure the tidying up job is done (close the file opened), ending with an end clause. There are some other options like raise, retry and else to raise an exception to be handled by caller or for re attempting the error cause with or without change in the input conditions. The else clause is used to configure a default handler. The code snippet below shows the way exception handling is implemented in ruby.

 begin
   ifile = File.open(opName,"r")
   buffer = ifile.read(512) 
   # code to work on the buffer 
   # more code
   # ..
   
   oFile = File.open(opName, "w")
   oFile.write(buffer);
 rescue SystemCallError
   print "System Error Occured: " + $!
   raise
 rescue StandardError => stdErrObj
   print "A general system failure: " + stdErrObj + " occured"
   raise
 end
 ensure
   ifile.close unless ifile.nil?
 end

The key for error handling is the variable $! which holds the exception description as returned by the begin clause as a result of some failure. When an exception occurs, the rescue block corresponding to the exception is invoked. In the example above if there is a SystemCallError then, the first rescue block is invoked. This is done by comparing the raised exception against each of the parameters in turn. If the raised exception matches the parameter, Ruby executes the code and stops looking. The match is made by using parameter === $!, which generally will mean that the exception named in the rescue clause will match the exception that is being raised. In case there is no matching rescue clause, then Ruby will move up the calling hierarchy and will try to find an exception handler in the caller, then the caller’s caller, so on. In Ruby the rescue clause can have multiple exceptions to be caught. In the example above, while rescuing the StandardError, the line StandardError => stdErrObj creates a local variable of the exsception object to be used in the rescue block instead of the $! variable.

The last block in the example is the ensure block. As the name suggests this block is for ensuring that the tidying up or any mandatory code that needs to be executed irrespective of the result of execution is written here. The ensure block can go even without the begin and rescue clauses.

The other two clauses that form a part of the Ruby exception handling package are the retry and raise clauses. Consider a system where we need to try logging into the system using different secure connection methods. The first attempt say uses SSL and the second one uses TLS. In case the first attempt fails we may want to reattempt using the different method before asking the user to re-enter the credentials. In such a case we use retry clause. The following code snippet exemplifies it:

connType = SSL
begin
 # First using SSL
 # Connection fails. Login fails
 rescue ProtocolError
 # Change the connection type
 If (connType == TLS)
  raise
 else 
  connType = TLS
 retry
end

The raise clause allows the developer to raise exceptions for the caller to handle it. The syntax for raise clause is as below

raise # raises a Current exception or a RuntimeError if no current exception is present.
raise “Wrong Connection” #a RuntimeError with “Wrong Connection” as the error message.
raise ConnectionException, “Wrong Connection type”, caller #A ConnectionException is raised with message and the stack trace of the error.

One can create custom exceptions and can have methods to add more information to it.

The key points in Ruby exception handling are:

  • Use of a try/catch/finally type of mechanism.
  • Use of $! variable to propagate the exception.
  • One rescue clause can be used to rescue/catch multiple exceptions.
  • Allows the flexibility to raise a RuntimeError exception with a message.
  • Gives the level from which the trace should be created.


Exception Handling in JAVA

JAVA provides a powerful mechanism from handling exceptions. It provides a try/catch/finally set of blocks along with a throws clause. The way an exception is triggered in JAVA is by using the throws clause. All the exceptions in JAVA fall under the hierarchy of java.lang.Exception class which in turn extends from java.lang.Throwable. The Exception class hierarchy in splits into RuntimeException and other exceptions. The exceptions that will occur during the program execution and cannot be predicted before hand, like for instance during File operations, a method can tell that it can face an issue during the operation and notifies the caller method of the possible exception that it will be throwing. For a NullPointerException which a type of RuntimeException, there is no way for the method to inform its caller and hence is called as unchecked exception. The notification is carried out by the use of the throws clause by the called method. The syntax for it is

public fileReader (String fileName) throws FileNotFoundException

One doesn’t have to throw all the exceptions that can happen in the called method. Only the exceptions that cannot be handled and are known that can be thrown during method execution. The throws clause can be used when one has to throw a new Exception. To throw it in that case all one has to do is:

throw new EOFException()

The exception thrown is handled by use of the try/catch/finally mechanism. The code snippet shows the syntax used:

try
{
//	open a file
//	read a file
} catch (FileNotFoundException fNFExp)
{
//	Get the exception message
} catch (IOException ioExp)
{
//	Get the exception message
} finally
{
//	close the file after opening it
}

In the try block the code that can potentially throw a checked exception is written. The exception is transferred as an object of a particular class. Instead of storing it in a fixed variable, JAVA stores the information in a new object of the exception class which is transferred by reference as a parameter to the catch clause. Then there are multiple catch blocks based on the number of potential checked exceptions. The JRE does and instanceOf check and identifies the corresponding catch block. Note that like in Perl the order of the catch clauses is important starting with the child exception till the parent exception. The control breaks out of the try block once there is an exception that is reported in the code execution and the control ends up in the corresponding catch clause. The output of the method is called the output the try block in case of success or the output of the catch block in case of an exception. The catch block can re-throw an exception of process it into some meaningful less severe output. The last block is finally block which is always invoked. As in rest of the languages this block is used for tidying purpose.

For creating custom exceptions one should have one’s class extend java.lang.Exception class or one of its child classes.

The key points here are:

  • The clear classification of exceptions based on if they can be predicted or not. This makes it easier during compile time for the JVM to identify the potential places where exception handler is needed.
  • The use of new object instead of a global variable for the exception propagation makes if difficult to change the exception object making it possible to retain most of the information.
  • In Java exceptions though powerful should be used cautiously as they are resource intensive.
  • The stack trace is available for the entire exception calls till handled by using the getStackTrace() method.
  • Since JAVA can use a lot of configuration, this can be coupled with the exception handling mechanism to get configure exception handlers.


Exception Handling in Smalltalk

Smalltalk is one of the earliest OO languages and has a mechanism similar to the other OO languages. The exceptions here are known as Signals and are handled by signal handlers. Unlike other languages which use the concept of try this and if fails error will be caught in a catch, Smalltalk first defines the handler and then asks the code to do something. The following in code snippet will help in understanding the concept more:

 aSignal
	handle:[:ex |
	    "this is the handler ... some handling code...
	     ex is the exception object."
	]
	do:[
	    "this is the computation"
	]

In this case whenever there is a signal of the type aSignal is raised due to the computation in the do: block, then the handler is invoked to handle the signal. The handler has an option of return to the caller method with an error message or an integer or gives a retry to do: computation (in case the file may be unlocked in a while to get an access) or reject the exception. When rejected the exception is handled by the next handler in the caller method. If there are not handlers defined then a new signal noHandlerSignal is initiated to raise an unhandled exception by default handlers. If this no defaultHandler, then the EmergencyHandler is invoked which opens a notifier window giving an option to debug.

The order of invocation of the handlers is as below:

1) enclosing #handle:do: handler, if any then (if no handler or rejected):

2) next enclosing #handle:do: handler(s), if any then (if none or rejected):

3) per-signal static handler block, if any then (if none or rejected):

4) NoHandler-signal #handle:do: handlers, if any then (if none or rejected):

5) NoHandler static handler-block, if any then (if none or rejected):

6) per-process emergency-handler-block, if any finally:

7) emergencyHandler defined in Exception-class

In Smalltalk the signals fall under the tree of the Object errorSignal and all the custom signals have errorSignal as parent. Hence in the handler chain it is necessary to maintain the handler hierarchy based on the signal.

The key features in Smalltalk exception handling are:

  • There is one handler block for sure for processing the error.
  • There is no block for tidying up the resources and hence we need to fix the things manually once each of happy and exceptional flow.


Exception Handling in Python

Python uses ‘try...except’ to handle exceptions and ‘raise’ to generate them. It is analogous to ‘try..catch’ and ‘throw’ constructs in C++, Java.

A general syntax is as follows.

 try:
    operations to be performed;
    ......................
 except Exception1:
    If there is Exception1 thrown, then this block is executed.
 except Exception2:
    If there is Exception2 thrown, then this block is executed.  
    .......................
 else:
    If there is no exception thrown then this block is executed. 

A single try statement can have multiple except statements. It is needed when the try block has statements that can throw different types of exceptions. If an exception occurs during execution of the try clause, the rest of the clause is skipped, then if its type matches the exception name mentioned after the ‘except’ keyword, the ‘except’ clause is executed.

A try...except block can have an else clause, like an if statement. If no exception is raised during the try block, the else clause is executed afterwards.

The except block with no exceptions: It is possible to write an exception clause with no exception name mentioned. It is used to handle any kind of exception.

 try:
    operations to be performed;
    ......................
 except:
    If there is any exception thrown, then this block is executed.
    ......................

The except block with multiple exceptions:

It is also possible to write same ‘except’ statement to handle multiple exceptions.

 try:
    operations to be performed;
    ......................
 except(Exception1, Exception2,...ExceptionN):
    If there is any exception thrown from the given exception list, 
    then this block is executed.   
    ......................

Argument of an Exception: An exception can also have an argument. It gives additional information about the exception.

 try:
    operations to be performed;  
    ......................
 except ExceptionType, Argument:
    the value of Argument can be printed here...

Raising an exception: You can raise an exception by using raise statement. It allows the programmer to force a particular exception to occur.An argument is optional.

raise ExceptionName(Arguments);

User-defined exceptions: A user can define its own exceptions by creating new exception class which is derived from the ‘Exception class’ either directly or indirectly.

Example:
  try:
 	  raise user_defined_name(2)
  except  raise user_defined_name as u:
	  print ‘The exception has encounter. Its value is-’, u.value

Finally clause: A finally clause is always executed irrespective of whether try-block raise an exception or not. In applications, it is useful for releasing external resources like files or database connections regardless of whether the program was successful.

 try:
    operations to be performed;
    ......................
    It might be skipped due to any exception.
 finally:
    This code is always executed.
    ......................

Exception Handling in C++

C++ uses try...catch blocks to handle exceptions and throw to generate them. The general format is as follows:

 try {
    ...
    ...
    the code to be guarded
    throw <write expression here>
    ...
    ...
 } catch( Arguments)
 {
    ...
    exception handling code
    ...
 }

The code inside the try block is the guarded section of code. The throw-expression throws (raises) an exception. The code inside the catch block is of the exception handler. It catches (handles) the exception thrown by throw-expression. Arguments for the catch block indicate the type of exception it can handle.

Program Flow:

1) If no exception occurs in the try block then rest of the try block is executed.

2) If an exception is thrown during execution of the guarded section of the try block, then try block displays a throw which specifies the type of error that occurred.

a) The compiler exits from the try block and looks for the first catch. If the first catch doesn’t match the error thrown, then it proceeds with the next catch till it finds the catch that matches its ‘thrown exception’.
b) If any of the catches match the thrown exception then its body gets executed.
c) If no catch matches the exception then there are two options.
i) If there is no catch that matches the exception then compiler calls the predefined run-time terminate() function.
ii) Another option is to include a catch whose argument is 3 periods: catch(…) which is called ellipsis. If there is such catch defined whose argument is three periods then its gets executed. The catch(…) is used if no other catch matches the thrown exception. If this catch(…) is defined, it must always be the last catch, unless it is the only catch.

Unhandled C++ exceptions: C++ doesn’t provide facility of including a block like finally in Java. If a matching handler (or ellipsis catch handler) is not found for the exception, then run-time method terminate() is called which eventually calls abort. If you want terminate() to call some other function in your program before exiting the application, then you can call set_terminate function and pass the name of the function to be called as an argument.

Order of catch handlers: The order in which catch handlers appear is very important, because try block checks the catch blocks in the order of their appearance. Once a matching catch handler is found, further catch handlers are simply ignored. Hence ‘ellipsis’ catch handler should be the last catch handler for the try block.

Example code: Here the variable ‘number’ is of type integer. Hence if the exception is thrown by the throw-expression, first catch block is executed because the data-type of ‘number’ matches with the type of parameter of first catch block.

 int number=5;
 try
    {                                                                           
 	throw number;
    }                                                                           
 catch(int int_number)
    {                                                                        
     cout << "Caught an exception with integer value: " << int_number
     << endl;                                                           
    }                                                                           
 catch(float float_number)
    {                                                                           
    cout << "Caught an exception with float value: " << float_number      << endl;                                                           
    }                                                                           
 catch(...)   // catch anything else
    {                                                                           
    cout << "Caught an exception with type neither integer nor float" << endl;
    }


Exception handling in Objective C

Objective-C’s exception handling revolves around 4 compiler directives. @try, @catch, , @finally, @throw.

1. @try block: It contains code that can potentially throw an exception.

2. @catch block: It contains exception-handling logic for exceptions thrown in a @try block. You can write multiple @catch blocks for handling different types of exceptions.

3. @finally block: A @finally block contains code that must be executed whether an exception is thrown or not.

4. @throw block: It is used to throw an exception, which is essentially an Objective-C object. This is usually an NSException object, but you can use any.


Throwing Exception: For throwing an exception, you should instantiate an object with the appropriate information, such as the exception name and the reason it was thrown. Example:

NSException *exception = [NSException exceptionWithName:@"myException"
                           reason:@"whatever"  userInfo:nil];
@throw exception;


Catching different types of exceptions: For catching an exception thrown in a @try block, one or more @catch()blocks are used following the @try block. The @catch() blocks should be ordered from most-specific to the least-specific.

@try {
    ...
}
@catch (SomeException *se) {
    // Catches a most specific exception type.
    ...
}
@catch (NSException *ne) {
    // Handle general exceptions.
    ...
 
    // Rethrow the exception so that it's handled at a higher level.
    @throw;
}
@catch (id ue) {
   // Catch all thrown objects which have not been caught by previous    catch blocks.
    ...
}
@finally {
    // Performs any clean-up processing, whether an exception occurred or not.
    ...
}

A @throw statement may appear anywhere in the program. When it is used inside of a @catch block, the @throw may appear without an argument, in which case the object caught by the @catch will be rethrown.

When an object is thrown, it will be caught by the nearest @catch block capable of handling objects of that type.

A @catch(id ...) block (as shown above) may also be provided to catch any Objective-C exceptions not caught by previous @catch blocks (if any).

The @finally clause will be executed after exit from the immediately preceding @try ... @catch section. It will get executed regardless of whether any exceptions are thrown or not. It’s analogous to the behavior of the finally clause in Java.


Exception handling in C#

C# makes use of try, catch , finally blocks for handling exceptions.

1) The ‘try’ block contains the code that could throw an exception.

2) The code for handling the exception is written inside the ‘catch’ block.

3) The ‘finally’ statement block represents a block of code you want to have executed whether an exception is thrown or not.

For example, you might have used some resource in ‘try’ statement block (like opening a file or database or network connection) that needs to be closed when you're finished using it. By placing these required statements in ‘finally’ block, you make sure that they get executed irrespective of exceptions. This ‘finally’ block is optional.

Basic structure:

try
{
// Statement which can cause an exception.
}
catch(Type t)
{
// Statements for handling the exception
}
finally
{
// cleanup code
} 

Multiple Catch Blocks:

- A try block can throw multiple exceptions, which can be handled by using multiple catch blocks. Multiple catch blocks are evaluated from top to bottom, but only one catch block is executed for each exception thrown. The first catch block that matches the exact type or a base class of the thrown exception will be executed.

- If no catch block matches the type of exception, then a catch block without any exception type (if any) will be executed.

Example:

public static void Main()
{
   try
   {
      // Code which can throw exception
   }
   catch(System.Net.WebException exp)
   {
      // Process a WebException
   }
   catch(System.Exception)
   {
      // Process a System level exception      
   }
   catch
   {
      // Process all the uncaught exceptions
   }
   finally
   {
      // (optional) code that will always be executed
   }
}

Throwing an Exception: Exceptions can be explicitly generated by a program by using the throw keyword. The general form of throwing an exception is as follows.

try
{
throw exception_obj;
}

Standard exceptions: In C#, System.Exception is the base class for all exceptions. Several exception classes inherit from this class including ApplicationException and SystemException. These two classes form the basis for most other runtime exceptions. IOException, WebException etc. derive directly from System.Exception. C# also provides the facility for creating user-defined exceptions.

Comparison and Conclusion

The concept of exception handling in various O-O languages is pretty much the same, but there are some subtle differences. The level on understanding the code is subjected to the verbose nature of language. Perl for instance uses the same eval function for the evaluation of dynamic blocks and exception handling, Smalltalk changes the order of the blocks. These can lead into confusion while looking at the code. In Perl the control starts and ends in try block hence there is chance of code execution even after a previous check returned and exception.

Perl and Smalltalk fail to provide a mechanism like finally block. This causes either inefficient resource utilization by not freeing up of resources during exceptions or redundant lines of code.

Perl and Ruby use a global variable to transport the exception across the layers. This exposes the possibility of corruption of the variable knowingly or unknowingly by the developer.

Thus due to the limitations of each of the languages there are some subtle differences in the exception handling mechanism among the OO languages, but the core concept still is the same.

References

1. Perl 5 Wiki

2. O'Reilly Article on Exception Handling in Perl

3. Core Java Volume 1 by Cay S. Horstmann and Gary Cornell

4. Microsoft Development Network (MSDN) Documentation on C++

5. FunctionX Tutorial on Exception Handling in C++

6. Python v2.6.2 documentation

7. Tutorials Point's Python Tutorial

8. Microsoft Development Network (MSDN) Documentation on C#

9. C# station tutorial

10. Apple's documentation on Objective C

11. GNU Compiler Collection Documentation on Objective C

12. Advanced Smalltalk by Jonathan Pletzke