2013-07-15 21 views
1

我想用ctypes模塊從python程序中調用一個(fortran)線性庫我寫的代數例程。我已成功導入庫,並可以調用我的子程序以及返回單個值的函數。我的問題是調用返回一個雙精度數組的函數。我無法弄清楚如何指定返回類型。因此,每當我調用這樣的函數時,我都會遇到段錯誤。指定一個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 = ...」,但我無法弄清楚w帽子應該真的去那裏。感謝您閱讀這個問題。 --Gus

+0

是否有你沒有使用[f2py](http://cens.ioc.ee/projects/f2py2e/usersguide/)的原因? – SethMMorton

+0

@SethMMorton因爲它無法處理派生數據類型。我的代碼非常「現代」,有很多「對象」,不僅僅是數字和數組。對於派生數據類型來說,這個包在大多數情況下似乎都有效。 https://github.com/jameskermode/f90wrap – glwhart

回答

1

編譯器可能會返回指向數組的指針或數組描述符...因此,在混合語言時,應始終使用bind(C),除非包裝器特別支持Fortran。並且(不奇怪)bind(C)函數不能返回數組。你理論上可以分配這個數組並且返回type(c_ptr)給它,但是如何在使用之後處理它呢?

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

+0

我有很多很多的代碼,我想從python調用。重寫所有這些以避免派生數據類型並始終處於子例程中對我來說不是一個非常實用的解決方案。然而,這個新的包裝似乎在很多(但還沒有完成)的實例中工作。 https://github.com/jameskermode/f90wrap – glwhart

+0

你甚至可以發佈,作爲答案添加一些關於它的評論。直到今天,我都不知道這個軟件包。 –

0

隨着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 **