Passing python strings to Fortran subroutine using ctypes

南笙酒味 提交于 2019-12-24 04:48:14

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!