微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 ISO C BINDINGS 将结构数组转换为派生类型数组?

如何解决如何使用 ISO C BINDINGS 将结构数组转换为派生类型数组?

我有以下测试 C 库:

#include <stdlib.h>

struct mystruct {
    int a;
    double  b;
};

struct mystruct *return_array_of_structs(int *size);

struct mystruct *return_array_of_structs(int *size) {

    int i;
    struct mystruct *ptr;
    ptr = malloc(sizeof(struct mystruct)*10);
    
    for(i=0; i<10; i++) {
        ptr[i].a = i+1;
        ptr[i].b = (i+1)*1.0L;
    }
    
    *size=10;
    return ptr;
}

以及以下打算使用 f2py 编译的模块:

module test_c_lib

        use iso_c_binding
        implicit none

        type :: t_mystruct
            integer :: a
            real(8) :: b
        end type
        
contains

        subroutine test()

                use iso_c_binding
                
                type(c_ptr)     :: ret_c_ptr            
                integer         :: length
                
                ! Interface to C function
                interface                        
                        type(c_ptr) function c_return_array_of_structs(a) bind(C,name="return_array_of_structs")
                                import
                                integer(c_int) :: a
                        end function
                end interface

                ! Call C function               
                ret_c_ptr = c_return_array_of_structs(length)

        
        end subroutine

end module

下面的 makefile 编译了这个:

f_mod.so:       f_mod.f90 c_lib.o
                f2py -c f_mod.f90 c_lib.o -m f_mod

c_lib.o:        c_lib.c
                gcc -c -fpic c_lib.c -o c_lib.o

我可以在 Python 中正常加载库并执行 test() 子例程没有问题。

import f_mod
f_mod.test_c_lib.test()

但我不知道如何将 ret_c_ptr 转换为派生类型 t_mystruct 的数组,我通常可以在 Fortran 中将其作为数组进行操作。任何提示? 注意:问题与 iso c bindings 和 Fortran 有关,而不是 f2py 或其与 Python 的集成。

解决方法

如果您有一个 C 指针并且想要将 Fortran 指针与该 C 指针的目标相关联,您想要使用 c_f_pointer 来进行该关联。 c_f_pointer 不(通常)关心类型是否为内在类型,或者是否为标量。

让我们将 Fortran 派生类型定义为可互操作的1

type,bind(c) :: t_mystruct
  integer(c_int) :: a
  real(c_double) :: b
end type

然后有一个该类型的 Fortran 指针数组:

type(t_mystruct),pointer :: ptr_f(:)

通过 ptr_c 指向一个合适的内存块,我们可以进行关联:

call c_f_pointer(ptr_c,ptr_f,[length])

使 ptr_f 成为指向 C 目标的 [length] 形状数组。


1 互操作性不是必需的,但如果可以的话,肯定会有所帮助。

,

这个问题的正确答案是上面 francescalus 提供的答案,但是我包含了完整的代码摘录以及 f2py 向 Python 公开的函数。

f2py 不允许返回派生类型的 numpy 数组(这将是自定义 dtype 的 numpy 向量),因此您需要为每个字段返回单独的向量。同样 f2py 不能返回没有明确大小的可分配数组,因此该函数需要拆分为两次调用,一次获取长度,另一次获取实际数组。

虽然这些是 f2py 的明显限制,但这允许直接调用 Fortran 函数(或本示例中的 C 封装函数)并获取 numpy 数组作为返回值。

代码没有优化,因为它意味着调用 C 函数两次,也许有可能像在 C 中那样使用某种静态变量,所以第二次调用已经计算了这些值,虽然我不知道:

  1. 如果这在 Fortran 中是可能的
  2. 如果这在共享对象的上下文中是可能的(这是 f2py 从模块创建的)

因为关于 f2py 的例子并不多,这可能对某人有用:

    module test_c_lib
    
            use iso_c_binding
            implicit none
            
            type,bind(c)  :: t_mystruct
                    integer(c_int) :: a
                    real(c_double) :: b
            end type
            
    contains

        subroutine get_array_len(l)

                use iso_c_binding
                
                integer,intent(out)    :: l
                
                type(c_ptr)               :: ret_c_ptr            
                integer                   :: length
                
                ! Interface to C function
                interface                        
                        type(c_ptr) function c_return_array_of_structs(a) bind(C,name="return_array_of_structs")
                                import
                                integer(c_int) :: a
                        end function
                end interface

                ! Call C function               
                ret_c_ptr = c_return_array_of_structs(length)
                l = length
        
        end subroutine

        subroutine get_array(l,a,b)

                use iso_c_binding
                
                integer,intent(in)     :: l
                integer,intent(out)    :: a(l)
                real(8),intent(out)    :: b(l)         
                
                type(c_ptr)               :: ret_c_ptr            
                type(t_mystruct),pointer :: f_ptr(:)
                integer                   :: length
                
                ! Interface to C function
                interface                        
                        type(c_ptr) function c_return_array_of_structs(a) bind(C,name="return_array_of_structs")
                                import
                                integer(c_int) :: a
                        end function
                end interface

                ! Call C function               
                ret_c_ptr = c_return_array_of_structs(length)
                call c_f_pointer(ret_c_ptr,f_ptr,[length])
                a = f_ptr%a
                b = f_ptr%b
        
        end subroutine

end module

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。