指向数组的指针作为 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 以使事情对未来的变化更加稳健。
在 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 以使事情对未来的变化更加稳健。