首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Fortran的“最终”子程序是否足够可靠以供实际使用?

Fortran的“最终”子程序是否足够可靠以供实际使用?
EN

Stack Overflow用户
提问于 2020-01-30 11:54:17
回答 1查看 688关注 0票数 8

现代Fortran包含各种面向对象的思想,包括通过FINAL关键字的“析构函数”的概念。

代码语言:javascript
复制
MODULE mobject
  TYPE :: tobject
    ! Data declarations
  CONTAINS
    FINAL :: finalize
  END TYPE
CONTAINS
  SUBROUTINE finalize(object)
    TYPE(tobject) object
    ...
  END SUBROUTINE
END MODULE

但是,这个特性可靠吗?值得注意的是,我注意到了关于何时以及是否会调用它的不一致之处,英特尔Fortran 19和GFortan 7、8之间的主要区别是:

function.

  • GFortran无法销毁存储在数组中的对象。
  • Intel Fortran:
    • 在赋值时执行虚假和潜在的超流破坏,甚至在包含垃圾数据的内存上执行虚假的超级销毁操作,而
    • 在从GFortran返回时执行对析构函数的虚假调用。

我注意到gfortran-7.4.0和gfortran-8.2.1.2之间没有差别。

这些不一致让我对析构函数的实际可用性产生了一些疑问。任何一种行为都完全符合标准吗?这方面的标准不清楚吗?该标准是否可能包含条款,从而导致违反直觉的行为?

详细分析(代码见下文)

  • PROGRAM块。对于在程序块中声明的实例, Gfortran不会调用析构函数,而Ifort将调用析构函数(参见example).
  • Scalar对象中的run1。对于声明为标量的实例,如果变量已经看到任何形式的初始化,则和IFort都将调用析构函数。然而,在分配函数返回值时,Intel Fortran也将调用它

代码语言:javascript
复制
- on the uninitialzied object on the stack before overwriting it with the data from the function, and
- seemingly at the end of the `newObject` function.

但是,在执行任何清理之前,可以通过显式检查对象是否已初始化来防止这一点。

这意味着程序员必须显式检查实例是否已经初始化。

数组中的

  • Objects。如果对象包含在数组中,并且数组超出作用域,则为

代码语言:javascript
复制
- Gfortran will not invoke the destructor.
- Intel Fortran may invoke the destructor, depending on how a given array member was initialized.
- It makes no difference, whether the array is declared `allocatable`.

通过赋值初始化的

  • Allocatable数组。当使用现代特性时,分配给可分配数组的赋值意味着分配,相同的情况是,除了没有未初始化的实例,IntelFortran可以从functions.

调用destructor.

  • Allocatable/Pointers。

代码语言:javascript
复制
- GFortran doesn't call the destructor at the end of the function returning the an `allocatable` object or a `pointer` to an object, and instead calls it when the value is deallocated in the client code, explicitly or by going out of scope for `allocatable`s. That's what I expected.
- Intel Fortran calls in some additional cases:  
    - When the object is declared `allocatable`, but not when it is a `pointer`, Intel Fortran invokes the destructor on the local value of the function upon exiting the function.
    - When initializing the object inside the function with implied allocation (`var = newObject(...)`), or in the case of the `pointer` variant, with explicit allocation (`allocate(var); var = newObject(...)`), the destructor is invoked on uninitialized memory, visible in `run5MoveAlloc` and `run6MovePtr` from `%name` containing junk data. This can be resolved by using the `allocate(var); call var%init(...)` pattern instead.

测试代码

代码语言:javascript
复制
!! -- Makefile ---------------------------------------------------
!! Runs the code with various compilers.

SHELL = bash
FC = NO_COMPILER_SPECIFIED
COMPILERS = gfortran-7 gfortran-8 ifort
PR = @echo$(n)pr -m -t -w 100

define n


endef

all: 
    rm -rf *.mod *.bin
    $(foreach FC, $(COMPILERS), $(n)\
      rm -rf *.mod && \
      $(FC) destructor.f90 -o $(FC).bin && \
      chmod +x $(FC).bin)
    $(PR) $(foreach FC, $(COMPILERS), <(head -1 <($(FC) --version)))
    $(info)
    $(foreach N,0 1 2 3 4 5 6,$(n) \
      $(PR) $(foreach FC, $(COMPILERS), <(./$(FC).bin $(N))))



!! -- destructor.f90 ---------------------------------------------

module mobject
  implicit none
  private
  public tobject, newObject

  type :: tobject
     character(32) :: name = "<undef>"
   contains
     procedure :: init
     final :: finalize
  end type tobject

contains

  subroutine init(object, name)
    class(tobject), intent(inout) :: object
    character(*), intent(in) :: name
    print *, "+ ", name
    object%name = name
  end subroutine init

  function newObject(name)
    type(tobject) :: newObject
    character(*), intent(in) :: name
    call new%init(name)
  end function newObject

  subroutine finalize(object)
    type(tobject) :: object
    print *, "- ", object%name
  end subroutine finalize

end module mobject



module mrun
  use mobject
  implicit none
contains

  subroutine run1()
    type(tobject) :: o1_uninit, o2_field_assigned, o3_tobject, o4_new, o6_init
    type(tobject), allocatable :: o5_new_alloc, o7_init_alloc
    print *, ">>>>> run1"
    o2_field_assigned%name = "o2_field_assigned"
    o3_tobject = tobject("o3_tobject")
    o4_new = newObject("o4_new")
    o5_new_alloc = newObject("o5_new_alloc")
    call o6_init%init("o6_init")
    allocate(o7_init_alloc)
    call o7_init_alloc%init("o7_init_alloc")
    print *, "<<<<< run1"
  end subroutine run1

  subroutine run2Array()
    type(tobject) :: objects(4)
    print *, ">>>>> run2Array"
    objects(1)%name = "objects(1)_uninit"
    objects(2) = tobject("objects(2)_tobject")
    objects(3) = newObject("objects(3)_new")
    call objects(4)%init("objects(4)_init")
    print *, "<<<<< run2Array"
  end subroutine run2Array

  subroutine run3AllocArr()
    type(tobject), allocatable :: objects(:)
    print *, ">>>>> run3AllocArr"
    allocate(objects(4))
    objects(1)%name = "objects(1)_uninit"
    objects(2) = tobject("objects(2)_tobject")
    objects(3) = newObject("objects(3)_new")
    call objects(4)%init("objects(4)_init")
    print *, "<<<<< run3AllocArr"
  end subroutine run3AllocArr

  subroutine run4AllocArrAssgn()
    type(tobject), allocatable :: objects(:)
    print *, ">>>>> run4AllocArrAssgn"
    objects = [ &
         tobject("objects(1)_tobject"), &
         newObject("objects(2)_new") ]
    print *, "<<<<< run4AllocArrAssgn"
  end subroutine run4AllocArrAssgn

  subroutine run5MoveAlloc()
    type(tobject), allocatable :: o_alloc
    print *, ">>>>> run5MoveAlloc"
    o_alloc = getAlloc()
    print *, "<<<<< run5MoveAlloc"
  end subroutine run5MoveAlloc

  function getAlloc() result(object)
    type(tobject), allocatable :: object
    print *, ">>>>> getAlloc"
    allocate(object)
    object = newObject("o_alloc")
    print *, "<<<<< getAlloc"
  end function getAlloc

  subroutine run6MovePtr()
    type(tobject), pointer :: o_pointer
    print *, ">>>>> run6MovePtr"
    o_pointer => getPtr()
    deallocate(o_pointer)
    print *, "<<<<< run6MovePtr"
  end subroutine run6MovePtr

  function getPtr() result(object)
    type(tobject), pointer :: object
    print *, ">>>>> getPtr"
    allocate(object)
    object = newObject("o_pointer")
    print *, "<<<<< getPtr"
  end function getPtr

end module mrun



program main
  use mobject
  use mrun
  implicit none
  type(tobject) :: object
  character(1) :: argument

  print *, ">>>>> main"
  call get_command_argument(1, argument)
  select case (argument)
  case("1")
     call run1()
  case("2")
     call run2Array()
  case("3")
     call run3AllocArr()
  case("4")
     call run4AllocArrAssgn()
  case("5")
     call run5MoveAlloc()
  case("6")
     call run6MovePtr()
  case("0")
     print *, "####################";
     print *, ">>>>> runDirectlyInMain"
     object = newObject("object_in_main")
     print *, "<<<<< runDirectlyInMain"
  case default
     print *, "Incorrect commandline argument"
  end select
  print *, "<<<<< main"
end program main

测试代码的输出

代码语言:javascript
复制
>> make
rm -rf *.mod *.bin
rm -rf *.mod && gfortran-7 destructor.f90 -o gfortran-7.bin && chmod +x gfortran-7.bin  
rm -rf *.mod && gfortran-8 destructor.f90 -o gfortran-8.bin && chmod +x gfortran-8.bin  
rm -rf *.mod && ifort destructor.f90 -o ifort.bin && chmod +x ifort.bin

pr -m -t -w 100  <(head -1 <(gfortran-7 --version))  <(head -1 <(gfortran-8 --version))  <(head -1 <(ifort --version))
GNU Fortran (SUSE Linux) 7.4.0   GNU Fortran (SUSE Linux) 8.2.1 2 ifort (IFORT) 19.0.4.243 2019041

pr -m -t -w 100  <(./gfortran-7.bin 0)  <(./gfortran-8.bin 0)  <(./ifort.bin 0) 
 >>>>> main                       >>>>> main                       >>>>> main
 ####################             ####################             ####################
 >>>>> runDirectlyInMain          >>>>> runDirectlyInMain          >>>>> runDirectlyInMain
 + object_in_main                 + object_in_main                 + object_in_main
 <<<<< runDirectlyInMain          <<<<< runDirectlyInMain          - <undef>
 <<<<< main                       <<<<< main                       - object_in_main
                                                                   <<<<< runDirectlyInMain
                                                                   <<<<< main

pr -m -t -w 100  <(./gfortran-7.bin 1)  <(./gfortran-8.bin 1)  <(./ifort.bin 1) 
 >>>>> main                       >>>>> main                       >>>>> main
 >>>>> run1                       >>>>> run1                       >>>>> run1
 + o4_new                         + o4_new                         - <undef>
 + o5_new_alloc                   + o5_new_alloc                   + o4_new
 + o6_init                        + o6_init                        - <undef>
 + o7_init_alloc                  + o7_init_alloc                  - o4_new
 <<<<< run1                       <<<<< run1                       + o5_new_alloc
 - o7_init_alloc                  - o7_init_alloc                  - o5_new_alloc
 - o6_init                        - o6_init                        + o6_init
 - o5_new_alloc                   - o5_new_alloc                   + o7_init_alloc
 - o4_new                         - o4_new                         <<<<< run1
 - o3_tobject                     - o3_tobject                     - <undef>
 - o2_field_assigned              - o2_field_assigned              - o2_field_assigned
 <<<<< main                       <<<<< main                       - o3_tobject
                                                                   - o4_new
                                                                   - o6_init
                                                                   - o5_new_alloc
                                                                   - o7_init_alloc
                                                                   <<<<< main

pr -m -t -w 100  <(./gfortran-7.bin 2)  <(./gfortran-8.bin 2)  <(./ifort.bin 2) 
 >>>>> main                       >>>>> main                       >>>>> main
 >>>>> run2Array                  >>>>> run2Array                  >>>>> run2Array
 + objects(3)_new                 + objects(3)_new                 - <undef>
 + objects(4)_init                + objects(4)_init                + objects(3)_new
 <<<<< run2Array                  <<<<< run2Array                  - <undef>
 <<<<< main                       <<<<< main                       - objects(3)_new
                                                                   + objects(4)_init
                                                                   <<<<< run2Array
                                                                   <<<<< main

pr -m -t -w 100  <(./gfortran-7.bin 3)  <(./gfortran-8.bin 3)  <(./ifort.bin 3) 
 >>>>> main                       >>>>> main                       >>>>> main
 >>>>> run3AllocArr               >>>>> run3AllocArr               >>>>> run3AllocArr
 + objects(3)_new                 + objects(3)_new                 - <undef>
 + objects(4)_init                + objects(4)_init                + objects(3)_new
 <<<<< run3AllocArr               <<<<< run3AllocArr               - <undef>
 <<<<< main                       <<<<< main                       - objects(3)_new
                                                                   + objects(4)_init
                                                                   <<<<< run3AllocArr
                                                                   <<<<< main

pr -m -t -w 100  <(./gfortran-7.bin 4)  <(./gfortran-8.bin 4)  <(./ifort.bin 4) 
 >>>>> main                       >>>>> main                       >>>>> main
 >>>>> run4AllocArrAssgn          >>>>> run4AllocArrAssgn          >>>>> run4AllocArrAssgn
 + objects(2)_new                 + objects(2)_new                 + objects(2)_new
 <<<<< run4AllocArrAssgn          <<<<< run4AllocArrAssgn          - objects(2)_new
 <<<<< main                       <<<<< main                       <<<<< run4AllocArrAssgn
                                                                   <<<<< main

pr -m -t -w 100  <(./gfortran-7.bin 5)  <(./gfortran-8.bin 5)  <(./ifort.bin 5) 
 >>>>> main                       >>>>> main                       >>>>> main
 >>>>> run5MoveAlloc              >>>>> run5MoveAlloc              >>>>> run5MoveAlloc
 >>>>> getAlloc                   >>>>> getAlloc                   >>>>> getAlloc
 + o_alloc                        + o_alloc                        + o_alloc
 <<<<< getAlloc                   <<<<< getAlloc                   - `4�\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
 <<<<< run5MoveAlloc              <<<<< run5MoveAlloc              - o_alloc
 - o_alloc                        - o_alloc                        <<<<< getAlloc
 <<<<< main                       <<<<< main                       - o_alloc
                                                                   <<<<< run5MoveAlloc
                                                                   - o_alloc
                                                                   <<<<< main

pr -m -t -w 100  <(./gfortran-7.bin 6)  <(./gfortran-8.bin 6)  <(./ifort.bin 6)
 >>>>> main                       >>>>> main                       >>>>> main
 >>>>> run6MovePtr                >>>>> run6MovePtr                >>>>> run6MovePtr
 >>>>> getPtr                     >>>>> getPtr                     >>>>> getPtr
 + o_pointer                      + o_pointer                      + o_pointer
 <<<<< getPtr                     <<<<< getPtr                     - `��\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
 - o_pointer                      - o_pointer                      - o_pointer
 <<<<< run6MovePtr                <<<<< run6MovePtr                <<<<< getPtr
 <<<<< main                       <<<<< main                       - o_pointer
                                                                   <<<<< run6MovePtr
                                                                   <<<<< main
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-01-30 12:26:59

TLDR: Gfortran有一些悬而未决的问题。英特尔声称全力支持。一些编译器声称不支持。

关于可靠性和可用性的问题在一般情况下是相当主观的,因为我们必须考虑许多对您来说是独一无二的要点(您需要支持多个编译器吗?)你需要支持他们的旧版本吗?确切地说是哪一个?如果某些实体没有最后确定,它有多重要?)。

如果没有实际的代码示例,您会提出一些很难回答的断言,并且可能是一个单独的完整问题和答案的主题。Gfortran在这个bug报告https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37336中发布了Fortran 2003和2008年特性的当前实现状态(链接指向指向bugzilla中跟踪的几个单独问题的元bug)。大家都知道,这个特性还没有完成,还有一些悬而未决的问题。最值得注意的是(至少对我来说),功能结果还没有最后确定。关于其他编译器(简化为Y/N/paritally)状态的概述在http://fortranwiki.org/fortran/show/Fortran+2003+status上进行,并在Fortran的文章中定期更新。

我不能谈论那些所谓的伪造的英特尔Fortran的终结。如果您在编译器中发现了一个bug,您应该向您的供应商提交一个bug报告。英特尔的反应一般都很好。

不过,有些个别问题是可以解决的。你可能会找到关于它们的不同的Q/A。但是:

  • Gfortran将不对程序块中声明的实例调用析构函数,而Ifort将调用(参见示例中的run1 ).

代码语言:javascript
复制
- Variables declared in the main program implicitly acquire the `save` attribute according to the standard. The compiler is not supposed to generate any automatic finalization.

然而,

  • Intel Fortran在分配函数返回值时,也将调用它

代码语言:javascript
复制
- As pointed out in the Gfortran bugzilla, gfortran does not finalize function result variables yet.

initialized. --这意味着程序员必须显式检查实例是否为。

代码语言:javascript
复制
- I am afraid there is no such concept in the Fortran standard. I have no idea what " if the variable has seen any form of initialization" could mean. Note that an initializer function **is a function like any other**.

  • 在使用现代特性时,对可分配数组的赋值意味着分配,但不存在IntelFortran可以调用析构函数.

的未初始化实例。

代码语言:javascript
复制
- Not sure what that actually means. There isno such "initialization" in Fortran. Perhaps function results again?

函数

  • Allocatable/Pointers .

代码语言:javascript
复制
- As pointed out several times, function results are not properly finalized in the current versions of Gfortran.

如果你想详细回答其中的任何一点,你真的需要问一个特定的问题,。这张太宽了。该网站的帮助/说明包含“请编辑该问题,将其限制在特定问题上,并提供足够的详细信息以确定适当的答案。避免同时问多个不同的问题。请参阅”请求帮助澄清此问题“。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59985499

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档