Fortran中是否存在异常处理?

时间:2018-01-11 17:29:43

标签: exception error-handling fortran

Fortran中是否有任何异常处理结构,就像在Python中一样?

try:
    print "Hello World"
except:
    print "This is an error message!"

如果它不存在,处理异常的最简单方法是什么?

5 个答案:

答案 0 :(得分:11)

Fortran中不存在异常,所以不,没有异常处理。

但你可以做类似于使用标准Fortran的异常处理 - 甚至还有一篇论文Arjen Markus, "Exception handling in Fortran"

最常见的表示法是使用指示错误代码的(整数)返回变量:

subroutine do_something(stat)
    integer :: stat
    print "Hello World"
    stat = 0
end subroutine

并在主程序中执行

call do_something(stat)
if (stat /= 0) print *,"This is an error message!"

本文中描述了其他方法,例如为异常定义专用派生类型,该类型也能够存储错误消息。 那里提到的最接近Exception的例子是使用子例程的备用返回(但不能用函数):

subroutine do_something(stat, *)
    integer :: stat
    !...

    ! Some error occurred
    if (error) return 1
end subroutine

并在主程序中执行

try: block
    call do_something(stat, *100)
    exit try ! Exit the try block in case of normal execution
100 continue ! Jump here in case of an error
    print *,"This is an error message!"
end block try

请注意,块构造需要符合Fortran 2008的编译器。

我从来没有见过这样的东西,但是:)

答案 1 :(得分:3)

有一些建议(请参阅下面的Steve Lionel评论)将异常处理添加到下一个Fortran标准中。例如:Exception handling - BCS Fortran Specialist Group

这在Fortran中显然有很长的历史(再次见到Steve的第二条评论)

答案 2 :(得分:2)

考虑到大量try-except用例用于I / O处理,您应该知道所有FORTRAN I / O函数都有一个ERR说明符,在该情况下指向行标签发生错误。例如:

C2345678
      READ( UNIT=5, FMT=10, ERR=30, IOSTAT=N ) X
   10 FORMAT(I5) 

      WRITE( UNIT=6, FMT=20, ERR=30, IOSTAT=N ) X
   20 FORMAT(I5)

   30 WRITE( *, * ) 'I/O error # ', N, ', on 1'

当然可以用任何其他表达式替换WRITE可执行文件,以实现某种形式的except功能。

PS 的示例来源herehere

答案 3 :(得分:1)

您可以在Fortran 2003中实现异常处理 而不求助于Arjen Markus解决方案中的备用return语句。 想法是使用存储异常的类型的最终过程。

一个基本的实现是

module exception_mod
   implicit none

   type exception_stack_t
      ! should contain a list of raised exceptions, 
      ! but keep it simple for this example
      logical :: raised = .false.
   contains
      final :: error_if_uncaught
   end type

contains
   subroutine raise(e)
      ! should take an exception as a dummy argument, and add it to the stack.
      type(exception_stack_t), intent(out) :: e

      e%raised = .true.
   end subroutine

   logical function catch(e) result(was_raised)
      ! should catch a particular type of exception that is passed to this procedure
      type(exception_stack_t), intent(inout) :: e

      was_raised = e%raised
      e%raised = .false.
   end function

   subroutine error_if_uncaught(this)
      type(exception_stack_t), intent(in) :: this

      if (this%raised) error stop "EXCEPTION was not caught"
   end subroutine
end module

举一个异常的例子是

   subroutine my_sqrt(x,sqrt_x,e)
      real, intent(in) :: x
      real, intent(out) :: sqrt_x
      type(exception_stack_t), optional, allocatable, intent(inout) :: e

      type(exception_stack_t), allocatable :: my_e

      if (x < 0) then
         allocate(my_e)
         call raise(my_e)
         ! should push exception `my_e` to the stack `e`
         if (present(e)) call move_alloc(my_e, e)
         return
      endif

      sqrt_x = sqrt(x)
   end subroutine

现在您可以通过多种方式调用此命令, 假设

real :: sqrt_x
type(exception_stack_t), allocatable :: e
  1. 常规通话:

    call my_sqrt(-1.0,sqrt_x)
    

    打印“未捕获到EXCEPTION” , 由于my_e中的my_sqrt局部变量。

  2. 在不捕获异常的情况下进行调用:

    call my_sqrt(-1.0,sqrt_x,e)
    

    给出了“未捕获到例外” 一旦e超出范围。

  3. 等效于try-except块:

    call my_sqrt(-1.0,sqrt_x,e)
    if (catch(e)) then
       print "(a)", "ValueError for 'my_sqrt', but I'm continuing."
    else
       print "(a,f0.10)", "sqrt = ", sqrt_x
    endif
    

    给出 “'my_sqrt'的ValueError,但我继续。”

这与使用错误指示整数不同, 因为

  1. 呼叫者不必记住检查错误状态 (final过程将执行此操作);

  2. exception_stack_t可以存储引发的异常列表, 并且可以通过相互调用的过程来传递。 可以在任何级别捕获异常。

您还可以定义异常类型的整个层次结构。

原则上,可以创建与Python中相同的异常体系结构, 尽管它会以非常不同的语法使用。

答案 4 :(得分:0)

尽管这不是适当的“异常处理”,但我发现以下子例程实际上很有用

SUBROUTINE RAISE_EXCEPTION(message)
  INTEGER i
  CHARACTER(LEN=*) message
  PRINT *,message
  i=1
  i=1/(i-i)
ENDSUBROUTINE

发生错误情况时可以调用的

IF (var<0) CALL RAISE_EXCEPTION('Error: var should be positive!')

如果代码是使用gfortran -ffpe-trap=zero-fbacktrace选项(带有ifort的-fpe0)编译的,则代码将停止运行(因为在子例程中有意除以零),并且调用堆栈将被打印。此外,如果您要调试代码,则该进程将不会被杀死(即使执行被暂停),因此您可以从调试器内部探索变量,调用堆栈等。