坚持使用继承没有工作混合的数据和过程多态

时间:2014-12-02 14:03:07

标签: inheritance fortran polymorphism

我最近试图在我的代码中添加一些新的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子程序中的案例类型开关?我认为这应该是可能的但不知何故我认为我混淆了指针和变量的用法,因为我对多态上下文中的指针的理解仍然有限。 在此先感谢您的帮助。

3 个答案:

答案 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从临时组件转移到多态组件)。

[1]:https://software.intel.com/en-us/forums/topic/286107类似。

答案 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中找到每个被调用的过程。感谢你们两位的帮助。