ctypesref.txt 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. # Ctypes reference
  2. https://docs.python.org/3/library/ctypes.html
  3. https://svn.python.org/projects/ctypes/branches/ctypes-1.1/docs/manual/tutorial.html
  4. https://numpy.org/doc/stable/user/c-info.python-as-glue.html#calling-other-compiled-libraries-from-python
  5. https://doc.sagemath.org/html/en/thematic_tutorials/numerical_sage/ctypes.html
  6. https://wiki.python.org/moin/ctypes
  7. https://www.scaler.com/topics/python-ctypes/
  8. # calling C function from Python
  9. Here are the steps to call a C function using Python Ctypes:
  10. 1. Creating a C file (with a .c extension) with all the necessary functions.
  11. 2. Using the C compiler, create a shared library file (.so extension).
  12. 3. Using the shared file, create an instance of ctypes.CDLL in Python.
  13. 4. The C function should then be called with the format
  14. <CDLL_instance>.<function_name>(<function_parameters>)
  15. # make shared object library
  16. $ gcc -c program.c
  17. $ gcc -shared -o program.so program.o
  18. Note that on osx -shared should be replaced by -dynamiclib and program.so should
  19. be called program.dylib
  20. import ctypes
  21. libc = ctypes.cdll.LoadLibrary("libc.so.6")
  22. libc.rand() # returns a pseudo-random integer
  23. * 32-bit platforms where sizeof(int) == sizeof(long), c_int is an alias for c_long,
  24. * 64-bit platforms where sizeof(long) == sizeof(long long), c_long is an alias
  25. for c_longlong
  26. # fundamental data types
  27. ctypes defines a number of primitive C compatible data types:
  28. ctypes types C type Python type
  29. ---------------------------------------------------------------------------------------
  30. c_char char 1-character string
  31. c_wchar wchar_t 1-character unicode string
  32. c_byte char int/long
  33. c_ubyte unsigned char int/long
  34. c_short short int/long
  35. c_ushort unsigned short int/long
  36. c_int int int/long
  37. c_uint unsigned int int/long
  38. c_long long int/long
  39. c_ulong unsigned long int/long
  40. c_longlong __int64 or long long int/long
  41. c_ulonglong unsigned __int64 or unsigned long long int/long
  42. c_float float float
  43. c_double double float
  44. c_char_p char *(NUL terminated) string or None
  45. c_wchar_p wchar_t *(NUL terminated) unicode or None
  46. c_void_p void * int/long or None
  47. Python C type
  48. ----------------------------------------------------------------
  49. None NULL
  50. ctypes.char_p char*
  51. ctypes.c_int int
  52. ctypes.c_longlong long long
  53. ctypes.c_double double
  54. numpy.ctypeslib.ndpointer(dtype=numpy.int32) int*
  55. numpy.ctypeslib.ndpointer(dtype=numpy.float64) double*
  56. By default ctypes assumes the return values are ints. If they are not you need
  57. to tell it by setting restype to the correct return type.
  58. >>> n = ctypes.c_int(42)
  59. >>> print(n)
  60. c_int(42)
  61. >>> n.value
  62. 42
  63. >>> n.value = -99
  64. >>> n.value
  65. -99
  66. >>> repr(n.value)
  67. '-99'
  68. >>> ctypes.sizeof(n)
  69. 4
  70. >>> ctypes.sizeof(ctypes.c_int)
  71. 4
  72. >>> n = ctypes.c_int(10)
  73. >>> arr = (ctypes.c_int * n.value)()
  74. >>> arr
  75. <__main__.c_int_Array_100 object at 0x7fe1a3861fd0>
  76. >>> ctypes.sizeof(arr)
  77. 40
  78. >>> for i in range(n.value):
  79. arr[i] = i
  80. >>> arr[:]
  81. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  82. >>> arr[3:]
  83. [3, 4, 5, 6, 7, 8, 9]
  84. >>> arr[:7]
  85. [0, 1, 2, 3, 4, 5, 6]
  86. >>> arr[3:7]
  87. [3, 4, 5, 6]
  88. >>> s = ctypes.c_wchar_p()
  89. >>> s.value = "institute"
  90. >>> s.value
  91. 'institute'
  92. >>> s = ctypes.c_wchar_p("institute")
  93. >>> s.value
  94. 'institute'
  95. # pointer
  96. >>> n = ctypes.c_int(42)
  97. >>> n
  98. c_int(42)
  99. >>> n.value
  100. 42
  101. >>> nptr = ctypes.pointer(n)
  102. >>> nptr.contents
  103. c_int(42)
  104. >>> nptr.contents.value
  105. 42
  106. calling the pointer type without an argument creates a NULL pointer.
  107. NULL pointer have a False boolean value:
  108. >>> null_ptr = ctypes.POINTER(ctypes.c_int)()
  109. >>> bool(null_ptr)
  110. False
  111. >>> n = ctypes.c_double(10)
  112. >>> n.value
  113. 10.0
  114. >>> nptr = ctypes.POINTER(ctypes.c_double)(n)
  115. >>> nptr.contents
  116. c_double(10.0)
  117. >>> nptr.contents.value
  118. 10.0
  119. Note:
  120. n = ctypes.c_double(10)
  121. nptr = ctypes.pointer(n) is equivalent to nptr = ctypes.POINTER(ctypes.c_double)(n)
  122. num = 10
  123. arr = (ctypes.c_int * num)()
  124. for n in range(num):
  125. arr[n] = n
  126. # arrays (pure Ctypes)
  127. n = ctypes.c_int(3)
  128. a = (ctypes.c_int * n.value)(1, 2, -5) # default ctypes way of creating arrays
  129. b = (ctypes.c_int * n.value)(-1, 3, 3)
  130. res = (ctypes.c_int * n.value)(0, 0, 0)
  131. math.add_int_array(a, b, res, n.value) # pass by value
  132. # arrays (using Numpy)
  133. import numpy as np
  134. n = ctypes.c_int(3)
  135. a = np.array([1, 2, -5], dtype=ctypes.c_int)
  136. b = np.array([-1, 3, 3], dtype=ctypes.c_int)
  137. res = np.zeros(n.value, dtypes=ctypes.c_int)
  138. intp = ctypes.POINTER(ctypes.c_int) # declare the pointer to int type as an object
  139. i = a.ctypes.data_as(intp)
  140. j = b.ctypes.data_as(intp)
  141. k = res.ctypes.data_as(intp)
  142. math.add_int_array(i, j, k, n.value) # pass by reference
  143. # numpy.ctypeslib.ndpointer
  144. An ndpointer instance is used to describe an ndarray in restype and argtypes
  145. specifications. This approach is more flexible than using, for example,
  146. POINTER(c_double), since several restrictions can be specified, which are
  147. verified upon calling the ctypes function. These include data type, number of
  148. dimensions, shape and flags. If a given array does not satisfy the specified
  149. restrictions, a TypeError is raised.
  150. numpy.ctypeslib.ndpointer(dtype=None, ndim=None, shape=None, flags=None)
  151. Parameters: dtype: data-type, optional
  152. Array data-type.
  153. ndim: int, optional
  154. Number of array dimensions.
  155. shape: tuple of ints, optional
  156. Array shape.
  157. flags: str or tuple of str
  158. Array flags; may be one or more of:
  159. * C_CONTIGUOUS/C/CONTIGUOUS
  160. * F_CONTIGUOUS/F/FORTRAN
  161. * OWNDATA/O
  162. * WRITEABLE/W
  163. * ALIGNED/A
  164. * WRITEBACKIFCOPY/X
  165. Returns: klass: ndpointer type object
  166. A type object, which is an _ndtpr instance containing dtype,
  167. ndim, shape and flags information.
  168. Raises: TypeError
  169. If a given array does not satisfy the specified restrictions.
  170. Reference:
  171. https://numpy.org/doc/stable/reference/routines.ctypeslib.html
  172. https://pydoc.dev/numpy/latest/numpy.ctypeslib.html
  173. # data from ctypes array into numpy array
  174. >>> n = 8
  175. >>> arr = (ctypes.c_double * n)(0, 1, 2, 3, 4, 5, 6, 7)
  176. >>> arr
  177. <c_double_Array_8 object at 0x7f4dccd39d50>
  178. >>> type(arr)
  179. <class 'c_double_Array_8'>
  180. >>> arr[0]
  181. 0.0
  182. >>> arr[7]
  183. 7.0
  184. >>> arr = np.ctypeslib.as_array(arr)
  185. >>> arr
  186. array([0., 1., 2., 3., 4., 5., 6., 7.])
  187. >>> arr.shape
  188. (8,)
  189. >>> arr.size
  190. 8
  191. # data from ctypes pointer into numpy array
  192. >>> n = 8
  193. >>> arr = (ctypes.c_double * n)(0, 1, 2, 3, 4, 5, 6, 7)
  194. >>> ptr = ctypes.POINTER(ctypes.c_double)(arr)
  195. >>> ptr
  196. <__main__.LP_c_double object at 0x7f4dccd39d50>
  197. >>> type(ptr)
  198. <class '__main__.LP_c_double'>
  199. >>> ptr[0]
  200. 0.0
  201. >>> ptr[7]
  202. 7.0
  203. >>> arr = np.ctypeslib.as_array(ptr, shape=(n,))
  204. >>> arr
  205. array([0., 1., 2., 3., 4., 5., 6., 7.])
  206. >>> arr.shape
  207. (8,)
  208. >>> arr.size
  209. 8
  210. # data from numpy array to ctypes array
  211. >>> n = 8
  212. >>> arr = np.arange(n, dtype=np.float64)
  213. >>> arr
  214. array([0., 1., 2., 3., 4., 5., 6., 7.])
  215. >>> type(arr)
  216. <class 'numpy.ndarray'>
  217. >>> arr = np.ctypeslib.as_ctypes(arr)
  218. >>> arr
  219. <c_double_Array_8 object at 0x7f4dc1fe90d0>
  220. >>> type(arr)
  221. <class 'c_double_Array_8'>
  222. >>> arr[0]
  223. 0.0
  224. >>> arr[7]
  225. 7.0
  226. # data from numpy array to ctypes pointer
  227. >>> n = 8
  228. >>> arr = np.arange(n, dtype=np.float64)
  229. >>> arr
  230. array([0., 1., 2., 3., 4., 5., 6., 7.])
  231. >>> type(arr)
  232. <class 'numpy.ndarray'>
  233. >>> ptr = arr.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
  234. >>> ptr
  235. <__main__.LP_c_double object at 0x7f4dc1fe9750>
  236. >>> type(ptr)
  237. <class '__main__.LP_c_double'>
  238. >>> ptr[0]
  239. 0.0
  240. >>> ptr[7]
  241. 7.0