简体   繁体   English

Fortran 中是否存在异常处理?

[英]Does exception handling exist in Fortran?

Is there any exception handling structure in Fortran, just like in Python? Fortran 中是否有任何异常处理结构,就像在 Python 中一样?

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

If it does not exist, what would be the easiest way to handle exceptions?如果它不存在,处理异常的最简单方法是什么?

Exceptions as such do not exist in Fortran, so no, there is no Exception handling. Fortran 中不存在这样的异常,所以不,没有异常处理。

But you can do something similar to Exception handling using Standard Fortran - there's even a paper on it Arjen Markus, "Exception handling in Fortran" .但是您可以使用标准 Fortran 执行类似于异常处理的操作 - 甚至有一篇关于它的论文Arjen Markus,“Fortran 中的异常处理”

The most common notation is to use an (integer) return variable indicating the error code:最常见的表示法是使用(整数)返回变量指示错误代码:

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

and in the main program do并在主程序中做

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

There are other ways described in the paper such as defining a dedicated derived type for exceptions that is capable of also storing an error message.论文中还描述了其他方法,例如为异常定义专用的派生类型,该类型也能够存储错误消息。 The example mentioned there that gets closest to an Exception is using alternate returns for subroutines (not possible with functions, though):那里提到的最接近 Exception 的示例是使用子例程的备用返回(尽管函数不可能):

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

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

and in the main program do并在主程序中做

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

Please note that the block construct requires a compiler compliant with Fortran 2008.请注意,块构造需要符合 Fortran 2008 的编译器。

I've never seen something like this out there, though :)我从来没有见过这样的东西,虽然:)

There are proposals (see Steve Lionel's comment below) to add exception handling to the next Fortran standard.有一些建议(请参阅下面的 Steve Lionel 的评论)将异常处理添加到下一个 Fortran 标准中。 See here for example: Exception handling - BCS Fortran Specialist Group例如,请参见此处: 异常处理 - BCS Fortran 专家组

This has apparently a long history in Fortran (again see Steve's second comment below)这显然在 Fortran 中有着悠久的历史(再次参见下面史蒂夫的第二条评论)

Considering that a great deal of try-except use cases are for I/O handling, you should know that all FORTRAN I/O functions have an ERR specifier which points to a line label in the case an error happens.考虑到大量的try-except用例用于 I/O 处理,您应该知道所有 FORTRAN I/O 函数都有一个ERR说明符,它在发生错误的情况下指向行标签。 For example:例如:

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'

of course one could replace the WRITE executable with any other expression to achieve some form of except functionality.当然,可以用任何其他表达式替换WRITE可执行文件以实现某种形式的except功能。

PS source of examples here and here . PS示例源herehere

Although this is not a proper "exception handling" I found practically useful the following subroutine虽然这不是一个正确的“异常处理”,但我发现以下子程序非常有用

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

that can be called when an error condition occurs, eg可以在发生错误情况时调用,例如

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

If the code is compiled with gfortran -ffpe-trap=zero and -fbacktrace options ( -fpe0 with ifort), the code will be stopped (because of the intentional division by zero in the subroutine) and a call stack will be printed.如果代码是用 gfortran -ffpe-trap=zero-fbacktrace选项( -fpe0和 ifort)编译的,代码将被停止(因为在子例程中有意除以零)并打印调用堆栈。 Moreover, if you are debugging the code, the process won't be killed (even if the execution is halted) so you can explore variables, call stack etc. from inside the debugger.此外,如果您正在调试代码,则该进程不会被终止(即使执行已停止),因此您可以从调试器内部探索变量、调用堆栈等。

You could implement exception handling in Fortran 2003 without resorting to the alternate return statements in Arjen Markus's solution.您可以在 Fortran 2003 中实现异常处理,而无需求助于 Arjen Markus 解决方案中的备用 return 语句。 The idea is to use the final procedure of a type that stores the exceptions.这个想法是使用存储异常的类型的最终过程。

A basic implementation is一个基本的实现是

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

and an example of raising an exception is引发异常的一个例子是

   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

Now you can call this in several ways, assuming现在您可以通过多种方式调用它,假设

real :: sqrt_x
type(exception_stack_t), allocatable :: e
  1. Regular call:定期调用:

     call my_sqrt(-1.0,sqrt_x)

    prints "EXCEPTION was not caught" , due to the my_e local variable in my_sqrt .版画“的异常没有被抓”,my_e在局部变量my_sqrt

  2. Calling without catching the exception:调用而不捕获异常:

     call my_sqrt(-1.0,sqrt_x,e)

    gives "EXCEPTION was not caught" once e goes out of scope.一旦e超出范围,就会给出“未捕获异常”

  3. Equivalent of a try-except block:相当于 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

    which gives "ValueError for 'my_sqrt', but I'm continuing."这给出了“'my_sqrt'的ValueError,但我正在继续。”

This is different from using an error-indicating integer, because这与使用错误指示整数不同,因为

  1. the caller does not have to remember to check the error status (the final procedure will do that);调用者不必记住检查错误状态( final过程会这样做);

  2. exception_stack_t could store a list of raised exceptions, and it could be passed along procedures that call each other. exception_stack_t可以存储引发异常的列表,并且可以沿着相互调用的过程传递。 Exceptions can be caught at any level.可以在任何级别捕获异常。

You could also define a whole hierarchy of exception types.您还可以定义异常类型的整个层次结构。

In principle it would be possible to create the same exception architecture as in Python, though it would be used with a very different syntax.原则上,可以创建与 Python 中相同的异常架构,尽管它会以非常不同的语法使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM