问题描述
我正在尝试使用ctypes将参数传递给共享库中的Fortran子例程。现在,这是我简单的fortran代码:
I'm trying to pass arguments to a Fortran subroutine in a shared library using ctypes. Now here's my simple fortran code:
MODULE test_module
INCLUDES
SUBROUTINE fstr_test(file_or_extension, ierr, iopen)
IMPLICIT NONE
INTEGER, INTENT(out) :: ierr
INTEGER, OPTIONAL :: iopen
CHARACTER(LEN=*), INTENT(in) :: file_or_extension
WRITE(6,*) file_or_extension,ierr,iopen
RETURN
END SUBROUTINE fstr_test
END MODULE test_module
我用
gcc -shared -fPIC -o libtest.o fstr_test.f90
这似乎很好用,实际上我可以在Python 3中加载共享库。非崩溃代码:
This seems to work fine, I can in fact load the shared library in Python 3. Here's non-crashing code:
from ctypes import *
libtest = cdll.LoadLibrary("libtest.so")
f = libtest.test_module_MOD__fstr_test
file = b'wout_li383.nc'
ierr = c_int(0)
iopen = c_int(0)
cfile=c_char_p(b"test_str")
f(cfile,byref(ierr),byref(iopen))
但是,我似乎无法正确传递字符串。在上面的表格中,字符串为空,并且两个整数正确打印到控制台。基本上,我取得的唯一进步就是让Python崩溃了。我在这里尝试了解决方案:
However, I can't seem to pass the string properly. In the form above the string is empty and the two integers print properly to the console. Basically the only progress I'm made is getting Python to crash. I tried the solution here:
但它似乎不适用于我,也许是因为它在说话关于Python 2?
but it doesn't seem to work for me, maybe because it's talking about Python 2?
例如
f.argtype = [c_char_p,c_int_c_int]
c = c_char_p(b"test.txt")
f(c,byref(ierr),byref(iopen))
不会使内核崩溃,但字符串为空。更改为byref:
Doesn't crash the kernel but the string is empty. Changing to byref:
f.argtype = [c_char_p,c_int_c_int]
c = c_char_p(b"test.txt")
f(byref(c),byref(ierr),byref(iopen))
同样的效果。现在,如果我尝试通过添加length参数并将其传递来传递长度,则会收到 Dead Kernel消息,并且必须重新启动内核。本身没有错误消息。
same effect. Now if I try to pass the length by adding the length argument and passing it I get a message of a "Dead Kernel" and I have to restart the kernel. There's no error message per-se.
f.argtype = [c_char_p,c_int,c_int_c_int]
file = b'text.txt'
c = c_char_p(file)
c_len = c_int(len(file))
f(c,byref(c_len),byref(ierr),byref(iopen))
希望这可以澄清。我没有收到错误消息,内核快要死了。
Hope this clarifies. I'm not getting an error message, the kernel is just dying.
推荐答案
所以有几个问题,一个是隐藏长度(至少在gfortran未知的情况下(其他编译器会做些什么)应该放在参数列表的末尾。您也不需要byref,也不需要将字节字符串包装在c_char_p
So a few issues, one the hidden length (at least with gfortran unknown what the other compilers do) should go at the end of the argument list. You don't need the byref on it either and don't wrap the byte-string in c_char_p
在子例程中更改ierr变量
Changing the ierr variable in the subroutine
SUBROUTINE fstr_test(file_or_extension, ierr, iopen)
IMPLICIT NONE
INTEGER, INTENT(out) :: ierr
INTEGER, OPTIONAL :: iopen
CHARACTER(LEN=*), INTENT(in) :: file_or_extension
WRITE(6,*) file_or_extension,ierr,iopen
ierr=1
RETURN
END SUBROUTINE fstr_test
和
gfortran -shared -fPIC -o libtest.so fstr_test.f90
然后python代码应该是这样的:
Then the python code should be this:
import ctypes
lib=ctypes.CDLL("./libtest.so")
f=getattr(lib,'__test_module_MOD_fstr_test')
f.argtypes=[ctypes.c_char_p,ctypes.c_int,ctypes.c_int,ctypes.c_int]
f.restype=None
x=b'abcdef'
ierr=ctypes.c_int(0)
iopen=0
f(x,ctypes.byref(ierr),iopen,len(x))
print(ierr.value)
虽然我还没有让let的可选参数起作用,所以我添加了一个空的int,但是这应该将字符串传递进来并让输出变得错误。
Though i haven't got the optional argument working let, so i've added an empty int, this should yet you pass the string in and get ierr out.
这篇关于使用ctypes将python字符串传递给Fortran子例程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!