The recent versions of Fortran have error management for specific operations that can fail, but you can recover from error and stop program from crashing. Modern Fortran has introduced four main areas for error capture:
All the external file handling statements and I/O operations can take iostat and iomsg clauses. iostat is an integer which returns a non-zero value if there is an error, in which case, the character variable assigned to iomsg will return a brief error message.
program error
integer :: io_stat
character (len=256) :: io_msg
! try to open a file that do not exist
open (file='myFile.dat', unit=10, &
access = "append", status="old", &
iostat=io_stat, iomsg=io_msg)
! analyze the error
if (io_stat/=0) then
write (*,*) "Open myFile.dat failed."
write (*,*) "iostat = ", io_stat
write (*,*) "iomsg = ", trim(io_msg)
else
write (*,*) "File was open succesfuly."
close (unit=10)
end if
end program
&ft;./error
Open myFile.dat failed.
iostat = 2
iomsg = Cannot open file myFile.dat: No such file or directory
This is a big topic, but in essence modern Fortran provides access to three intrinsic modules: IEEE_arithmetic, IEEE_exceptions and IEEE_features. These features can be used to intercept errors such as divide by zero and overflow but at the expense of some performance.
The IEEE_features module controls access to the features the programmer may require, by use association in the scoping unit where the programmer places the use statement
subroutine test
use, intrinsic :: ieee_features
! ...
end subroutine
Modern Fortran allows run-time allocation and deallocation of arrays of any type, and a typical error might be to try to dynamically allocate an array so large that there is not enough memory, or an attempt to deallocate an array which is not already allocated. There are optional clauses stat and errmsg which can be used to prevent program failure and allow the programmer to take evasive action.
real, allocatable, dimension (:) :: x
integer :: my_stat
character (256) :: my_errmsg
allocate (x(100000000), stat=my_stat, errmsg=my_errmsg)
if (my_stat/=0) then
write(*,*) 'Failed to allocate memory for x'
write(*,*) 'stat: ', my_stat
write(*,*) 'errmsg: ', trim(my_errmsg)
end if
In this example, below the external program "test.exe" is executed in a separated process. You can capture the error code and message in local variables and then use this information to take next action: Print the error message and stop the program.
integer :: my_cmdstat
character (256) :: my_cmdmsg
call execute_command_line('test.exe', cmdstat=my_cmdstat, cmdmsg=my_cmdmsg )
if (my_cmdstat/=0) stop
The best practice in Fortran is to prevent errors in the first place by using preconditions. These are conditional statements you can make on arguments so that you do not encounter an error.
Though there is no official framework to handle errors in Fortran, there is third-party software that you can depend upon. Please investigate before implementing your own system.
To find runtime errors you need to create additional code for testing. This can be data driven test or unit test. You can run the unit test, fix the code and run again until you eliminate all possible errors.
The third practice is to enable a subprogram to raise an error. For this you must create an error result or output. You must detect the error and analize it using logic expressions in the main program. This is called "error handling".
You can define error codes as numeric parameters. Usually we use uppercase letters to define these codes. A function can return an error code. A subroutine ca have an input/output argument that can signal an error and also an optional output argument that give you back the error message.
After the call, if the status return is present, the caller can decide how to handle the error. If it is absent, you can stop the program with an error message and status > 0.
This is is an effective design that lets the caller choose either a simple procedural method, or some form of error handling is necesary. The problem with this system is that you don't know exactly where the error was signaled. What line of code.
module raise
public process
contains
subroutine process(test, err)
integer, intent(in) :: test
integer, intent(inout) :: err
if (test > 10) then
err = ERR_INVALID; return
else if (test < 5) then
err = ERR_DEFAULT; return
else
do i=1, test
write (*,'(i3)', advance="no") i
end do
print *, ""
err = ERR_NONE
end if;
end subroutine
end module
program main
use raise
! define 3 error coddes
integer, parameter :: &
ERR_NONE = 0, &
ERR_DEFAULT = 1, &
ERR_INVALID = 2
integer:: count, error
write (*,'(a7)', advance="no") "count:"
read *, count
call process (count, error)
if (error == ERR_NONE) then
print *, "done. no errors!"
else if (error == ERR_DEFAULT) then
print *, "too small: < 5"
else if (error == ERR_INVALID) then
print *, "too large: > 10"
end if;
contains
end program
>gfortran raise.f95 -o raise
>./raise
count:1
too small: < 5
>./raise
count:7
1 2 3 4 5 6 7
done. no errors!
> ./raise
count:12
too large: > 10
>./raise
count:10
1 2 3 4 5 6 7 8 9 10
done. no errors!
>
Modern fortran enable you to define derived type "error". This can have a code and a message. Any subroutine or function can return a variable of type "error". After calling the subroutine you can analyze the error object and hanlde the error.
type :: ErrorType
integer :: code, line
character(len=256) :: message
end type
Lucking Tech Notes: Fortran Error Handling
Read next: Parallel Computing