Proposal
Currently, ctypes._CFuncPtr
objects always allows you to call it with more arguments than the length of .argtypes
. I find this behavior strange because it means accidently passing extra arguments can go unnoticed due to the lack of errors. For example:
libc.puts.argtypes = [ctypes.c_char_p]
libc.puts.restype = ctypes.c_int
libc.puts(b"Hello, World!") # works
libc.puts(b"foo", b"bar", b"baz") # still works even though it's wrong
I propose that the behavior be changed so that once .argtypes
has been set, the number of arguments in a call must be equal to the length of .argtypes
unless the last element of .argtypes
is the built-in Ellipsis
object (in which case the number of arguments should be greater than or equal to len(.argtypes) - 1
).
So to declare a variadic function, you must either use Ellipsis
as the last element of .argtypes
, or not set .argtypes
at all. For example:
libc.puts.argtypes = [ctypes.c_char_p] # non-variadic function
libc.puts.restype = ctypes.c_int
libc.puts(b"Hello, World!") # works
libc.puts(b"foo", b"bar", b"baz") # too many arguments, raises an exception
libc.printf.argtypes = [ctypes.c_char_p, ...] # variadic function
libc.printf.restype = ctypes.c_int
libc.printf(b"Hello, World!\n") # works
libc.printf(b"%d %lf %s\n", 25, ctypes.c_double(3.14), b"foo") # also works
I know this would break backwards-compatibility, so if this gets accepted, maybe add it as a __future__
feature for now so that people have to opt-in to it?
Reasons for the Proposal
- This stricter behavior for non-variadic functions can help catch mistakes in code early.
- Ending the parameter list of the function prototype with an ellipsis matches C’s syntax for declaring variadic functions, so it’s very intuitive.