如何使用 ISO C BINDINGS 将结构数组转换为派生类型数组?
How do I convert an array of structs into an array of derived type with 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
形状数组 [length]
指向 C 目标。
1 互操作性不是必需的,但如果可以的话,肯定会有所帮助。
这个问题的正确答案是由上面的 francescalus 提供的,但是我包含了完整的代码摘录以及 f2py
到 Python 公开的功能。
f2py
不允许 return 派生类型的 numpy 数组(这将是自定义 dtype 的 numpy 向量),因此您需要 return 每个字段的单独向量.另外 f2py
不能 return 没有显式大小的可分配数组,因此这个函数需要分成两个调用,一个获取长度,另一个获取实际数组。
虽然这些是 f2py
的明显限制,但这允许直接调用 Fortran 函数(或本例中的 C 封装函数)并获取 numpy 数组作为 return 值。
代码没有优化,因为它暗示调用 C 函数两次,也许有可能像 C 中那样使用某种静态变量,所以第二次调用已经计算了值,虽然我没有知道:
- 如果这在 Fortran 中完全可行的话
- 如果这在共享对象的上下文中是可能的(这是
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
我有以下测试 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
形状数组 [length]
指向 C 目标。
1 互操作性不是必需的,但如果可以的话,肯定会有所帮助。
这个问题的正确答案是由上面的 francescalus 提供的,但是我包含了完整的代码摘录以及 f2py
到 Python 公开的功能。
f2py
不允许 return 派生类型的 numpy 数组(这将是自定义 dtype 的 numpy 向量),因此您需要 return 每个字段的单独向量.另外 f2py
不能 return 没有显式大小的可分配数组,因此这个函数需要分成两个调用,一个获取长度,另一个获取实际数组。
虽然这些是 f2py
的明显限制,但这允许直接调用 Fortran 函数(或本例中的 C 封装函数)并获取 numpy 数组作为 return 值。
代码没有优化,因为它暗示调用 C 函数两次,也许有可能像 C 中那样使用某种静态变量,所以第二次调用已经计算了值,虽然我没有知道:
- 如果这在 Fortran 中完全可行的话
- 如果这在共享对象的上下文中是可能的(这是
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