[英]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: 我认为应该修改三点:
=>
). =>
)。 So, I have changed the name of a module procedure inject()
to inject_default()
. inject()
的名称更改为inject_default()
。 (But please see test2.f90 also). allocatable
to a class variable (eg, inj2
) to allocate it with a concrete type (eg, t_inject_a
). allocatable
附加到类变量(例如inj2
)上,以将其分配给具体类型(例如t_inject_a
)。 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.f90
和test3.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.