在 Fortran 中不延迟地扩展对象和覆盖过程

Extending an object and overriding a procedure without being deferred in Fortran

我有一个包含许多不同功能和方法的代码。有些方法都针对同一个特征,即只能选择一个选项。

此外,根据某个功能,我可能需要在其他地方修改子例程。所以在例程 inject 的循环中,我可能有一个小的 if 语句询问我是否使用了功能 A,然后执行一些额外的操作。 这是非常令人沮丧的,因为不同的功能似乎非常随意地与其他例程连接,并且可能难以维护。

我决定采取以下措施来避免这种情况:

我定义了一个对象t_inject,目的是执行例程inject。我重写了例程 inject,使其仅包含所有不同场景通用的代码。

 type t_inject 


 contains 

 procedure,nopass :: inject => inject_default
 end type 

现在我有另一个对象来处理我的特征 A 以防它被选中。

 type,extends(t_inject) :: t_inject_a


 contains 

 procedure, nopass :: inject => inject_a
 end type 

我的子程序 inject_a 和 inject 具有相同的接口。例如

 subroutine inject_a( part ) 
  type(t_part) , intent(inout) :: part % an external data type 
 call inject(part) 
 ! do the extra bit of stuff you need to do

 end subroutine 

 subroutine inject( part) 
   type(t_part) , intent(inout) :: part % an external data type 

  ! carry out the default stuff 
 end subroutine 

现在在我的主程序中

class(t_inject) :: inj 

allocate(inj :: t_inject_a) 

call inj% inject ( part) 

你会这样做吗?它有效吗?
我最初想做一个带有 deferred 注入过程的抽象声明类型,然后我可以扩展。 但是对于一个非常微不足道的问题,我可能不需要那个——我也想知道我的调用 call inj% inject(part) 是否足以让编译器知道去哪里。有时我看到代码在调用之前需要 class is 条件。

我觉得应该修改三点:

  • 类型绑定过程需要引用实际过程名称(通过 =>)。因此,我将模块过程的名称 inject() 更改为 inject_default()。 (但也请参阅 test2.f90)。
  • 我们需要将 allocatable 附加到 class 变量(例如,inj2)以分配具体类型(例如,t_inject_a)。
  • allocate语句中,具体类型的名称应该出现在::之前,这样allocate( t_inject_a :: inj2 ).

修改后的代码可能如下所示:

!! test.f90
module test_mod
    implicit none

    type t_inject 
    contains 
        procedure, nopass :: inject => inject_default
    endtype

    type, extends(t_inject) :: t_inject_a
    contains 
        procedure, nopass :: inject => inject_a
    endtype

    type t_part    !! some other type
        integer :: x = 100, y = 200
    endtype

contains
    subroutine inject_default( part )
        type(t_part), intent(inout) :: part

        print *, "x = ", part % x
    endsubroutine

    subroutine inject_a( part ) 
        type(t_part), intent(inout) :: part

        call inject_default( part )
        print *, "y = ", part % y
    endsubroutine 
end

program main
    use test_mod
    implicit none
    class( t_inject ), allocatable :: inj1, inj2
    type( t_part ) :: part

    !! Polymorphic allocation with concrete types.
    allocate( t_inject   :: inj1 )
    allocate( t_inject_a :: inj2 )

    print *, "inj1:"
    call inj1 % inject( part )

    print *, "inj2:"
    call inj2 % inject( part )
end

"gfortran-8 test.90 && ./a.out" 给出

 inj1:
 x =          100
 inj2:
 x =          100
 y =          200

我们还可以通过使用procedure, nopass :: inject来使用模块过程inject()(而不是inject_default()),例如:

!! test2.f90
module test_mod
    implicit none

    type t_inject 
    contains 
        procedure, nopass :: inject
        ! procedure, nopass :: inject => inject  !! this also works
    endtype

    type, extends(t_inject) :: t_inject_a
    contains 
        procedure, nopass :: inject => inject_a
    endtype

    type t_part    !! some other type
        integer :: x = 100, y = 200
    endtype

contains
    subroutine inject( part )
        type(t_part), intent(inout) :: part

        print *, "x = ", part % x
    endsubroutine

    subroutine inject_a( part ) 
        type(t_part), intent(inout) :: part

        call inject( part )
        print *, "y = ", part % y
    endsubroutine 
end

!! The remaining part (and the result) is the same...

此外,还可以将 inject() 等实际过程分隔在不同的文件中,并 use 它们定义新类型,如 t_inject (参见 mylib.f90test3.f90 下面)。这可能有助于在某些库文件中重用例程。

!! mylib.f90
module mylib
    implicit none

    type t_part    !! some other type
        integer :: x = 100, y = 200
    endtype

contains

    subroutine inject( part )
        type(t_part), intent(inout) :: part

        print *, "x = ", part % x
    end
    subroutine inject_a( part ) 
        type(t_part), intent(inout) :: part

        call inject( part )
        print *, "y = ", part % y
    end
end

!! test3.f90
module test_mod
    use mylib
    implicit none

    type t_inject 
    contains 
        procedure, nopass :: inject
    endtype

    type, extends(t_inject) :: t_inject_a
    contains 
        procedure, nopass :: inject => inject_a
    endtype
end

!! The main program is the same as test.f90.
!! compile: gfortran-8 mylib.f90 test3.f90