我想让Fortan API使用C代码 . Fortran代码主要包含派生类型,这些类型不能通过使用 iso_c_binding
模块与C互操作 . 我发现此帖子包含类似的问题https://software.intel.com/en-us/forums/topic/394312 . 这篇文章c-fortran interoperability - derived types with pointers表明有新的标准可以解决这个问题,但它仍然没有被任何fortran编译器实现 . 我用gfortran 4.8.3和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_
相应的C代码是:
#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);
}
该程序返回:
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
有趣的是, string
和 someInteger
变量从C中正确返回,而 array
返回错误值 . 我假设原因是数组在Fortran代码中不是连续分配的,所以C从 *(array+i)
开始返回连续的内存位置 . 是否有可能确保Fortran连续分配 array
或存在任何其他更可移植的方式来访问来自C的可分配组件的派生类型 .
1 回答
real, dimension(:), allocatable :: array
不等同于C指针 . 你的方法不能简单地工作 .实际上在对象中存在数组描述符 . 描述符比包含连续分配的数组的地址(C样式指针) .
您可以在https://software.intel.com/en-us/node/510871中阅读有关特定编译器的描述符实现的更多信息 .
您可以尝试找出描述符中地址的偏移量并手动使用它 . 无论如何,您的C结构必须更复杂,并且必须包含另一个结构 - 描述符 . 你应该小心地获得结构的大小 . 英特尔的手册表明,对于一维数组,32位为36位,64位二进制为72位 .
您对其他问题的链接和我的回答建议使用TS 29113 . 英特尔's descriptor should be compatible this one and that'为什么英特尔的手册很有用 . 然而,gfortran尚未兼容并使用不同的描述符 .