It is well-defined, as long as you type it correctly every time. When you specify .argtypes
it does the construction for you, the same way each time, and checks that pointers or manual contructions by the user are of the declared type, and that the number of arguments is at least the number defined.
Really, would you rather type my_func(1)
or my_func(ctypes.c_int(1))
??my_func(ctypes.c_float(1.5))
won't fail if the parameter is supposed to be a ctypes.c_int
, but it will if my_func.argtypes = ctypes.c_int,
.
Consider a C function in module x
that takes an int
and returns that int
:
>>> from ctypes import *
>>> x = CDLL('x')
>>> x.test() # forgot to pass a parameter. Garbage...
1983836396
>>> x.test(1.5) # ctypes knows Python float is ambiguous (C float or double?)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: Don't know how to convert parameter 1
>>> x.test(c_float(1.5)) # But now, garbage...
1069547520
>>> x.test('hello') # garbage...
1197125872
>>> x.test(b'hello') # garbage...
1202177960
>>> x.test(5) # oh, got it right this time.
5
Contrast:
>>> x.test.argtypes = c_int,
>>> x.test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: this function takes at least 1 argument (0 given)
>>> x.test(1.5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
>>> x.test('hello')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
>>> x.test(b'hello')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
>>> x.test(c_float(1.5))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
>>> x.test(5) # MUST pass correctly.
5