我最近试图在我的代码中添加一些新的fortran 2003功能。基本上我想使用数据和过程多态的混合。阅读了有关这些概念的基础教程(Fortran 2003中的面向对象编程) 第1部分:代码可重用性:https://www.pgroup.com/lit/articles/insider/v3n1a3.htm和 Fortran 2003中面向对象的编程 第2部分:数据多态性https://www.pgroup.com/lit/articles/insider/v3n2a2.htm)我试图让我的基本概念起作用。
我的想法是我有一个基本类型interaction
,它有一个名字和一些类型绑定程序。然后我有一些特定的交互,比如smallso2inv
,它们通过一些参数扩展交互类型,并覆盖基本类型的类型绑定过程。现在我想传递基本类型的初始化方法,如
call interaction%initialize("smallso2inv")
然后内部指针specInteraction
应指向类型smallso2inv
。现在使用
call interaction%readInit()
我希望调用smallso2inv类型的readInit例程。这将使用计算所需的参数填充smallso2inv
类型。
这是我的交互模块的一个很小的工作示例,其中包含上述类型和我当前的实现:
module mod_interaction
implicit none
public
type interaction
character*80 :: name
class(interaction), pointer :: specInteraction => null()
procedure, pass(this) :: readInit
procedure, pass(this) :: readUpdate
procedure, pass(this) :: compute
procedure, pass(this) :: addToHamiltonian
procedure, pass(this) :: updateParameter
end type interaction
type, extends(interaction) :: smallso2inv
double precision :: uvalue, j1value, j2value
double precision :: uincr, j1incr, j2incr
contains
procedure, pass(this) :: setup => smallso2inv_initialize
procedure, pass(this) :: readInit => smallso2inv_readInit
procedure, pass(this) :: readUpdate => smallso2inv_readUpdate
procedure, pass(this) :: compute => smallso2inv_compute
procedure, pass(this) :: addToHamiltonian => smallso2inv_addToHamiltonian
procedure, pass(this) :: updateParameter => smallso2inv_updateParameter
end type smallso2inv
contains
subroutine initialize(this,name)
implicit none
type(interaction) :: this
character*80 :: name
class(interaction), allocatable, target :: thisInteraction
!
select case(name)
case('smallSO2inv')
allocate(smallso2inv::thisInteraction)
call thisInteraction%setup()
this%specInteraction => thisInteraction
end select
end subroutine initialize
subroutine smallso2inv_initialize(this)
!
class(smallso2inv) :: this
!
this%uvalue = 0.0d0
this%j1value = 0.0d0
this%j2value = 0.0d0
print*, "hallo smallso2inv init"
end subroutine smallso2inv_initialize
subroutine readInit (this)
class(interaction) :: this
character*80 :: name
call this%specInteraction%readInit()
end subroutine readInit
subroutine smallso2inv_readInit(this)
!
class(smallso2inv) :: this
!
print*, "hello smallso2inv readInit"
!
end subroutine smallso2inv_readInit
end module mod_interaction
我现在因使用ifort 15.0.0而遇到两个错误。
error #6437: A subroutine or function is calling itself recursively. [READINIT]
call this%specInteraction%readInit()
error #6460: This is not a field name that is defined in the encompassing structure. [SETUP]
call thisInteraction%setup()
我的实现似乎尝试从基本interaction
类型调用子例程,而不是使用smallso2inv
类型。有没有办法解决这个问题,而无需转到readInit
子程序中的案例类型开关?我认为这应该是可能的但不知何故我认为我混淆了指针和变量的用法,因为我对多态上下文中的指针的理解仍然有限。
在此先感谢您的帮助。
答案 0 :(得分:1)
可能会有更多错误,但这个错误是报告的错误:
初始化调用自身。它用不同的对象实例调用它并不重要。它必须是recursive
。
recursive subroutine readInit (this)
class(interaction) :: this
call this%specInteraction%readInit()
end subroutine readInit
另一个错误是初始化。您需要使用select type
typeguard。在type is
区域内,您可以将thisInteraction
视为具有新的非多态类型。您也可以使用class is
代替。这是否是一个明智的设计选择,我不确定。我个人会去两个单独的程序,可能是重载类型名称的函数。
subroutine initialize(this,name)
...
select case(name)
case('smallSO2inv')
allocate(smallso2inv::thisInteraction)
select case (thisInteraction)
type is (smallso2inv)
call thisInteraction%setup()
this%specInteraction => thisInteraction
end type
end select
end subroutine initialize
请注意,character*n
表示法已经过时,如果您没有任何特殊的用户定义作业,则没有理由使用= 0.0d0
代替简单= 0
。
答案 1 :(得分:1)
递归投诉(在我看来)是编译器错误[1]。在编译时,编译器在一般情况下不能知道readInit绑定将分派给哪个特定过程,并且围绕递归调用的限制是执行概念(即在运行时)。简单的解决方法是直接输入并添加递归属性。
您只能访问对象的声明类型的绑定(和组件)。一种选择是声明"临时"对象(注意你有一个对象生命周期问题 - 因此我使临时指针在下面)具有所需的类型,或者你可以使用像Vladimir F建议的类似结构构造函数。对于简单的构造,函数方法是典型的但是,如果构造过程需要传回的不仅仅是构造的对象(失败标志或类似),那么使用本地临时对象可能更合适。
subroutine initialize(this,name)
implicit none
type(interaction) :: this
character(80) :: name
!
select case(name)
case('smallSO2inv')
block
! Local pointer of the required type.
type(smallso2inv), pointer :: thisInteraction
! Allocate an object of that type and point the pointer at it.
allocate(thisInteraction)
! Initialize the object.
call thisInteraction%setup()
! Save the pointer.
this%specInteraction => thisInteraction
end block
end select
...
这可能更像是一个样式问题,但在上面没有真正意义通过smallso2inv_initialize
绑定调用setup
过程,因为具有绑定的对象不是多态的(所以它的动态类型和声明的类型是相同的) - 你可以直接调用该过程。
你还应该考虑组件和临时是否需要是一个指针 - 如果组件没有被用作引用,那么使它们都可以分配(使用MOVE_ALLOC从临时组件转移到多态组件)。
答案 2 :(得分:0)
实际上这个稍微改变的设置有效(仅作为浏览此问题的用户的参考):
module mod_interaction
implicit none
public
type interaction
character*80 :: name
class(interaction), pointer :: specInteraction => null()
procedure, pass(this) :: readInit
procedure, pass(this) :: readUpdate
procedure, pass(this) :: compute
procedure, pass(this) :: addToHamiltonian
procedure, pass(this) :: updateParameter
end type interaction
type, extends(interaction) :: smallso2inv
double precision :: uvalue, j1value, j2value
double precision :: uincr, j1incr, j2incr
contains
procedure, pass(this) :: initialize => smallso2inv_initialize
procedure, pass(this) :: readInit => smallso2inv_readInit
procedure, pass(this) :: readUpdate => smallso2inv_readUpdate
procedure, pass(this) :: compute => smallso2inv_compute
procedure, pass(this) :: addToHamiltonian => smallso2inv_addToHamiltonian
procedure, pass(this) :: updateParameter => smallso2inv_updateParameter
end type smallso2inv
contains
subroutine initialize(this,name)
!
implicit none
!
type(interaction) :: this
character*80 :: name
class(interaction),allocatable, target :: thisInteraction
!
this%name = name
select case(name)
case('smallSO2inv')
allocate(smallso2inv::thisInteraction)
select type (thisInteraction)
type is (smallso2inv)
call thisInteraction%initialize()
this%specInteraction => thisInteraction
end select
end select
!
!
end subroutine initialize
subroutine smallso2inv_initialize(this)
!
class(smallso2inv) :: this
!
this%uvalue = 0.0d0
this%j1value = 0.0d0
this%j2value = 0.0d0
print*, "hallo smallso2inv init"
end subroutine smallso2inv_initialize
recursive subroutine readInit (this)
class(interaction) :: this
class(interaction), allocatable :: thisInteraction
character*80 :: name
!
call this%specInteraction%readInit()
print*, "name=",this%name
!
end subroutine readInit
subroutine smallso2inv_readInit(this)
!
class(smallso2inv) :: this
!
print*, "hello smallso2inv readInit"
print*, "uvalue=",this%uvalue
!
end subroutine smallso2inv_readInit
end module mod_interaction
弗拉基米尔F回答证明非常有助于让我的initialize
例程工作,并且IanH回答非常有助于让readInit
例程工作。对此解决方案的一些评论。这与ifort 15.0.0其他编译器到目前为止未经测试完全一致。 specInteraction
必须是指针,如在ifort 15.0.0中type interaction
不能包含相同类型的可分配类(未实现的功能)。像IanH一样,readInit
必须是递归的,尽管在运行时调用类型smallso2inv
的正确对象,但编译器在编译时不知道它的类型。这也是类型绑定过程重载在这里至关重要的原因,因为在编译时也必须在基类interaction
中找到每个被调用的过程。感谢你们两位的帮助。