Commit d1595a99 authored by gabrieldemarmiesse's avatar gabrieldemarmiesse

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

parent d055de85
# queue.pyx
from libc.stdint cimport intptr_t
cimport cqueue
cdef class Queue:
......@@ -23,12 +22,12 @@ cdef class Queue:
if self._c_queue is not NULL:
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,
<void*> value):
raise MemoryError()
cdef extend(self, intptr_t* values, size_t count):
cdef extend(self, int* values, size_t count):
cdef size_t i
for i in range(count):
if not cqueue.queue_push_tail(
......@@ -43,8 +42,8 @@ cdef class Queue:
for value in values:
self.append(value)
cpdef intptr_t peek(self) except? -1:
cdef intptr_t value = <intptr_t> cqueue.queue_peek_head(self._c_queue)
cpdef int peek(self) except? -1:
cdef int value = <Py_ssize_t> cqueue.queue_peek_head(self._c_queue)
if value == 0:
# this may mean that the queue is empty,
......@@ -53,10 +52,10 @@ cdef class Queue:
raise IndexError("Queue is empty")
return value
cpdef intptr_t pop(self) except? -1:
cpdef int pop(self) except? -1:
if cqueue.queue_is_empty(self._c_queue):
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):
return not cqueue.queue_is_empty(self._c_queue)
......@@ -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
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``
as it is garanteed to be at least as big as an ``int`` and the same size
as a ``void*``
which usually fit into the size of a pointer type, we can avoid
additional memory allocations through a trick: we cast our ``int`` values
to ``void*`` and vice versa, and store the value directly as the
pointer value.
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)
Again, the same error handling considerations as for the
``__cinit__()`` method apply, so that we end up with this
implementation instead::
cdef append(self, intptr_t value):
cdef append(self, int value):
if not cqueue.queue_push_tail(self._c_queue,
<void*>value):
raise MemoryError()
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.
"""
cdef size_t i
......@@ -352,13 +353,15 @@ example.
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()``,
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):
return <intptr_t>cqueue.queue_peek_head(self._c_queue)
cdef int peek(self):
return <Py_ssize_t>cqueue.queue_peek_head(self._c_queue)
cdef intptr_t pop(self):
return <intptr_t>cqueue.queue_pop_head(self._c_queue)
cdef int pop(self):
return <Py_ssize_t>cqueue.queue_pop_head(self._c_queue)
Handling errors
......@@ -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
value, and check if the queue really is empty or not::
cdef intptr_t peek(self) except? -1:
value = <intptr_t>cqueue.queue_peek_head(self._c_queue)
cdef int peek(self) except? -1:
cdef int value = <Py_ssize_t>cqueue.queue_peek_head(self._c_queue)
if value == 0:
# this may mean that the queue is empty, or
# that it happens to contain a 0 value
......@@ -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
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):
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
``peek()``.
......@@ -476,7 +479,7 @@ for example here :file:`test_queue.py`:
.. literalinclude:: ../../examples/tutorial/clibraries/test_queue.py
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,
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
......@@ -505,12 +508,12 @@ predicate. The API could look as follows::
* 0 for reject
* 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,
* 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);
It is normal for C callback functions to have a generic :c:type:`void*`
......@@ -521,13 +524,13 @@ predicate function.
First, we have to define a callback function with the expected
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"
try:
# recover Python function object from void* argument
func = <object>context
# call function, convert result into 0/1 for True/False
return bool(func(<intptr_t>value))
return bool(func(<int>value))
except:
# catch any Python errors and return error indicator
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