指向数组的指针作为 Fortran 派生类型中的成员变量

Pointers to arrays as member variables in Fortran derived type

在 Fortran 中,无法将派生类型的成员变量作为目标。 (我想这与未指定派生类型如何存储在内存中的标准有关?)但是,我可以将指针作为成员变量并将指针与指针相关联。就像我在下面的示例中所做的那样。

module DataMod

   type DataType

      real(8), private, dimension(:,:), pointer, contiguous :: A
      real(8), private, dimension(:,:), pointer, contiguous :: B

      integer :: n

   contains 

      procedure :: alloc
      procedure :: set
      procedure :: print_
      final :: dealloc

   end type DataType

   interface DataType
      procedure :: NewDataType
   end interface DataType

   contains

   function NewDataType(dimension_) result(new)

      integer, intent(in) :: dimension_

      type(DataType) :: new

      new%n = dimension_

   end function NewDataType

   subroutine alloc(dataObject)

      class(DataType) :: dataObject

      allocate(dataObject%A(dataObject%n,dataObject%n))
      allocate(dataObject%B(dataObject%n,dataObject%n))

   end subroutine alloc

   subroutine set(dataObject, datas, choice)

      class(DataType) :: dataObject

      real(8), dimension(dataObject%n,dataObject%n), intent(in) :: datas

      character(len=1), intent(in) :: choice

      real(8), dimension(:,:), pointer :: dataPointer
      integer :: i,j

      if(choice .eq. 'A') then
         datapointer => dataObject%A
      elseif(choice .eq. 'B') then
         datapointer => dataObject%B
      else
         stop
      endif

      do j = 1,dataObject%n
         do i = 1,dataObject%n
            datapointer(i,j) = datas(i,j)
         enddo
      enddo

   end subroutine set

   subroutine print_(dataObject)

      class(DataType), intent(in) :: dataObject

      print *, 'A'
      print *, dataObject%A(1:dataObject%n,1:dataObject%n)
      print * 
      print *, 'B'
      print *, dataObject%B(1:dataObject%n,1:dataObject%n)

   end subroutine print_

   subroutine dealloc(dataObject)

      type(DataType) :: dataObject

      deallocate(dataObject%A)
      deallocate(dataObject%B)

   end subroutine dealloc

end module DataMod


program DataTest

   use DataMod, only: DataType

   implicit none

   real(8), dimension(2,2) :: testArray
   type(DataType) :: testType

   testType = DataType(2)
   call testType%alloc()

   testArray(1,1) = 1
   testArray(2,1) = 2
   testArray(1,2) = 3
   testArray(2,2) = 4

   call testType%set(testArray, 'A')

   testArray(1,1) = 5
   testArray(2,1) = 6
   testArray(1,2) = 7
   testArray(2,2) = 8

   call testType%set(testArray, 'B')

   call testType%print_()

end program DataTest

在 set 例程中,我使用 if 语句设置一个指针,以决定它是否应该将传入矩阵转储到 A 或 B 中。在我目前正在处理的程序中,我必须决定哪四个的组合将不同的矩阵相乘并设置一对指针比编写 16 个几乎相同的 dgemm 调用要好得多。

我的问题是,除了悬挂指针等的正常危险之外,这种方法是否还有任何问题,或者没有指针可以做到这一点的方法?不应从对象外部访问数组。有任何性能问题吗?

类型定义中的组件可能不使用 TARGET 属性声明(除了缺少语法,这将与当前语言中的其他概念和规则不一致),但如果派生类型的变量具有 TARGET 属性,那么它的所有子对象也都具有 TARGET 属性。对于类型定义:

type DataType
  real(8), private, dimension(:,:), allocatable :: A
  real(8), private, dimension(:,:), allocatable :: B
  ...

程序集可以写成...

 subroutine set(dataObject, datas, choice)
   class(DataType), TARGET :: dataObject
   real(8), dimension(dataObject%n,dataObject%n), intent(in) :: datas
   character(len=1), intent(in) :: choice
   real(8), dimension(:,:), pointer :: dataPointer

   ! require dataobject%A and ..%B to already be allocated.
   if(choice .eq. 'A') then
     datapointer => dataObject%A
   elseif(choice .eq. 'B') then
     datapointer => dataObject%B
   else
     stop
   endif

   datapointer = datas    ! or some other operation.
   ...

dataPointer 可以声明为连续的,它指向的可分配数组始终是连续的。)

没有 TARGET 属性的实际参数可能与具有 TARGET 属性的虚拟参数相关联。如果是这种情况,则与伪参数关联的指针在过程执行完成时变为未定义。 (在某些情况下,此类指针也可能未定义,即使实际参数具有 TARGET 属性 - 有关详细信息,请参阅 F2018 15.5.2.4p8 - 但这些情况不适用于标量。)

因此,在一般情况下,如果指向派生类型对象的组件之一的指针需要比上面的 set 这样的过程更长寿(例如,如果 dataPointer 不是局部于set) 并且你不能保证实参总是有TARGET属性,那么原来使用指针组件的方法可能更合适。问题中的实现似乎没问题——尽管我建议使终结器成为 IMPURE ELEMENTAL 以使事情对未来的变化更加稳健。