Commit d1595a99 authored by gabrieldemarmiesse's avatar gabrieldemarmiesse

Put back int as the main data type for the interface.

parent d055de85
# queue.pyx # queue.pyx
from libc.stdint cimport intptr_t
cimport cqueue cimport cqueue
cdef class Queue: cdef class Queue:
...@@ -23,12 +22,12 @@ cdef class Queue: ...@@ -23,12 +22,12 @@ cdef class Queue:
if self._c_queue is not NULL: if self._c_queue is not NULL:
cqueue.queue_free(self._c_queue) cqueue.queue_free(self._c_queue)
cpdef append(self, intptr_t value): cpdef append(self, int value):
if not cqueue.queue_push_tail(self._c_queue, if not cqueue.queue_push_tail(self._c_queue,
<void*> value): <void*> value):
raise MemoryError() raise MemoryError()
cdef extend(self, intptr_t* values, size_t count): cdef extend(self, int* values, size_t count):
cdef size_t i cdef size_t i
for i in range(count): for i in range(count):
if not cqueue.queue_push_tail( if not cqueue.queue_push_tail(
...@@ -43,8 +42,8 @@ cdef class Queue: ...@@ -43,8 +42,8 @@ cdef class Queue:
for value in values: for value in values:
self.append(value) self.append(value)
cpdef intptr_t peek(self) except? -1: cpdef int peek(self) except? -1:
cdef intptr_t value = <intptr_t> cqueue.queue_peek_head(self._c_queue) cdef int value = <Py_ssize_t> cqueue.queue_peek_head(self._c_queue)
if value == 0: if value == 0:
# this may mean that the queue is empty, # this may mean that the queue is empty,
...@@ -53,10 +52,10 @@ cdef class Queue: ...@@ -53,10 +52,10 @@ cdef class Queue:
raise IndexError("Queue is empty") raise IndexError("Queue is empty")
return value return value
cpdef intptr_t pop(self) except? -1: cpdef int pop(self) except? -1:
if cqueue.queue_is_empty(self._c_queue): if cqueue.queue_is_empty(self._c_queue):
raise IndexError("Queue is empty") raise IndexError("Queue is empty")
return <intptr_t> cqueue.queue_pop_head(self._c_queue) return <Py_ssize_t> cqueue.queue_pop_head(self._c_queue)
def __bool__(self): def __bool__(self):
return not cqueue.queue_is_empty(self._c_queue) return not cqueue.queue_is_empty(self._c_queue)
...@@ -318,27 +318,28 @@ to give them a straight C interface. ...@@ -318,27 +318,28 @@ to give them a straight C interface.
In C, it is common for data structures to store data as a ``void*`` to In C, it is common for data structures to store data as a ``void*`` to
whatever data item type. Since we only want to store ``int`` values, whatever data item type. Since we only want to store ``int`` values,
which usually fit into the size of a pointer type, we will use ``intptr_t`` which usually fit into the size of a pointer type, we can avoid
as it is garanteed to be at least as big as an ``int`` and the same size additional memory allocations through a trick: we cast our ``int`` values
as a ``void*`` to ``void*`` and vice versa, and store the value directly as the
pointer value.
Here is a simple implementation for the ``append()`` method:: Here is a simple implementation for the ``append()`` method::
cdef append(self, intptr_t value): cdef append(self, int value):
cqueue.queue_push_tail(self._c_queue, <void*>value) cqueue.queue_push_tail(self._c_queue, <void*>value)
Again, the same error handling considerations as for the Again, the same error handling considerations as for the
``__cinit__()`` method apply, so that we end up with this ``__cinit__()`` method apply, so that we end up with this
implementation instead:: implementation instead::
cdef append(self, intptr_t value): cdef append(self, int value):
if not cqueue.queue_push_tail(self._c_queue, if not cqueue.queue_push_tail(self._c_queue,
<void*>value): <void*>value):
raise MemoryError() raise MemoryError()
Adding an ``extend()`` method should now be straight forward:: Adding an ``extend()`` method should now be straight forward::
cdef extend(self, intptr_t* values, size_t count): cdef extend(self, int* values, size_t count):
"""Append all ints to the queue. """Append all ints to the queue.
""" """
cdef size_t i cdef size_t i
...@@ -352,13 +353,15 @@ example. ...@@ -352,13 +353,15 @@ example.
So far, we can only add data to the queue. The next step is to write So far, we can only add data to the queue. The next step is to write
the two methods to get the first element: ``peek()`` and ``pop()``, the two methods to get the first element: ``peek()`` and ``pop()``,
which provide read-only and destructive read access respectively:: which provide read-only and destructive read access respectively.
To avoid the compiler warning when casting ``void*`` to ``int`` directly,
we use an intermediate data type big enough to hold a ``void*``. Here ``Py_ssize_t``::
cdef intptr_t peek(self): cdef int peek(self):
return <intptr_t>cqueue.queue_peek_head(self._c_queue) return <Py_ssize_t>cqueue.queue_peek_head(self._c_queue)
cdef intptr_t pop(self): cdef int pop(self):
return <intptr_t>cqueue.queue_pop_head(self._c_queue) return <Py_ssize_t>cqueue.queue_pop_head(self._c_queue)
Handling errors Handling errors
...@@ -374,8 +377,8 @@ first case to raise an exception, whereas the second case should ...@@ -374,8 +377,8 @@ first case to raise an exception, whereas the second case should
simply return ``0``. To deal with this, we need to special case this simply return ``0``. To deal with this, we need to special case this
value, and check if the queue really is empty or not:: value, and check if the queue really is empty or not::
cdef intptr_t peek(self) except? -1: cdef int peek(self) except? -1:
value = <intptr_t>cqueue.queue_peek_head(self._c_queue) cdef int value = <Py_ssize_t>cqueue.queue_peek_head(self._c_queue)
if value == 0: if value == 0:
# this may mean that the queue is empty, or # this may mean that the queue is empty, or
# that it happens to contain a 0 value # that it happens to contain a 0 value
...@@ -424,10 +427,10 @@ also needs adaptation. Since it removes a value from the queue, ...@@ -424,10 +427,10 @@ also needs adaptation. Since it removes a value from the queue,
however, it is not enough to test if the queue is empty *after* the however, it is not enough to test if the queue is empty *after* the
removal. Instead, we must test it on entry:: removal. Instead, we must test it on entry::
cdef intptr_t pop(self) except? -1: cdef int pop(self) except? -1:
if cqueue.queue_is_empty(self._c_queue): if cqueue.queue_is_empty(self._c_queue):
raise IndexError("Queue is empty") raise IndexError("Queue is empty")
return <intptr_t>cqueue.queue_pop_head(self._c_queue) return <Py_ssize_t>cqueue.queue_pop_head(self._c_queue)
The return value for exception propagation is declared exactly as for The return value for exception propagation is declared exactly as for
``peek()``. ``peek()``.
...@@ -476,7 +479,7 @@ for example here :file:`test_queue.py`: ...@@ -476,7 +479,7 @@ for example here :file:`test_queue.py`:
.. literalinclude:: ../../examples/tutorial/clibraries/test_queue.py .. literalinclude:: ../../examples/tutorial/clibraries/test_queue.py
As a quick test with 10000 numbers on the author's machine indicates, As a quick test with 10000 numbers on the author's machine indicates,
using this Queue from Cython code with C ``intptr_t`` values is about five using this Queue from Cython code with C ``int`` values is about five
times as fast as using it from Cython code with Python object values, times as fast as using it from Cython code with Python object values,
almost eight times faster than using it from Python code in a Python almost eight times faster than using it from Python code in a Python
loop, and still more than twice as fast as using Python's highly loop, and still more than twice as fast as using Python's highly
...@@ -505,12 +508,12 @@ predicate. The API could look as follows:: ...@@ -505,12 +508,12 @@ predicate. The API could look as follows::
* 0 for reject * 0 for reject
* 1 for accept * 1 for accept
*/ */
typedef intptr_t (*predicate_func)(void* user_context, QueueValue data); typedef int (*predicate_func)(void* user_context, QueueValue data);
/* Pop values as long as the predicate evaluates to true for them, /* Pop values as long as the predicate evaluates to true for them,
* returns -1 if the predicate failed with an error and 0 otherwise. * returns -1 if the predicate failed with an error and 0 otherwise.
*/ */
intptr_t queue_pop_head_until(Queue *queue, predicate_func predicate, int queue_pop_head_until(Queue *queue, predicate_func predicate,
void* user_context); void* user_context);
It is normal for C callback functions to have a generic :c:type:`void*` It is normal for C callback functions to have a generic :c:type:`void*`
...@@ -521,13 +524,13 @@ predicate function. ...@@ -521,13 +524,13 @@ predicate function.
First, we have to define a callback function with the expected First, we have to define a callback function with the expected
signature that we can pass into the C-API function:: signature that we can pass into the C-API function::
cdef intptr_t evaluate_predicate(void* context, cqueue.QueueValue value): cdef int evaluate_predicate(void* context, cqueue.QueueValue value):
"Callback function that can be passed as predicate_func" "Callback function that can be passed as predicate_func"
try: try:
# recover Python function object from void* argument # recover Python function object from void* argument
func = <object>context func = <object>context
# call function, convert result into 0/1 for True/False # call function, convert result into 0/1 for True/False
return bool(func(<intptr_t>value)) return bool(func(<int>value))
except: except:
# catch any Python errors and return error indicator # catch any Python errors and return error indicator
return -1 return -1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment