Fortran 中的多态数组分配例程

Polymorphic array allocation routine in Fortran

目标是创建一个可以处理任何类型的一级分配的分配例程。然后,我们的代码库可以通过标准化错误捕获进行一次调用。

编译错误如下:

generic_allocation.f08:32:27:

         call myAllocator ( array_int, source_int, lambda )
                           1
Error: Actual argument to ‘myarray’ at (1) must be polymorphic
generic_allocation.f08:33:27:

         call myAllocator ( array_real, source_real, lambda )
                           1
Error: Actual argument to ‘myarray’ at (1) must be polymorphic

这个代码可以改正吗?

测试代码尝试分配一个整数数组,然后分配一个实数数组:

module mAllocator
    implicit none
contains
    subroutine myAllocator ( myArray, source_type, lambda )
        class ( * ), allocatable, intent ( inout ) :: myArray ( : )
        class ( * ),              intent ( in )    :: source_type
        integer,                  intent ( in )    :: lambda

        integer                 :: alloc_status  = 0
        character ( len = 512 ) :: alloc_message = ''
            allocate ( myArray ( 1 : lambda ), source = source_type, stat = alloc_status, errmsg = alloc_message )
            if ( alloc_status /= 0 ) then
                write ( *, "( ' allocation  errmsg = ', g0, '.' )" ) trim ( alloc_message )
                stop 'Fatal error in subroutine myAllocator'
            end if
    end subroutine myAllocator
end module mAllocator

program generic_allocation

    use mAllocator, only : myAllocator

    implicit none

    integer, parameter   :: lambda = 10
    integer, parameter   :: source_int = 1
    real,    parameter   :: source_real = 1.0

    integer, allocatable :: array_int  ( : )
    real,    allocatable :: array_real ( : )
        call myAllocator ( array_int, source_int, lambda )
        call myAllocator ( array_real, source_real, lambda )
end program generic_allocation

代码的第一个版本依赖于 select type 构造,如 FORTRAN: polymorphism allocation. Another reference used is Fortran polymorphism, functions and allocation 所示。

gfortran 版本为 6.0

$ gfortran -v
Using built-in specs.
COLLECT_GCC=gfortran
COLLECT_LTO_WRAPPER=/opt/gnu/6.0/libexec/gcc/x86_64-pc-linux-gnu/6.0.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ./configure --prefix=/opt/gnu/6.0 --enable-languages=c,c++,fortran,lto --disable-multilib --disable-werror
Thread model: posix
gcc version 6.0.0 20160227 (experimental) (GCC) 

您遇到了语言中的故意限制,该限制旨在防止过程将对象分配给与实际参数的声明类型不匹配的某种类型。考虑一下如果您的分配器将对应于 array_int 的伪参数分配为 REAL 类型会发生什么。

您无法通过单个过程实现您的目标,但是您可以编写一段源代码,然后将其包含到多个过程的主体中,一个用于每个声明的类型(并且kind) 你想处理的。

! In AllocateBody.i90
integer, intent(in) :: lambda
integer                 :: alloc_status
character ( len = 512 ) :: alloc_message
allocate ( myArray ( 1 : lambda ),  &
    source = source_type,  &
    stat = alloc_status,  &
    errmsg = alloc_message )
if ( alloc_status /= 0 ) then
  write ( *, "( ' allocation  errmsg = ', g0, '.' )" )  &
      trim ( alloc_message )
  stop 'Fatal error in subroutine myAllocator'
end if    


! Elsewhere.
subroutine my_allocator_integer(myArray, source_type, lambda )
  integer, intent(out), allocatable :: myArray(:)
  integer, intent(in) :: source_type
  include 'AllocateBody.i90'
end subroutine my_allocator_integer

subroutine my_allocator_real(myArray, source_type, lambda )
  real, intent(out), allocatable :: myArray(:)
  real, intent(in) :: source_type
  include 'AllocateBody.i90'
end subroutine my_allocator_real

subroutine my_allocator_foo(myArray, source_type, lambda )
  type(foo), intent(out), allocatable :: myArray(:)
  type(foo), intent(in) :: source_type
  include 'AllocateBody.i90'
end subroutine my_allocator_foo

您可以将所有这些特定程序放在一个通用名称后面。

但是,在开始之前,请注意在现代 Fortran 中,即使没有 ALLOCATE 语句也可以分配可分配的内容 - 对可分配变量的简单赋值可能导致它被分配。您无法处理这些情况的错误消息。还有大量的编码结构会导致编译器 "allocating" 内存满足其自身的内部需求,同样,您无法处理错误。在较低的层次上,操作系统实际满足程序对内存的请求的方式也在对你不利——系统可能被过度使用,并且操作系统可能直到分配后很久才向进程报告内存不足错误声明已经完成。结合起来,在可用内存非常低并且尝试分配小对象失败的情况下,可能没有足够的可用内存供编译器执行您的错误报告代码。还有一个问题是,编译器的运行时可以更好地了解失败的原因以及它可以通过简单的整数代码和字符消息进行通信的程序状态——例如,编译器的运行时可以为用户提供堆栈跟踪或类似的,除了它可以传递回程序的任何消息。

总而言之,对于小分配,程序员提供的错误报告可能不是很有效率。

对于较大的分配来说,这可能是非常值得的,其中特定失败的可能性更高,并且很可能可以成功地传达原因并由用户采取行动 ("Your problem dimension is too big! Please make it smaller and try again...")。