I am trying to find a way to pass derived-type objects with allocatable components to Fortran procedures, without the procedures knowing the type definition. To understand why I want to do this, some info on the background may be useful.
Consider a generic procedure that contains a sparse-matrix - vector multiplication, like a Lanczos diagonalization routine. The procedure itself does not use the matrix, only the vector. The only thing the procedure needs to do with the matrix is to pass it, along with the vector, to a matrix-vector multiplication routine. The sparse matrix has to be a derived-type variable with allocatable components.
The way I see it, the procedure does not need to know the data type of the sparse matrix. It just needs to pass it to the matrix-vector multiplication routine, which will then decode it appropriately. What I tried to do was to use the TRANSFER
intrinsic function to cast the derived-type variable into an allocatable array of bytes, and then transfer it back to the initial derived-type variable. This unfortunately does not work with derived-type variables with allocatable components, see the following two links: Link 1,
Link 2
My question is therefore the following, as stated above: is there a reasonable way* to pass derived-type objects with allocatable components to Fortran procedures, without the procedures knowing the type definition?
*Note: I know that I could use customized internal formatted writes to store a derived-type variable into an intrinsic-type array, e.g. a character array. This seems to me extremely weird, but maybe I'm wrong?
EDIT :: as Vladimir F says below, the behaviour when invoking TRANSFER
for derived types with allocatable components is non-standard. Yet, I found out that, surprisingly, this works with the latest version of the PGI compiler. Here is a test program:
module Increments
TYPE :: structure
real s
integer q
real, allocatable :: flt1d(:)
END TYPE structure
contains
subroutine IncrementAndPrintReal(data)
character(len=1) :: data(:)
real :: r
r = transfer(data, r)
r = r + 1.0
print *,r
data = transfer(r, data)
end subroutine
subroutine IncrementAndPrintInteger(data)
character(len=1) :: data(:)
integer :: i
i = transfer(data, i)
i = i + 1
print *,i
data = transfer(i, data)
end subroutine
subroutine IncrementTenTimes(incrFunc, data)
character(len=1) :: data(:)
integer :: i
interface
subroutine incrFunc(data)
character(len=1) :: data(:)
end subroutine
end interface
do i = 1, 10
call incrFunc(data)
enddo
end subroutine
subroutine IncrementAndPrintStructure(data)
character(len=1) :: data(:)
type(structure) :: t0
t0 = transfer(data, t0)
print *, t0%flt1d
t0%flt1d = t0%flt1d(1) + 1
print*
data = transfer(t0, data)
end subroutine
end module
program main
use Increments
character(len=1), allocatable :: data(:)
integer :: lengthData
real :: r = 5.0
integer :: i = 10
type(structure) :: t
t%s = 1
t%q = 2
allocate(t%flt1d(11))
t%flt1d = 3
lengthData = size(transfer(r, data))
allocate(data(lengthData))
data = transfer(r, data)
call IncrementTenTimes(IncrementAndPrintReal, data)
deallocate(data)
lengthData = size(transfer(i, data))
allocate(data(lengthData))
data = transfer(i, data)
call IncrementTenTimes(IncrementAndPrintInteger, data)
deallocate(data)
lengthData = size(transfer(t, data))
allocate(data(lengthData))
data = transfer(t, data)
call IncrementTenTimes(IncrementAndPrintStructure, data)
deallocate(data)
end program
And here are the results for different compilers:
ifort (v11.1 and v12.1.5):
==============
6.000000
7.000000
8.000000
9.000000
10.00000
11.00000
12.00000
13.00000
14.00000
15.00000
11
12
13
14
15
16
17
18
19
20
3.000000 3.000000 3.000000 3.000000 3.000000
3.000000 3.000000 3.000000 3.000000 3.000000
3.000000
0.0000000E+00 0.0000000E+00 4.000000 4.000000 4.000000
4.000000 4.000000 4.000000 4.000000 4.000000
4.000000
0.0000000E+00 0.0000000E+00 1.000000 1.000000 1.000000
1.000000 1.000000 1.000000 1.000000 1.000000
1.000000
0.0000000E+00 0.0000000E+00 1.000000 1.000000 1.000000
1.000000 1.000000 1.000000 1.000000 1.000000
1.000000
0.0000000E+00 0.0000000E+00 1.000000 1.000000 1.000000
1.000000 1.000000 1.000000 1.000000 1.000000
1.000000
0.0000000E+00 0.0000000E+00 1.000000 1.000000 1.000000
1.000000 1.000000 1.000000 1.000000 1.000000
1.000000
0.0000000E+00 0.0000000E+00 1.000000 1.000000 1.000000
1.000000 1.000000 1.000000 1.000000 1.000000
1.000000
0.0000000E+00 0.0000000E+00 1.000000 1.000000 1.000000
1.000000 1.000000 1.000000 1.000000 1.000000
1.000000
0.0000000E+00 0.0000000E+00 1.000000 1.000000 1.000000
1.000000 1.000000 1.000000 1.000000 1.000000
1.000000
0.0000000E+00 0.0000000E+00 1.000000 1.000000 1.000000
1.000000 1.000000 1.000000 1.000000 1.000000
1.000000
gfortran (gcc version 4.4.3):
=============================
6.0000000
7.0000000
8.0000000
9.0000000
10.000000
11.000000
12.000000
13.000000
14.000000
15.000000
11
12
13
14
15
16
17
18
19
20
3.0000000 3.0000000 3.0000000 3.0000000 3.0000000 3.0000000 3.0000000 3.0000000 3.0000000 3.0000000 3.0000000
1.82795013E-38 0.0000000 4.0000000 4.0000000 4.0000000 4.0000000 1.54142831E-44 1.12103877E-44 2.80259693E-45 4.0000000 4.0000000
*** glibc detected *** ./tr: double free or corruption (fasttop): 0x0000000000c70b20 ***
======= Backtrace: =========
/lib/libc.so.6(+0x77806)[0x7f9fb0e59806]
/lib/libc.so.6(cfree+0x73)[0x7f9fb0e600d3]
./tr[0x4010af]
./tr[0x401175]
./tr[0x40262e]
./tr[0x4026ea]
/lib/libc.so.6(__libc_start_main+0xfd)[0x7f9fb0e00c4d]
./tr[0x400a59]
======= Memory map: ========
00400000-00403000 r-xp 00000000 00:16 123 /home/stefanos/Documents/dig/progs/other/transfer/tr
00602000-00603000 r--p 00002000 00:16 123 /home/stefanos/Documents/dig/progs/other/transfer/tr
00603000-00604000 rw-p 00003000 00:16 123 /home/stefanos/Documents/dig/progs/other/transfer/tr
00c70000-00c91000 rw-p 00000000 00:00 0 [heap]
7f9fac000000-7f9fac021000 rw-p 00000000 00:00 0
7f9fac021000-7f9fb0000000 ---p 00000000 00:00 0
7f9fb0de2000-7f9fb0f5c000 r-xp 00000000 08:01 5512795 /lib/libc-2.11.1.so
7f9fb0f5c000-7f9fb115b000 ---p 0017a000 08:01 5512795 /lib/libc-2.11.1.so
7f9fb115b000-7f9fb115f000 r--p 00179000 08:01 5512795 /lib/libc-2.11.1.so
7f9fb115f000-7f9fb1160000 rw-p 0017d000 08:01 5512795 /lib/libc-2.11.1.so
7f9fb1160000-7f9fb1165000 rw-p 00000000 00:00 0
7f9fb1165000-7f9fb117b000 r-xp 00000000 08:01 5505258 /lib/libgcc_s.so.1
7f9fb117b000-7f9fb137a000 ---p 00016000 08:01 5505258 /lib/libgcc_s.so.1
7f9fb137a000-7f9fb137b000 r--p 00015000 08:01 5505258 /lib/libgcc_s.so.1
7f9fb137b000-7f9fb137c000 rw-p 00016000 08:01 5505258 /lib/libgcc_s.so.1
7f9fb137c000-7f9fb13fe000 r-xp 00000000 08:01 5505028 /lib/libm-2.11.1.so
7f9fb13fe000-7f9fb15fd000 ---p 00082000 08:01 5505028 /lib/libm-2.11.1.so
7f9fb15fd000-7f9fb15fe000 r--p 00081000 08:01 5505028 /lib/libm-2.11.1.so
7f9fb15fe000-7f9fb15ff000 rw-p 00082000 08:01 5505028 /lib/libm-2.11.1.so
7f9fb15ff000-7f9fb16ea000 r-xp 00000000 08:01 787983 /usr/lib/libgfortran.so.3.0.0
7f9fb16ea000-7f9fb18e9000 ---p 000eb000 08:01 787983 /usr/lib/libgfortran.so.3.0.0
7f9fb18e9000-7f9fb18ea000 r--p 000ea000 08:01 787983 /usr/lib/libgfortran.so.3.0.0
7f9fb18ea000-7f9fb18eb000 rw-p 000eb000 08:01 787983 /usr/lib/libgfortran.so.3.0.0
7f9fb18eb000-7f9fb18ec000 rw-p 00000000 00:00 0
7f9fb18ec000-7f9fb190c000 r-xp 00000000 08:01 5512780 /lib/ld-2.11.1.so
7f9fb1ad9000-7f9fb1add000 rw-p 00000000 00:00 0
7f9fb1b09000-7f9fb1b0b000 rw-p 00000000 00:00 0
7f9fb1b0b000-7f9fb1b0c000 r--p 0001f000 08:01 5512780 /lib/ld-2.11.1.so
7f9fb1b0c000-7f9fb1b0d000 rw-p 00020000 08:01 5512780 /lib/ld-2.11.1.so
7f9fb1b0d000-7f9fb1b0e000 rw-p 00000000 00:00 0
7fff5e340000-7fff5e356000 rw-p 00000000 00:00 0 [stack]
7fff5e396000-7fff5e397000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted
pgfortran (v12.5):
==================
6.000000
7.000000
8.000000
9.000000
10.00000
11.00000
12.00000
13.00000
14.00000
15.00000
11
12
13
14
15
16
17
18
19
20
3.000000 3.000000 3.000000 3.000000
3.000000 3.000000 3.000000 3.000000
3.000000 3.000000 3.000000
4.000000 4.000000 4.000000 4.000000
4.000000 4.000000 4.000000 4.000000
4.000000 4.000000 4.000000
5.000000 5.000000 5.000000 5.000000
5.000000 5.000000 5.000000 5.000000
5.000000 5.000000 5.000000
6.000000 6.000000 6.000000 6.000000
6.000000 6.000000 6.000000 6.000000
6.000000 6.000000 6.000000
7.000000 7.000000 7.000000 7.000000
7.000000 7.000000 7.000000 7.000000
7.000000 7.000000 7.000000
8.000000 8.000000 8.000000 8.000000
8.000000 8.000000 8.000000 8.000000
8.000000 8.000000 8.000000
9.000000 9.000000 9.000000 9.000000
9.000000 9.000000 9.000000 9.000000
9.000000 9.000000 9.000000
10.00000 10.00000 10.00000 10.00000
10.00000 10.00000 10.00000 10.00000
10.00000 10.00000 10.00000
11.00000 11.00000 11.00000 11.00000
11.00000 11.00000 11.00000 11.00000
11.00000 11.00000 11.00000
12.00000 12.00000 12.00000 12.00000
12.00000 12.00000 12.00000 12.00000
12.00000 12.00000 12.00000
By asking this question in the PGI forum, I learned about allocatable unlimited polymorphic objects CLASS(*), ALLOCATABLE
(thanks mkcolg). This seems to be a better way to pass derived-type data around, since it allows for proper type checking when the data reaches the relevant module / procedure. Examples can be found in the PGInsider article Object-Oriented Programming in Fortran 2003 Part 2: Data Polymorphism.