简体   繁体   English

扩展对象并覆盖过程而不在Fortran中延迟

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

I have a code with lots of different features and methods. 我有一个具有许多不同功能和方法的代码。 Some methods are all for the same feature, ie only one among a selection can be selected. 有些方法都是针对同一功能的,即只能选择一种方法。

Furthermore, depending on a feature I might need to do modify subroutines elsewhere. 此外,根据功能的不同,我可能需要在其他地方修改子例程。 So in a loop in routine inject I might have a small if statement asking if I have used feature A , then do a few extra operations. 因此,在例行inject的循环中,我可能会有一条小的if语句,询问是否使用了功能A ,然后再做一些额外的操作。 This is very frustating since different features seems to be connected with others routines very arbirarirly, and can be difficult to maintain. 这很令人沮丧,因为不同的功能似乎非常任意地与其他例程联系在一起,并且可能难以维护。

I have decided to do following to avoid this: 为了避免这种情况,我决定采取以下措施:

I define an object t_inject with the purpose to execture routine inject . 我定义了一个对象t_inject ,目的是实现例程inject I rewrite my routine inject such that it contains only the code that is common for all different scenarios. 我重写了例程inject ,使其仅包含所有不同情况下通用的代码。

 type t_inject 


 contains 

 procedure,nopass :: inject => inject_default
 end type 

Now I have another object to handle my feature A in case it is selected. 现在,如果选择了特征A ,我还有另一个对象来处理它。

 type,extends(t_inject) :: t_inject_a


 contains 

 procedure, nopass :: inject => inject_a
 end type 

My subroutines inject_a and inject have same interface. 我的子例程inject_a和inject具有相同的接口。 Eg 例如

 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 

Now in my main program 现在在我的主程序中

class(t_inject) :: inj 

allocate(inj :: t_inject_a) 

call inj% inject ( part) 

Is that the way you would do it and is it valid? 这就是您要这样做的方式,并且有效吗?
I initially thought of doing an abstract declared type with a deferred inject procedure where I then could extent. 最初,我想到了使用deferred注入过程来进行抽象声明的类型,然后我可以扩展该过程。 But for a very trivial problem I might not need that - I am also wondering whether my call call inj% inject(part) is sufficient for the compiler to know to where to go. 但是对于一个非常琐碎的问题,我可能不需要—我也想知道我的call inj% inject(part)是否足以让编译器知道去哪里。 Sometimes I see codes which need the class is condition before making the call. 有时我在调用之前会看到需要class is条件的代码。

I think three points should be modified: 我认为应该修改三点:

  • The type-bound procedures need to refer to actual procedure names (via => ). 类型绑定过程需要引用实际过程名称(通过=> )。 So, I have changed the name of a module procedure inject() to inject_default() . 因此,我将模块过程inject()的名称更改为inject_default() (But please see test2.f90 also). (但也请参见test2.f90)。
  • We need to attach allocatable to a class variable (eg, inj2 ) to allocate it with a concrete type (eg, t_inject_a ). 我们需要将allocatable附加到类变量(例如inj2 )上,以将其分配给具体类型(例如t_inject_a )。
  • In the allocate statement, the name of a concrete type should appear before :: , such that allocate( t_inject_a :: inj2 ) . allocate语句中,具体类型的名称应出现在::之前,例如allocate( t_inject_a :: inj2 )

The modified code may look like this: 修改后的代码可能如下所示:

!! 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" gives “ gfortran-8 test.90 && ./a.out”给出

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

We can also use a module procedure inject() (rather than inject_default() ) by using procedure, nopass :: inject , for example: 我们还可以通过使用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...

In addition, one can also separate actual procedures like inject() in a different file and use them to define new types like t_inject (see mylib.f90 and test3.f90 below). 此外,还可以将实际过程(例如inject()分离到另一个文件中,并use它们来定义新类型,例如t_inject (请参见下面的mylib.f90test3.f90 )。 This might be useful to reuse routines in some library file. 这对于重用某些库文件中的例程可能很有用。

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

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

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