Reputation: 635
The goal is to create a single allocation routine which can handle any type of rank one allocation. Our code library can then have a single call with standardized error trapping.
The compiler error follows:
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
Can this code be rectified?
The test code attempts to allocate an integer array and then a real array:
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
The first version of the code relied upon a select type
construct as shown in FORTRAN: polymorphism allocation. Another reference used is Fortran polymorphism, functions and allocation.
The gfortran version is 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)
Upvotes: 1
Views: 660
Reputation: 21431
You are encountering a deliberate restriction in the language, put in place to prevent a procedure from allocating an object to some type that does not match the declared type of the actual argument. Consider what would happen if your allocator allocated the dummy argument corresponding to array_int to be of type REAL.
You cannot achieve your goal with a single procedure, however you may be able to get away with writing a single stretch of source code, that you then INCLUDE into the body of multiple procedures, one for each declared type (and kind) that you wish to deal with.
! 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
You could put all these specific procedures behind the one generic name.
However, before you embark on this, note that in modern Fortran that allocatable things can be allocated even without an ALLOCATE statement - simple assignment to an allocatable variable can result in it being ALLOCATED. You have no way of handling error messages for those cases. There are also a very large number of coding constructs that will result in the compiler "allocating" memory for its own internal needs, which again, you have no way of handling errors for. At a lower level, the way that operating systems actually fulfil requests by program for memory is also working against you - the system may be overcommitted, and an insufficient memory error may not be reported by the operating system to the process until well after an allocate statement had completed. In combination, in the situation where available memory is very low and an attempt to allocate a small object has failed, it is possible that there is insufficient memory available for the compiler to even execute your error reporting code. There is also the issue that the compiler's runtime has a better idea of the reasons for failure and the state of the program that it can communicate via a simple integer code and a character message - for example the compiler's runtime can give the user a stack trace or similar, in addition to whatever message it could otherwise pass back to the program.
All up, for small allocations, programmer provided error reporting may not be very productive.
It can be very worthwhile for larger allocations, where the probability of specific failure is higher and it is very likely that the reason can be successfully communicated and acted on ("Your problem dimension is too big! Please make it smaller and try again...") by a user.
Upvotes: 5