【问题标题】:Specify the return type for a ctypes call (in python) to a fortran function that returns an array of doubles将 ctypes 调用的返回类型(在 python 中)指定为返回双精度数组的 fortran 函数
【发布时间】:2013-07-15 16:22:39
【问题描述】:

我正在尝试使用 ctypes 模块从 python 程序中调用我编写的线性代数例程 (fortran) 库。我已成功导入库,可以调用我的 子例程 和返回单个值的函数。我的问题是调用返回双精度数组的函数。我不知道如何指定返回类型。结果,每当我调用这样的函数时,我都会遇到段错误。

这是一个最小的工作示例,一个在两个 3 向量之间取叉积的例程:

!****************************************************************************************
! Given vectors a and b, c = a x b
function cross_product(a,b)
real(dp) a(3), b(3), cross_product(3)

cross_product = (/a(2)*b(3) - a(3)*b(2), &
                  a(3)*b(1) - a(1)*b(3), &
                  a(1)*b(2) - a(2)*b(1)/)
end function cross_product

这是我的 python 脚本:

#!/usr/bin/python
from ctypes import byref, cdll, c_double
testlib =  cdll.LoadLibrary('/Users/hart/codes/celib/trunk/libutils.so')
cross = testlib.vector_matrix_utilities_mp_cross_product_

a = (c_double * 3)()
b = (c_double * 3)()
a[0] = c_double(0.0)
a[1] = c_double(1.0)
a[2] = c_double(2.0)
b[0] = c_double(1.0)
b[1] = c_double(3.0)
b[2] = c_double(2.0)

print a,b
cross.restype = c_double * 3
print cross.restype
print cross(byref(a),byref(b))

这是输出:

goku:~/python/ctypes> ./test_example.py 
<__main__.c_double_Array_3 object at 0x10399b710> <__main__.c_double_Array_3 object at 0x10399b7a0>
<class '__main__.c_double_Array_3'>
Segmentation fault: 11
goku:~/python/ctypes>

我已经为“cross.restype = ...”这一行尝试了不同的排列,但我不知道实际应该去那里。感谢您阅读这个问题。 --格斯

【问题讨论】:

  • 你不使用f2py的原因是什么?
  • @SethMMorton 因为它无法处理派生数据类型。我的代码非常“现代”,有很多“对象”,而不仅仅是数字和数组。对于派生数据类型,这个包似乎在大多数情况下都能解决问题。 github.com/jameskermode/f90wrap

标签: python fortran ctypes


【解决方案1】:

编译器可能会返回一个指向数组的指针,或者数组描述符...因此,在混合语言时,您应该始终使用bind(C),除非包装器特别支持 Fortran。而且(毫不奇怪)bind(C) 函数不能返回数组。理论上你可以分配数组并将type(c_ptr)返回给它,但是使用后如何释放呢?

所以我的建议是使用子程序。

【讨论】:

  • 我有很多我想从 python 调用的代码。在我的情况下,重写所有代码以避免派生数据类型并始终位于子例程中并不是一个非常实用的解决方案。然而,这个新的包装器似乎在许多(但不是全部)实例中工作。 github.com/jameskermode/f90wrap
  • 你甚至可以发布它作为答案添加一些关于它的 cmets。直到今天我才知道这个包。
【解决方案2】:

使用 gfortran,函数调用有一个隐藏参数:

>>> from ctypes import *
>>> testlib = CDLL('./libutils.so')
>>> cross = testlib.cross_product_

>>> a = (c_double * 3)(*[0.0, 1.0, 2.0])
>>> b = (c_double * 3)(*[1.0, 3.0, 2.0])
>>> c = (c_double * 3)()
>>> pc = pointer(c)

>>> cross(byref(pc), a, b)
3
>>> c[:]
[-4.0, 2.0, -1.0]

但是Vladimir's suggestion 使用bind(C) 和子例程是更好的方法。

仅供参考,数组成为 C 函数调用中的指针,因此使用 byref 是多余的。我需要byrefpointer 才能为隐藏参数创建double **

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 2019-08-27
    • 2013-06-12
    相关资源
    最近更新 更多