Reputation: 11
I am trying to get Fortan API to work with the C code. The Fortran code contains mainly derived types which are NOT interoperable with the C by using iso_c_binding
module. I have found this post
that contains similar problem https://software.intel.com/en-us/forums/topic/394312. This post c-fortran interoperability - derived types with pointers suggest that there is new standard that could solve this problem, but it is still not implemented by any fortran compiler. I have tried this code with gfortran 4.8.3 and gcc:
module types
type SomeDerivedType
integer :: someInteger
character*24 :: string
real, dimension(:), allocatable :: array
end type
end module
! Given the address of a C_PTR this subroutine allocates the memory required
! for a SomeDerivedType derived data type, initializes it with the given values,
! and stores the C_LOCated address in the C_PTR.
subroutine makeDerivedType_(cdt) bind(c, name='makeDerivedType_')
use iso_c_binding
use types
type (C_PTR) :: cdt
type (SomeDerivedType), pointer :: fdt
integer :: i
allocate(fdt)
allocate(fdt%array(0:10))
fdt%someInteger = 4
fdt%string = 'Hello'
do i = 0,9
fdt%array(i) = i*2.0
end do
cdt = C_LOC(fdt)
end subroutine makeDerivedType_
! This subroutine converts the given C_PTR value to a fortran pointer making
! it accessible again from fortran
subroutine examineDerivedType_(this) bind(c, name='examineDerivedType_')
use iso_c_binding
use types
type(C_PTR), value :: this
type(SomeDerivedType), pointer :: that
integer :: i
call C_F_POINTER(this,that)
write(*,*) "that%someInteger", that%someInteger
do i = 0,9
print*, "that%array:", that%array(i)
end do
end subroutine examineDerivedType_
The corresponding C code is:
#include<stdio.h>
extern void makeDerivedType_(void **m);
extern void examineDerivedType_( void *m );
typedef struct SomeDerivedType {
int someInteger;
char string[24];
double *array;
}SomeDerivedType;
int main(int argc, char **argv)
{
void *m = 0;
char *teststring[2] = {"test1", "test2"};
makeDerivedType_(&m);
SomeDerivedType* dt = (SomeDerivedType*) m;
for(int i = 0; i < 10; i++)
printf("%f\n",dt->array[i]);
printf("someInteger: %i\n",dt->someInteger);
printf("%s \n", dt->string);
examineDerivedType_(m);
}
This program returns:
2.000000
8192.001968
524288.126953
8388610.039062
67108880.375000
0.000000
0.000000
0.000000
0.000000
0.000000
someInteger: 4
Hello ?
that%someInteger 4
that%array: 0.00000000
that%array: 2.00000000
that%array: 4.00000000
that%array: 6.00000000
that%array: 8.00000000
that%array: 10.0000000
that%array: 12.0000000
that%array: 14.0000000
that%array: 16.0000000
that%array: 18.0000000
It is interesting to see that string
and someInteger
variables are properly returned from C,
while array
returns bad values. I assumed that the reason is that array is not contiguously allocated in Fortran code so C returns contiguous memory locations starting from *(array+i)
. Is it possible to ensure that Fortran allocates array
contiguously or exists any other more portable way to access derived type with allocatable components from C.
Upvotes: 1
Views: 1438
Reputation: 60088
The ultimate problem is just that your Fortran code uses real
and the C code uses a double
. If you make it compatible, it will work in this simple case.
real, dimension(:), allocatable :: array
is NOT equivalent to a C pointer.
There is actually an array descriptor present in the object. The descriptor than contains a address (C style pointer) to the contiguously allocated array. The problem would be apparent if you used a different order of the components (e.g., the array first) or if there were additional components after the array.
You can read more about the implementation of the descriptor for one particular compiler in Handling Fortran Array Descriptors.
The offset of the address in the descriptor will typically be 0. That means that your code, even if incorrect, could actually work for this simple access, but you must also count with the padding in the derived type/struct.
In any way, your C struct must be more complicated and must include another struct - the descriptor. You should be careful to get the size of the struct right. The Intel's manual suggests it is 36 bytes for 32bit and 72 bytes for 64bit binaries for one-dimensional arrays.
Your link to the other question and my answer there suggests to use TS 29113. Intel's descriptor should be compatible this one and that's why the Intel's manual is useful. gfortran, however, is not yet compatible and uses a different descriptor.
Upvotes: 4