首页 文章

如何在C中传递派生类型,它与C结构不相互影响?

提问于
浏览
1

我想让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

有趣的是, stringsomeInteger 变量从C中正确返回,而 array 返回错误值 . 我假设原因是数组在Fortran代码中不是连续分配的,所以C从 *(array+i) 开始返回连续的内存位置 . 是否有可能确保Fortran连续分配 array 或存在任何其他更可移植的方式来访问来自C的可分配组件的派生类型 .

1 回答

  • 3

    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尚未兼容并使用不同的描述符 .

相关问题