问题
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
I compile this with
gcc -shared -fPIC -o libtest.o fstr_test.f90
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))
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:
Passing string to Fortran DLL using ctypes and Python
but it doesn't seem to work for me, maybe because it's talking about Python 2?
For example
f.argtype = [c_char_p,c_int_c_int]
c = c_char_p(b"test.txt")
f(c,byref(ierr),byref(iopen))
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))
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.
回答1:
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
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
and
gfortran -shared -fPIC -o libtest.so fstr_test.f90
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)
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.
来源:https://stackoverflow.com/questions/42328866/passing-python-strings-to-fortran-subroutine-using-ctypes