I have a Python driver calling a Fortran subroutine. The subroutine has 1 character, then 3 integers. Before I added the character variable at the start and didn’t have the argtypes call, it ran fine. But adding the character (and its length) plus the artgtypes call at the start gives a TypeError:
import ctypes as ct
lib = ct.CDLL('FTN_lib.dll')
fs = getattr(lib,'PY_OBJFUN_MOD_mp_GETSIZE')
fs.restype = None
fs.argtypes = [ct.c_char_p,ct.c_int,ct.c_int,ct.c_int,ct.c_int]
x = b'x ' + sys.argv[1].encode('UTF-8') + b' debug=900'
n = ct.c_int(0)
nl = ct.c_int(0)
nnl = ct.c_int(0)
fs(x, len(x), ct.byref(n), ct.byref(nl), ct.byref(nnl))
fs(x, len(x), ct.byref(n), ct.byref(nl), ct.byref(nnl))
ctypes.ArgumentError: argument 3: <class ‘TypeError’>: wrong type
It seems the addition of fs.argtypes is causing the problem. The test code didn’t have it, and it worked fine. Adding fs.argtypes causes the test code to fail too. Hence there is some problem using ct.c_int construct which is used as ct_byref in the call.
The Fortran code header is:
subroutine getsize(file,n,nl,nnl)
implicit none
character(*), intent(in ) :: file
integer , intent(out) :: n
integer , intent(out) :: nl
integer , intent(out) :: nnl
Fortran passes the integers by reference instead of by value. argtypes
for the integer parameters should be ct.POINTER(ct.c_int)
.
Note: A comment on another answer indicates the string size should be int32 on 32-bit systems and int64 on64-bit. I’d use c_size_t
. It also says string lengths are added to the end of the parameter list and not interleaved. I don’t have Fortran to test.
Try:
fs.argtypes = ct.c_char_p, ct.POINTER(ct.c_int), ct.POINTER(ct.c_int), ct.POINTER(ct.c_int), ct.c_size_t
x = b'x ' + sys.argv[1].encode('UTF-8') + b' debug=900'
n = ct.c_int(0)
nl = ct.c_int(0)
nnl = ct.c_int(0)
fs(x, ct.byref(n), ct.byref(nl), ct.byref(nnl), len(x))
“The subroutine has 1 character, then 3 integers” – yet you provide an
argtypes
list of 1 character and 4 integers – is the last integer supposed to be the return type? If so, assign it torestype
instead@MathiasR.Jessen no there are 5 types in argtypes (char, its length, and 3 ints). In Fortran a character is sent from Python (or C) as a char and a length. Hence a Fortran character has two argument in Python. There is no restype, it is a subroutine.
Gotcha. Try passing
ct.pointer(n)
instead ofct.byref(n)
@MathiasR.Jessen nope exactly the same problem. I’m using ct.byref is this worked in my test example, created from this thread stackoverflow.com/questions/77989853/…. The syntax fs(ct.byref(n), ct.byref(nl), ct.byref(nnl)) works fine without the character in Fortran / Python. Just getting this error when I add the char.
Can you share the argument declarations from the subroutine?
Show 8 more comments