Catching and Throwing Exceptions in Fortran 90

When a method can throw an exception (i.e., its SIDL definition has a throws clause), a variable should be passed to hold an exception. For maximum backward compatibility, the exception argument type is a sidl.BaseInterface pointer that is assumed to implement the sidl.BaseException interface. The exception argument should appear after the return value when both occur in a method, and it behaves like an out parameter. After the call, the client should test this argument using is_null or not_null. If it is not_null, an exception was thrown by the method, and the method should respond appropriately. When an exception is thrown, the value of all other arguments is undefined, and the best course of action is to ignore their values. If your code does not check the exception argument after each call that can throw an exception, any exceptions that are thrown will be utterly ignored; they will not propagate automatically to higher level routines.

Here is another example adapted from the Babel regression tests. Package ExceptionTest has a class named Fib with a method declared in SIDL as follows:


int getFib(in int n, in int max_depth, in int max_value, in int depth)
    throws NegativeValueException, FibException;

Here is the outline of a FORTRAN 90 code fragment to use this method.


  use ExceptionTest_Fib
  use ExceptionTest_FibException
  use ExceptionTest_NegativeValueException
  use sidl_BaseInterface
  type(ExceptionTest_Fib_t)                    :: fib
  type(sidl_BaseInterface_t)                   :: except
  type(ExceptionTest_FibException_t)           :: fibexcept
  type(ExceptionTest_NegativeValueException_t) :: nvexcept
  integer (selected_int_kind(9))  :: index, maxdepth, maxval, depth, result
  call new(fib)

  index    = 4
  maxdepth = 100
  maxvalue = 32000
  depth    = 0
  call getFib(fib, index, maxdepth, maxvalue, depth, result, except)
  if (not_null(except)) then
    call cast(except, fibexcept)
    if (not_null(fibexcept)) then
!      do something here with the FibException
    else
      call cast(except, nvexcept)
!     do something here with the NegativeValueException
    endif
    call deleteRef(except)
  else
    write (*,*) 'getFib for ', index, ' returned ', result
  endif
  call deleteRef(fib)

Here is an example of an implementation subroutine that throws an exception. Note you must cast the returned exception object into the exception out parameter. The setNote method provides a useful error message, and the add method helps provide a multi-language traceback capability (provided each layer of the call stack calls add).


recursive subroutine ExceptionTest_Fib_getFib_mi(self, n, max_depth, &
  max_value, depth, retval, exception)
  use sidl_BaseInterface
  use ExceptionTest_Fib
  use ExceptionTest_NegativeValueException
  use ExceptionTest_FibException
  use ExceptionTest_Fib_impl
  ! DO-NOT-DELETE splicer.begin(ExceptionTest.Fib.getFib.use)
  use ExceptionTest_TooBigException
  use ExceptionTest_TooDeepException
  ! DO-NOT-DELETE splicer.end(ExceptionTest.Fib.getFib.use)
  implicit none
  type(ExceptionTest_Fib_t) :: self
  integer (selected_int_kind(9)) :: n, max_depth, max_value
  integer (selected_int_kind(9)) :: retval, depth
  type(sidl_BaseInterface_t) :: exception
! DO-NOT-DELETE splicer.begin(ExceptionTest.Fib.getFib)
  type(ExceptionTest_NegativeValueException_t) :: negexc
! ...lines deleted...
  character (len=*) myfilename 
  parameter(myfilename='ExceptionTest_Fib_Impl.f')
  retval = 0
  if (n .lt. 0) then
     call new(negexc)
     if (not_null(negexc)) then
        call setNote(negexc, &
             'called with negative n')
        call add(negexc, myfilename, 57, &
                 'ExceptionTest_Fib_getFib_impl')
        call cast(negexc, exception)
        return
     endif
  else
! ...numerous lines deleted....
! DO-NOT-DELETE splicer.end(ExceptionTest.Fib.getFib)
end subroutine ExceptionTest_Fib_getFib_mi

Please note that when your code throws an exception it should deleteRef any references it was planning to return to its caller. Any caller of a method that returns an exception should ignore the values of out and inout parameters, so anything you do not free will become a reference and memory leak. In general, it is good practice to call set_null on all out and inout array, class and interface arguments before returning when throwing an exception. This makes things work out better for clients who forget to check if an exception occurred or willfully choose to ignore it.



babel-0.10.2
users_guide Last Modified 2005-03-23

http://www.llnl.gov/CASC/components
components@llnl.gov