1

So after reading the answers answer 1 and answer 2, I was curious about always having to set argtypes on a function. The answers state that you should always specify the argtypes, i.e:

my_func.argtypes = [ctypes.c_int]
my_func(1)

As otherwise, things may go wrong if you pass the wrong sized variable.

But lets suppose i always do instead:

my_func(ctypes.c_int(1))

Is this well defined? Does setting argtypes do anything more than what is achieved by always specifying the ctypes of arguments when you call the function (other than providing for less typing)?

Avishka Dambawinna
  • 1,180
  • 1
  • 13
  • 29
Rob
  • 574
  • 1
  • 4
  • 17

1 Answers1

2

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
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • To clarify, are you saying that yes, it is well-defined, but it's just not a great idea? – Joseph Sible-Reinstate Monica May 11 '20 at 21:10
  • 1
    @JosephSible-ReinstateMonica Yes. More goes wrong not setting it, and most problems posted under `[ctypes]` tag are due to this. – Mark Tolonen May 11 '20 at 21:12
  • I should of been clearer in my question. I'm writing a library that does all the wrapping for the user and makes sure everything passed in ctypes is an object with a from_param method. But its good to see that argtypes and explicitly passing the ctype do the same thing. – Rob May 12 '20 at 09:22
  • @MarkTolonen What do I do for argtypes if the C function I'm calling does not receive any input parameters? Should I write argtypes=[ ] ? If I don't return anything, should I write restype=ctypes.c_void_p ? – VMMF Jul 29 '22 at 19:08
  • @VMMF `.argtypes=()` – Mark Tolonen Jul 30 '22 at 21:25
  • @MarkTolonen argtypes = None worked too – VMMF Jul 30 '22 at 21:37