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

问题描述 投票:3回答:1

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

MODULE mobject
  TYPE :: tobject
    ! Data declarations
  CONTAINS
    FINAL :: finalize
  END TYPE
CONTAINS
  SUBROUTINE finalize(object)
    TYPE(tobject) object
    ...
  END SUBROUTINE
END MODULE

但是,此功能可靠吗?值得注意的是,我注意到有关何时以及是否调用它的不一致之处,并且在Intel Fortran 19和GFortan 7、8之间存在重大差异:

  • GFortran无法销毁存储在数组中的对象。
  • Intel Fortran:
    • 在分配时执行伪造的和潜在的超级混乱破坏,甚至可能在包含垃圾数据的内存中执行,并且
    • 从函数返回后对析构函数执行虚假调用。

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

这些不一致之处引发了一些关于析构函数对我的实际可用性的问题。两种行为是否完全符合标准?标准不清楚吗?该标准是否可能包含导致不直观行为的条款?

详细分析(代码见下文)

  • PROGRAM Block。对于IPRO将在PROGRAM块中声明的实例,Gfortran将不会调用析构函数(请参见示例中的run1

  • Scalar对象。对于声明为标量的实例,如果变量已进行任何形式的初始化,则Gfortran和IFort都将调用析构函数。但是,Intel Fortran在分配函数返回值时也将调用它

    • 在堆栈上未初始化的对象上,然后用函数中的数据覆盖它,然后
    • 似乎在newObject函数的末尾。

    但是,可以通过明确检查是否对象进行初始化,然后执行任何清理。

这意味着,程序员必须明确检查实例是否已初始化。

  • 数组中的对象。如果对象包含在数组中,并且数组超出范围,则>

    • Gfortran将不会调用析构函数。
    • Intel Fortran可以调用析构函数,具体取决于初始化给定数组成员的方式。
    • 无论是否将数组声明为allocatable,都没有区别。
  • 通过分配初始化的可分配数组。

  • 使用现代功能时,对可分配数组的分配意味着分配,除未有IntelFortran可以在其上调用析构函数的未初始化实例外,其他情况也一样。
  • 来自函数的可分配/指针。

    • GFortran不会在函数的末尾调用析构函数,将allocatable对象或pointer返回给对象,而是在将值显式地或通过释放在客户端代码中释放时调用它allocatable s的范围。这就是我所期望的。
    • [Intel Fortran在其他一些情况下会致电:
      • [当对象被声明为allocatable时,而不是声明为pointer时,则不是,Intel Fortran在退出函数时,将对函数的本地值调用析构函数。
      • [在使用隐式分配(var = newObject(...))初始化函数内部的对象时,或者在pointer变体的情况下使用显式分配(allocate(var); var = newObject(...))初始化对象时,在未初始化的内存上调用析构函数,该内存在run5MoveAlloc中可见]和run6MovePtr中的%name包含垃圾数据。可以通过使用allocate(var); call var%init(...)模式来解决。

    测试代码
    !! -- 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
    

    测试代码的输出

    >> 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
    

    现代Fortran包含各种面向对象的思想,包括通过FINAL关键字的“析构函数”概念。模块mobject TYPE :: tobject!数据声明包含最终:::

    fortran gfortran intel-fortran fortran2003
    1个回答
    0
    投票

    我担心这个问题是非常主观的,并且将取决于太多因素(您需要支持多个编译器吗?您需要支持其较旧版本吗?到底是哪个版本?如果某些实体未最终确定,这有多重要? )。

    您提出了一些主张,如果没有实际的代码示例就很难回答,并且可能是单独的完整问题和解答的主题。 Gfortran在此bug报告https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37336中发布了Fortran 2003和2008功能实现的当前状态(该链接指向一个meta bug,该bug指向在bugzilla中跟踪的几个独立问题)。众所周知,该功能尚未完成,并且存在未解决的问题。最值得注意的是(至少对我而言),函数结果尚未最终确定。

    © www.soinside.com 2019 - 2024. All rights reserved.