Issue #13709: some fixes to the ctypes documentation. In addition to fixing
the problems pointed in the issue, I removed the confusing Windows/Linux distinction. It serves no real goal in the documentation, and is probably wrong anyway since for Windows the WINFUNCTYPE constructor should be used. In addition, the "look, this is faster on Linux" comment is misleading since it's not explained. The outcome may just be an artifact of qsort implementation for this particular input, and may change between C runtime version releases.
This commit is contained in:
parent
3405001491
commit
2a1e74a075
|
@ -926,21 +926,21 @@ Callback functions
|
||||||
:mod:`ctypes` allows to create C callable function pointers from Python callables.
|
:mod:`ctypes` allows to create C callable function pointers from Python callables.
|
||||||
These are sometimes called *callback functions*.
|
These are sometimes called *callback functions*.
|
||||||
|
|
||||||
First, you must create a class for the callback function, the class knows the
|
First, you must create a class for the callback function. The class knows the
|
||||||
calling convention, the return type, and the number and types of arguments this
|
calling convention, the return type, and the number and types of arguments this
|
||||||
function will receive.
|
function will receive.
|
||||||
|
|
||||||
The CFUNCTYPE factory function creates types for callback functions using the
|
The :func:`CFUNCTYPE` factory function creates types for callback functions
|
||||||
normal cdecl calling convention, and, on Windows, the WINFUNCTYPE factory
|
using the ``cdecl`` calling convention. On Windows, the :func:`WINFUNCTYPE`
|
||||||
function creates types for callback functions using the stdcall calling
|
factory function creates types for callback functions using the ``stdcall``
|
||||||
convention.
|
calling convention.
|
||||||
|
|
||||||
Both of these factory functions are called with the result type as first
|
Both of these factory functions are called with the result type as first
|
||||||
argument, and the callback functions expected argument types as the remaining
|
argument, and the callback functions expected argument types as the remaining
|
||||||
arguments.
|
arguments.
|
||||||
|
|
||||||
I will present an example here which uses the standard C library's
|
I will present an example here which uses the standard C library's
|
||||||
:c:func:`qsort` function, this is used to sort items with the help of a callback
|
:c:func:`qsort` function, that is used to sort items with the help of a callback
|
||||||
function. :c:func:`qsort` will be used to sort an array of integers::
|
function. :c:func:`qsort` will be used to sort an array of integers::
|
||||||
|
|
||||||
>>> IntArray5 = c_int * 5
|
>>> IntArray5 = c_int * 5
|
||||||
|
@ -953,7 +953,7 @@ function. :c:func:`qsort` will be used to sort an array of integers::
|
||||||
items in the data array, the size of one item, and a pointer to the comparison
|
items in the data array, the size of one item, and a pointer to the comparison
|
||||||
function, the callback. The callback will then be called with two pointers to
|
function, the callback. The callback will then be called with two pointers to
|
||||||
items, and it must return a negative integer if the first item is smaller than
|
items, and it must return a negative integer if the first item is smaller than
|
||||||
the second, a zero if they are equal, and a positive integer else.
|
the second, a zero if they are equal, and a positive integer otherwise.
|
||||||
|
|
||||||
So our callback function receives pointers to integers, and must return an
|
So our callback function receives pointers to integers, and must return an
|
||||||
integer. First we create the ``type`` for the callback function::
|
integer. First we create the ``type`` for the callback function::
|
||||||
|
@ -961,36 +961,8 @@ integer. First we create the ``type`` for the callback function::
|
||||||
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
|
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
|
||||||
>>>
|
>>>
|
||||||
|
|
||||||
For the first implementation of the callback function, we simply print the
|
To get started, here is a simple callback that shows the values it gets
|
||||||
arguments we get, and return 0 (incremental development ;-)::
|
passed::
|
||||||
|
|
||||||
>>> def py_cmp_func(a, b):
|
|
||||||
... print("py_cmp_func", a, b)
|
|
||||||
... return 0
|
|
||||||
...
|
|
||||||
>>>
|
|
||||||
|
|
||||||
Create the C callable callback::
|
|
||||||
|
|
||||||
>>> cmp_func = CMPFUNC(py_cmp_func)
|
|
||||||
>>>
|
|
||||||
|
|
||||||
And we're ready to go::
|
|
||||||
|
|
||||||
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) # doctest: +WINDOWS
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
|
|
||||||
>>>
|
|
||||||
|
|
||||||
We know how to access the contents of a pointer, so lets redefine our callback::
|
|
||||||
|
|
||||||
>>> def py_cmp_func(a, b):
|
>>> def py_cmp_func(a, b):
|
||||||
... print("py_cmp_func", a[0], b[0])
|
... print("py_cmp_func", a[0], b[0])
|
||||||
|
@ -999,23 +971,7 @@ We know how to access the contents of a pointer, so lets redefine our callback::
|
||||||
>>> cmp_func = CMPFUNC(py_cmp_func)
|
>>> cmp_func = CMPFUNC(py_cmp_func)
|
||||||
>>>
|
>>>
|
||||||
|
|
||||||
Here is what we get on Windows::
|
The result::
|
||||||
|
|
||||||
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) # doctest: +WINDOWS
|
|
||||||
py_cmp_func 7 1
|
|
||||||
py_cmp_func 33 1
|
|
||||||
py_cmp_func 99 1
|
|
||||||
py_cmp_func 5 1
|
|
||||||
py_cmp_func 7 5
|
|
||||||
py_cmp_func 33 5
|
|
||||||
py_cmp_func 99 5
|
|
||||||
py_cmp_func 7 99
|
|
||||||
py_cmp_func 33 99
|
|
||||||
py_cmp_func 7 33
|
|
||||||
>>>
|
|
||||||
|
|
||||||
It is funny to see that on linux the sort function seems to work much more
|
|
||||||
efficiently, it is doing less comparisons::
|
|
||||||
|
|
||||||
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) # doctest: +LINUX
|
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) # doctest: +LINUX
|
||||||
py_cmp_func 5 1
|
py_cmp_func 5 1
|
||||||
|
@ -1025,32 +981,13 @@ efficiently, it is doing less comparisons::
|
||||||
py_cmp_func 1 7
|
py_cmp_func 1 7
|
||||||
>>>
|
>>>
|
||||||
|
|
||||||
Ah, we're nearly done! The last step is to actually compare the two items and
|
Now we can actually compare the two items and return a useful result::
|
||||||
return a useful result::
|
|
||||||
|
|
||||||
>>> def py_cmp_func(a, b):
|
>>> def py_cmp_func(a, b):
|
||||||
... print("py_cmp_func", a[0], b[0])
|
... print("py_cmp_func", a[0], b[0])
|
||||||
... return a[0] - b[0]
|
... return a[0] - b[0]
|
||||||
...
|
...
|
||||||
>>>
|
>>>
|
||||||
|
|
||||||
Final run on Windows::
|
|
||||||
|
|
||||||
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) # doctest: +WINDOWS
|
|
||||||
py_cmp_func 33 7
|
|
||||||
py_cmp_func 99 33
|
|
||||||
py_cmp_func 5 99
|
|
||||||
py_cmp_func 1 99
|
|
||||||
py_cmp_func 33 7
|
|
||||||
py_cmp_func 1 33
|
|
||||||
py_cmp_func 5 33
|
|
||||||
py_cmp_func 5 7
|
|
||||||
py_cmp_func 1 7
|
|
||||||
py_cmp_func 5 1
|
|
||||||
>>>
|
|
||||||
|
|
||||||
and on Linux::
|
|
||||||
|
|
||||||
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) # doctest: +LINUX
|
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) # doctest: +LINUX
|
||||||
py_cmp_func 5 1
|
py_cmp_func 5 1
|
||||||
py_cmp_func 33 99
|
py_cmp_func 33 99
|
||||||
|
@ -1059,9 +996,6 @@ and on Linux::
|
||||||
py_cmp_func 5 7
|
py_cmp_func 5 7
|
||||||
>>>
|
>>>
|
||||||
|
|
||||||
It is quite interesting to see that the Windows :func:`qsort` function needs
|
|
||||||
more comparisons than the linux version!
|
|
||||||
|
|
||||||
As we can easily check, our array is sorted now::
|
As we can easily check, our array is sorted now::
|
||||||
|
|
||||||
>>> for i in ia: print(i, end=" ")
|
>>> for i in ia: print(i, end=" ")
|
||||||
|
@ -1071,9 +1005,9 @@ As we can easily check, our array is sorted now::
|
||||||
|
|
||||||
**Important note for callback functions:**
|
**Important note for callback functions:**
|
||||||
|
|
||||||
Make sure you keep references to CFUNCTYPE objects as long as they are used from
|
Make sure you keep references to :func:`CFUNCTYPE` objects as long as they are
|
||||||
C code. :mod:`ctypes` doesn't, and if you don't, they may be garbage collected,
|
used from C code. :mod:`ctypes` doesn't, and if you don't, they may be garbage
|
||||||
crashing your program when a callback is made.
|
collected, crashing your program when a callback is made.
|
||||||
|
|
||||||
|
|
||||||
.. _ctypes-accessing-values-exported-from-dlls:
|
.. _ctypes-accessing-values-exported-from-dlls:
|
||||||
|
|
Loading…
Reference in New Issue