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