【问题标题】:how to write wrapper for 'allocate'如何为“分配”编写包装器
【发布时间】:2021-10-05 22:04:30
【问题描述】:

我正在尝试为“分配”函数编写一个包装器,即接收数组和维度、分配内存并返回分配的数组的函数。最重要的是该函数必须使用不同等级的数组。但是我必须在函数接口中明确声明数组的等级,在这种情况下,只有当我将特定等级的数组作为参数传递时,代码才会编译。例如,这段代码无法编译:

module memory_allocator
contains 

  subroutine memory(array, length)
    implicit none

    real(8), allocatable, intent(out), dimension(:) :: array
    integer, intent(in) :: length

    integer :: ierr

    print *, "memory: before: ", allocated(array)

    allocate(array(length), stat=ierr)
    if (ierr /= 0) then
      print *, "error allocating memory: ierr=", ierr
    end if

    print *, "memory: after: ", allocated(array)

  end subroutine memory

  subroutine freem(array)
    implicit none

    real(8), allocatable, dimension(:) :: array

    print *, "freem: before: ", allocated(array)
    deallocate(array)
    print *, "freem: after: ", allocated(array)

  end subroutine freem

end module memory_allocator

program alloc
  use memory_allocator
  implicit none

  integer, parameter :: n = 3
  real(8), allocatable, dimension(:,:,:) :: foo
  integer :: i, j, k

  print *, "main: before memory: ", allocated(foo)
  call memory(foo, n*n*n)
  print *, "main: after memory: ", allocated(foo)

  do i = 1,n
    do j = 1,n
      do k = 1, n
        foo(i, j, k) = real(i*j*k)
      end do
    end do
  end do

  print *, foo

  print *, "main: before freem: ", allocated(foo)
  call freem(foo)  
  print *, "main: after freem: ", allocated(foo)

end program alloc

编译错误:

gfortran -o alloc alloc.f90 -std=f2003
alloc.f90:46.14:

  call memory(foo, n*n*n)
              1
Error: Rank mismatch in argument 'array' at (1) (1 and 3)
alloc.f90:60.13:

  call freem(foo)  
             1
Error: Rank mismatch in argument 'array' at (1) (1 and 3)  

有没有办法实现这样的包装器?..

谢谢!

【问题讨论】:

    标签: fortran


    【解决方案1】:

    这可以通过通用接口块来完成。您必须为要处理的每个等级创建过程,例如 memory_1d、memory_2d、... memory_4d。 (显然很多剪切和粘贴。)然后您编写一个通用接口块,为所有这些过程提供替代名称内存作为通用过程名称。当您调用内存时,编译器会根据参数的排名来区分应该调用哪个 memory_Xd。您的 freem 函数也是如此。

    这就是 sin 等内部函数长期以来的工作方式——您可以使用各种预置的实参数或复杂参数调用 sin,编译器会计算出要调用的实际 sin 函数。在真正古老的 FORTRAN 中,您必须为不同的 sin 函数使用不同的名称。现在现代的 Fortran,您可以使用自己的例程设置相同的东西。

    编辑:添加演示方法和语法的代码示例:

    module double_array_mod
    
       implicit none
    
       interface double_array
          module procedure double_vector
          module procedure double_array_2D
       end interface double_array
    
       private  ! hides items not listed on public statement 
       public :: double_array
    
    contains
    
       subroutine double_vector (vector)
          integer, dimension (:), intent (inout) :: vector
          vector = 2 * vector
       end subroutine double_vector
    
       subroutine double_array_2D (array)
          integer, dimension (:,:), intent (inout) :: array
          array = 2 * array
       end subroutine double_array_2D
    
    end module double_array_mod
    
    
    program demo_user_generic
    
       use double_array_mod
    
       implicit none
    
       integer, dimension (2) :: A = [1, 2]
       integer, dimension (2,2) :: B = reshape ( [11, 12, 13, 14], [2,2] )
       integer :: i
    
       write (*, '( / "vector before:", / 2(2X, I3) )' )  A
       call double_array (A)
       write (*, '( / "vector after:", / 2(2X, I3) )' )  A
    
       write (*, '( / "2D array before:" )' )
       do i=1, 2
          write (*, '( 2(2X, I3) )' )  B (i, :)
       end do
       call double_array (B)
       write (*, '( / "2D array after:" )' )
       do i=1, 2
          write (*, '( 2(2X, I3) )' )  B (i, :)
       end do   
    
       stop
    end program demo_user_generic
    

    【讨论】:

    • 非常感谢!虽然它需要在分配器模块中重复代码,但至少我可以在调用此分配器函数时使用通用名称。这就是我想要的。
    【解决方案2】:

    subroutine memory(array, length) 具有第一个虚拟参数一维数组 (real(8), allocatable, intent(out), dimension(:) :: array)。

    使用 3 维数组 foo (real(8), allocatable, dimension(:,:,:) :: foo) 从主程序调用此子程序显然是错误的。这就是编译器实际所说的。

    如果你真的需要这样的子程序,为每个不同维度的数组编写一对memory/freem子程序——一个子程序对用于一维数组,另一个子程序对用于二维数组,等等。

    顺便说一下,memory 子程序通常会有所不同,因为要分配 n 维数组,您需要将 n 个范围传递给上述子程序。

    【讨论】:

    • kemiisto,我知道这个编译错误是绝对明显的。我也明白实现我想要的一种方法是为不同的等级编写单独的分配器。我将不得不这样做作为最后的手段:) 但我的问题是 - 是否有一种合法的 fortran 方式来编写一个包装器来分配具有相同功能的,即通用 wrt 等级......无论如何,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-24
    • 1970-01-01
    • 2016-08-15
    相关资源
    最近更新 更多