【问题标题】:Pointers to arrays as member variables in Fortran derived type指向数组的指针作为 Fortran 派生类型中的成员变量
【发布时间】:2020-10-01 04:02:53
【问题描述】:

在 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 调用要好得多。

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

【问题讨论】:

  • 不是派生类型的组件的指针是否总是一个过程的局部变量,该过程接受一个派生类型的相关对象的参数?例如,在子程序set 中,指针datapointerset 的本地指针。
  • 在这种情况下,是的。在某些情况下,直接从外部访问数据是相关的,但制作副本会更安全、更容易。

标签: pointers fortran derived-types fortran2008


【解决方案1】:

类型定义中的组件可能不使用 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 使事情对未来的变化更加稳健。

【讨论】:

  • 感谢您的明确答复。为什么它应该是元素?如果有一个 DataTypes 数组?
  • 是的 - 这种变化总是发生在较大的程序中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 2013-05-07
  • 1970-01-01
  • 2017-12-27
  • 2014-02-06
相关资源
最近更新 更多