123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- # Ctypes reference
- https://docs.python.org/3/library/ctypes.html
- https://svn.python.org/projects/ctypes/branches/ctypes-1.1/docs/manual/tutorial.html
- https://numpy.org/doc/stable/user/c-info.python-as-glue.html#calling-other-compiled-libraries-from-python
- https://doc.sagemath.org/html/en/thematic_tutorials/numerical_sage/ctypes.html
- https://wiki.python.org/moin/ctypes
- https://www.scaler.com/topics/python-ctypes/
- # calling C function from Python
- Here are the steps to call a C function using Python Ctypes:
- 1. Creating a C file (with a .c extension) with all the necessary functions.
- 2. Using the C compiler, create a shared library file (.so extension).
- 3. Using the shared file, create an instance of ctypes.CDLL in Python.
- 4. The C function should then be called with the format
- <CDLL_instance>.<function_name>(<function_parameters>)
- # make shared object library
- $ gcc -c program.c
- $ gcc -shared -o program.so program.o
- Note that on osx -shared should be replaced by -dynamiclib and program.so should
- be called program.dylib
- import ctypes
- libc = ctypes.cdll.LoadLibrary("libc.so.6")
- libc.rand() # returns a pseudo-random integer
- * 32-bit platforms where sizeof(int) == sizeof(long), c_int is an alias for c_long,
- * 64-bit platforms where sizeof(long) == sizeof(long long), c_long is an alias
- for c_longlong
- # fundamental data types
- ctypes defines a number of primitive C compatible data types:
- ctypes types C type Python type
- ---------------------------------------------------------------------------------------
- c_char char 1-character string
- c_wchar wchar_t 1-character unicode string
- c_byte char int/long
- c_ubyte unsigned char int/long
- c_short short int/long
- c_ushort unsigned short int/long
- c_int int int/long
- c_uint unsigned int int/long
- c_long long int/long
- c_ulong unsigned long int/long
- c_longlong __int64 or long long int/long
- c_ulonglong unsigned __int64 or unsigned long long int/long
- c_float float float
- c_double double float
- c_char_p char *(NUL terminated) string or None
- c_wchar_p wchar_t *(NUL terminated) unicode or None
- c_void_p void * int/long or None
- Python C type
- ----------------------------------------------------------------
- None NULL
- ctypes.char_p char*
- ctypes.c_int int
- ctypes.c_longlong long long
- ctypes.c_double double
- numpy.ctypeslib.ndpointer(dtype=numpy.int32) int*
- numpy.ctypeslib.ndpointer(dtype=numpy.float64) double*
- By default ctypes assumes the return values are ints. If they are not you need
- to tell it by setting restype to the correct return type.
- >>> n = ctypes.c_int(42)
- >>> print(n)
- c_int(42)
- >>> n.value
- 42
- >>> n.value = -99
- >>> n.value
- -99
- >>> repr(n.value)
- '-99'
- >>> ctypes.sizeof(n)
- 4
- >>> ctypes.sizeof(ctypes.c_int)
- 4
- >>> n = ctypes.c_int(10)
- >>> arr = (ctypes.c_int * n.value)()
- >>> arr
- <__main__.c_int_Array_100 object at 0x7fe1a3861fd0>
- >>> ctypes.sizeof(arr)
- 40
- >>> for i in range(n.value):
- arr[i] = i
- >>> arr[:]
- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- >>> arr[3:]
- [3, 4, 5, 6, 7, 8, 9]
- >>> arr[:7]
- [0, 1, 2, 3, 4, 5, 6]
- >>> arr[3:7]
- [3, 4, 5, 6]
- >>> s = ctypes.c_wchar_p()
- >>> s.value = "institute"
- >>> s.value
- 'institute'
- >>> s = ctypes.c_wchar_p("institute")
- >>> s.value
- 'institute'
- # pointer
- >>> n = ctypes.c_int(42)
- >>> n
- c_int(42)
- >>> n.value
- 42
- >>> nptr = ctypes.pointer(n)
- >>> nptr.contents
- c_int(42)
- >>> nptr.contents.value
- 42
- calling the pointer type without an argument creates a NULL pointer.
- NULL pointer have a False boolean value:
- >>> null_ptr = ctypes.POINTER(ctypes.c_int)()
- >>> bool(null_ptr)
- False
- >>> n = ctypes.c_double(10)
- >>> n.value
- 10.0
- >>> nptr = ctypes.POINTER(ctypes.c_double)(n)
- >>> nptr.contents
- c_double(10.0)
- >>> nptr.contents.value
- 10.0
- Note:
- n = ctypes.c_double(10)
- nptr = ctypes.pointer(n) is equivalent to nptr = ctypes.POINTER(ctypes.c_double)(n)
- num = 10
- arr = (ctypes.c_int * num)()
- for n in range(num):
- arr[n] = n
- # arrays (pure Ctypes)
- n = ctypes.c_int(3)
- a = (ctypes.c_int * n.value)(1, 2, -5) # default ctypes way of creating arrays
- b = (ctypes.c_int * n.value)(-1, 3, 3)
- res = (ctypes.c_int * n.value)(0, 0, 0)
- math.add_int_array(a, b, res, n.value) # pass by value
- # arrays (using Numpy)
- import numpy as np
- n = ctypes.c_int(3)
- a = np.array([1, 2, -5], dtype=ctypes.c_int)
- b = np.array([-1, 3, 3], dtype=ctypes.c_int)
- res = np.zeros(n.value, dtypes=ctypes.c_int)
- intp = ctypes.POINTER(ctypes.c_int) # declare the pointer to int type as an object
- i = a.ctypes.data_as(intp)
- j = b.ctypes.data_as(intp)
- k = res.ctypes.data_as(intp)
- math.add_int_array(i, j, k, n.value) # pass by reference
- # numpy.ctypeslib.ndpointer
- An ndpointer instance is used to describe an ndarray in restype and argtypes
- specifications. This approach is more flexible than using, for example,
- POINTER(c_double), since several restrictions can be specified, which are
- verified upon calling the ctypes function. These include data type, number of
- dimensions, shape and flags. If a given array does not satisfy the specified
- restrictions, a TypeError is raised.
- numpy.ctypeslib.ndpointer(dtype=None, ndim=None, shape=None, flags=None)
- Parameters: dtype: data-type, optional
- Array data-type.
- ndim: int, optional
- Number of array dimensions.
- shape: tuple of ints, optional
- Array shape.
- flags: str or tuple of str
- Array flags; may be one or more of:
- * C_CONTIGUOUS/C/CONTIGUOUS
- * F_CONTIGUOUS/F/FORTRAN
- * OWNDATA/O
- * WRITEABLE/W
- * ALIGNED/A
- * WRITEBACKIFCOPY/X
- Returns: klass: ndpointer type object
- A type object, which is an _ndtpr instance containing dtype,
- ndim, shape and flags information.
- Raises: TypeError
- If a given array does not satisfy the specified restrictions.
- Reference:
- https://numpy.org/doc/stable/reference/routines.ctypeslib.html
- https://pydoc.dev/numpy/latest/numpy.ctypeslib.html
- # data from ctypes array into numpy array
- >>> n = 8
- >>> arr = (ctypes.c_double * n)(0, 1, 2, 3, 4, 5, 6, 7)
- >>> arr
- <c_double_Array_8 object at 0x7f4dccd39d50>
- >>> type(arr)
- <class 'c_double_Array_8'>
- >>> arr[0]
- 0.0
- >>> arr[7]
- 7.0
- >>> arr = np.ctypeslib.as_array(arr)
- >>> arr
- array([0., 1., 2., 3., 4., 5., 6., 7.])
- >>> arr.shape
- (8,)
- >>> arr.size
- 8
- # data from ctypes pointer into numpy array
- >>> n = 8
- >>> arr = (ctypes.c_double * n)(0, 1, 2, 3, 4, 5, 6, 7)
- >>> ptr = ctypes.POINTER(ctypes.c_double)(arr)
- >>> ptr
- <__main__.LP_c_double object at 0x7f4dccd39d50>
- >>> type(ptr)
- <class '__main__.LP_c_double'>
- >>> ptr[0]
- 0.0
- >>> ptr[7]
- 7.0
- >>> arr = np.ctypeslib.as_array(ptr, shape=(n,))
- >>> arr
- array([0., 1., 2., 3., 4., 5., 6., 7.])
- >>> arr.shape
- (8,)
- >>> arr.size
- 8
- # data from numpy array to ctypes array
- >>> n = 8
- >>> arr = np.arange(n, dtype=np.float64)
- >>> arr
- array([0., 1., 2., 3., 4., 5., 6., 7.])
- >>> type(arr)
- <class 'numpy.ndarray'>
- >>> arr = np.ctypeslib.as_ctypes(arr)
- >>> arr
- <c_double_Array_8 object at 0x7f4dc1fe90d0>
- >>> type(arr)
- <class 'c_double_Array_8'>
- >>> arr[0]
- 0.0
- >>> arr[7]
- 7.0
- # data from numpy array to ctypes pointer
- >>> n = 8
- >>> arr = np.arange(n, dtype=np.float64)
- >>> arr
- array([0., 1., 2., 3., 4., 5., 6., 7.])
- >>> type(arr)
- <class 'numpy.ndarray'>
- >>> ptr = arr.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
- >>> ptr
- <__main__.LP_c_double object at 0x7f4dc1fe9750>
- >>> type(ptr)
- <class '__main__.LP_c_double'>
- >>> ptr[0]
- 0.0
- >>> ptr[7]
- 7.0
|