【问题标题】:I/O in pure Fortran procedures纯 Fortran 过程中的 I/O
【发布时间】:2012-01-07 03:50:33
【问题描述】:

我正在尝试将错误检查合并到我正在编写的纯程序中。我想要类似的东西:

pure real function func1(output_unit,a)
    implicit none
    integer :: a, output_unit

    if (a < 0) then
        write(output_unit,*) 'Error in function func1: argument must be a nonnegative integer. It is ', a
    else
    func1 = a/3

    endif
    return
end function func1

但是,纯函数不允许对外部文件有 IO 语句,所以我尝试将单元号传递给函数,例如output_unit = 6,这是默认输出。 gfortran 仍然认为这是非法的。有没有解决的办法?是否可以将函数设为派生类型(而不是此处的固有类型 real),在出现错误时输出字符串?

【问题讨论】:

  • 从技术上讲,“纯”过程不会以任何方式修改其环境或任何操作数。
  • 所以它不应该打印到屏幕上?它可以打印到主程序可以访问并打印到屏幕的内部字符串吗?不过,那是相当迂回的。
  • @SamuelTan - 不 - PURE 中没有任何类型的 I/O。简而言之,它不会改变任何东西(变量、本地或全局),它不会保存,也不会 I/O 任何东西(列出它所做的事情会更容易:\ ... ;)
  • 绕过这些限制的常用方法是将函数转换为子例程并添加一个额外的参数来返回状态(您可以为此参数使用派生类型,以返回错误代码和错误文本)。
  • @HotLicks。我在 do concurrent 块中使用这个函数,所以它必须是纯的(我必须明确地将 pure 放在前面,否则编译器会抱怨)。

标签: error-handling io fortran gfortran fortran95


【解决方案1】:

您不是第一个遇到这个问题的人,我很高兴地说,标准中的这个缺陷将在 Fortran 2015 中得到补救。如this document 中所述(第 6 页,标题“已批准对标准"),“在pure 过程中对error stop 语句出现的限制应该被删除”

Fortran 2008 标准在一些新的并行计算功能的上下文中包含了error stop 语句。它会发出错误信号并尽快停止所有进程。目前,stoperror stop 语句都不允许在 pure 过程中使用,因为它们显然不是线程安全的。实际上,在发生内部错误的情况下,这是不必要的限制。

根据您的编译器,您可能需要耐心等待实现。我知道英特尔has implemented 在他们的 ifort 编译器中。 ("F2015: 在 PURE/ELEMENTAL 程序中解除对 STOP 和 ERROR STOP 的限制")

另类

对于另一种方法,您可以查看 at this question,但在您的情况下,这可能有点棘手,因为您必须更改 do concurrent 关键字,而不仅仅是 pure

(正确答案结束)

如果弄脏手是一种选择......

与此同时,您可以做一些残酷的事情,例如

pure subroutine internal_error(error_msg)
    ! Try hard to produce a runtime error, regardless of compiler flags.
    ! This is useful in pure subprograms where you want to produce an error, 
    ! preferably with a traceback.
    ! 
    ! Though far from pretty, this solution contains all the ugliness in this 
    ! single subprogram.
    ! 
    ! TODO: replace with ERROR STOP when supported by compiler
    implicit none

    character(*), intent(in) :: error_msg

    integer, dimension(:), allocatable :: molested

    allocate(molested(2))
    allocate(molested(2))
    molested(3) = molested(4)
    molested(1) = -10
    molested(2) = sqrt(real(molested(1)))
    deallocate(molested)
    deallocate(molested)
    molested(3) = molested(-10)
end subroutine internal_error

如果有人问,你不是从我这里得到的。

【讨论】:

  • 残酷的子程序不必要地复杂。您也可以只编写一个外部子程序并位于一个纯粹的接口中。非常简单。
  • 这符合标准吗?
  • 当然不是,但考虑到它的作用,我认为它没有害处。当它中止程序时,它不会以任何方式破坏其余的运行。并且标准(尤其是 95 标准)也没有指定当您对负数进行平方根时会发生什么。在符合 IEEE 的 Fortran 2003 CPU 中,只有当您确实设置了正确的标志以启用对 IEEE 异常的暂停时,您才会有一些保证。但通常人们并不希望到处都是这样。我当然不会在浮点异常上停止运行我的主要代码。
【解决方案2】:

我自己找到了答案,详细here。它使用了被认为“过时”的东西,但仍然有效;它被称为交替返回。将过程写成子程序,因为它不适用于函数。

pure real subroutine procA(arg1)
    implicit none
    integer :: arg1

    if (arg < 0) then
        return 1 ! exit the function and go to the first label supplied
                 ! when function was called. Also return 2, 3 etc.
    else
        procA = ... ! whatever it should do under normal circumstances
    endif
endsubroutine procA

.... 

! later on, procedure is called
num = procA(a, *220)

220 write(6,*) 'Error with func1: you've probably supplied a negative argument'

eriktous 建议的可能会更好——让过程返回一个状态,可能是一个逻辑值或整数,并让程序在每次调用过程后检查这个值。如果一切顺利,继续。否则,打印相关的错误消息。

欢迎评论。

【讨论】:

  • 啊!请不要使用这个。它被贴上过时的标签是有原因的。当前标准建议以下作为替代方案(与我的建议基本相同):The same effect can be achieved with a return code that is used in a SELECT CASE construct on return.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-14
  • 2015-05-24
相关资源
最近更新 更多