在Fortran扩展类型中指定多态组件

时间:2019-11-11 19:36:10

标签: oop fortran derived-types

我正在编写一个模块,该模块定义两个派生类型,每个派生类型具有一个具有公共父类型的派生类型组件,如下所示。

   type :: aux0
      integer :: a
   end type aux0

   type, extends(aux0) :: aux1
      integer :: b
   end type aux1

   type, extends(aux0) :: aux2
      integer :: c
   end type aux2

我想定义两个派生类型,每个派生类型分别具有类型aux1aux2的组件。我有几个例程仅根据字段aux % a(例如fun1)执行某些工作。我想将这些方法绑定到cplx1cplx2上。因此,我使用类cplx1的字段cplx2auxaux0创建了一个公共父对象,并为公共函数编写了一个aux0变量类的接口。但是,我想在实际类型auxcplx1中指定cplx2组件的类型,因为其他一些函数需要字段aux的确定类型。我想知道如何或是否可行。

module type

   ! ... aux# types definitions

   type :: cplx0
      class(aux0), allocatable :: aux(:)
   contains
      ! routines that use aux % a
      procedure, pass :: fun1
   end type cplx0

   type, extends(cplx0) :: cplx1
      ! type(aux1) :: aux(:) ! doesn't work
   contains
      ! routines that use aux % b
   end type cplx1

   type, extends(cplx0) :: cplx2
      ! type(aux2) :: aux(:)! doesn't work
   contains
      ! routines that use aux % c
   end type cplx2

contains 

   function fun1(self)
      class(cplx0) :: self
      integer      :: i
      do i = 1, size(self % aux)
         print *, self % aux(i) % a
      end do 
   end function fun1

  ! ... more functions

end module type

如果我取消注释type(aux1),则错误是

Error: Component ‘aux’ at (1) already in the parent type at (2)

这是可以理解的,但我想知道如何规避它。

1 个答案:

答案 0 :(得分:0)

不可能。如果要通过某个组件的类型来应用约束,则基于在某种扩展层次结构中包含该组件的类型,则需要在扩展中定义该组件。

考虑到帖子中的示例代码,fun1中的逻辑无需绑定到cplx类型层次结构(cplx层次结构中的扩展看起来不会覆盖该过程)。 fun1中的逻辑可以在非类型绑定过程中,采用aux类型的多态对象,该实现将cplx的延迟绑定向前实现。

或者更普遍地,不是让fun1直接在aux组件上运行,而是让它通过绑定在该组件的等效组件上运行。例如:

module aux_module
  implicit none

  type :: aux0
    integer :: a
  end type aux0

  type, extends(aux0) :: aux1
    integer :: b
  end type aux1

  type, extends(aux0) :: aux2
    integer :: c
  end type aux2
contains
  ! Really the logic in `fun1` from the question's example code
  ! doesn't have to be within a binding.  It could be factored out.
  subroutine proc2(aux)
    class(aux0), intent(in) :: aux(:)
    integer :: i
    do i = 1, size(aux)
      print *, aux(i) % a
    end do 
  end subroutine proc2
end module aux_module

module cplx_module
  use aux_module
  implicit none

  type, abstract :: cplx0
  contains
    ! Does this have to be a binding?
    procedure :: proc1
    procedure(cplx0_get_aux), deferred :: get_aux
  end type cplx0

  interface
    function cplx0_get_aux(c)
      import cplx0
      import aux0
      implicit none
      class(cplx0), intent(in), target :: c
      ! we return a pointer in case we want it to be on the 
      ! left hand side of an assignment statement.
      class(aux0), pointer :: cplx0_get_aux(:)
    end function cplx0_get_aux
  end interface

  type, extends(cplx0) :: cplx1
    type(aux1) :: aux(2)
  contains
    procedure :: get_aux => cplx1_get_aux
  end type cplx1

  type, extends(cplx0) :: cplx2
    type(aux2) :: this_doesnt_have_to_be_called_aux(3)
  contains
    procedure :: get_aux => cplx2_get_aux
  end type cplx2
contains
  ! The internals of this could just forward to proc2.
  subroutine proc1(self)
    class(cplx0), target :: self
    integer      :: i
    associate(the_aux => self%get_aux())
      do i = 1, size(the_aux)
        print *, the_aux(i) % a
      end do 
    end associate
  end subroutine proc1

  function cplx1_get_aux(c)
    class(cplx1), intent(in), target :: c
    class(aux0), pointer :: cplx1_get_aux(:)
    cplx1_get_aux => c%aux
  end function cplx1_get_aux

  function cplx2_get_aux(c)
    class(cplx2), intent(in), target :: c
    class(aux0), pointer :: cplx2_get_aux(:)
    cplx2_get_aux => c%this_doesnt_have_to_be_called_aux
  end function cplx2_get_aux
end module cplx_module

program p
  use cplx_module
  implicit none

  type(cplx1) :: c1
  type(cplx2) :: c2

  c1 = cplx1([aux1(a=1,b=2), aux1(a=11,b=22)])
  call c1%proc1
  ! call proc2(c1%aux)

  c2 = cplx2([aux2(a=1,c=2), aux2(a=11,c=22), aux2(a=111,c=222)])
  call c2%proc1
  ! call proc2(c2%this_doesnt_have_to_be_called_aux)
end program p