在 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.f90
和 test3.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
我有一个包含许多不同功能和方法的代码。有些方法都针对同一个特征,即只能选择一个选项。
此外,根据某个功能,我可能需要在其他地方修改子例程。所以在例程 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.f90
和 test3.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