Commit d055de85 authored by gabrieldemarmiesse's avatar gabrieldemarmiesse

Used intptr_t instead of int to avoid warnings (and crashes in C++). Removed old examples files.

parent 3d291a58
# queue.pyx
from libc.stdint cimport intptr_t
cimport cqueue
cdef class Queue:
"""A queue class for C integer values.
>>> q = Queue()
>>> q.append(5)
>>> q.peek()
5
>>> q.pop()
5
"""
cdef cqueue.Queue* _c_queue
def __cinit__(self):
self._c_queue = cqueue.queue_new()
if self._c_queue is NULL:
raise MemoryError()
def __dealloc__(self):
if self._c_queue is not NULL:
cqueue.queue_free(self._c_queue)
cpdef append(self, intptr_t value):
if not cqueue.queue_push_tail(self._c_queue,
<void*> value):
raise MemoryError()
cdef extend(self, intptr_t* values, size_t count):
cdef size_t i
for i in range(count):
if not cqueue.queue_push_tail(
self._c_queue, <void*> values[i]):
raise MemoryError()
# The `cpdef` feature is obviously not available for the `extend()`
# method, as the method signature is incompatible with Python argument
# types (Python doesn't have pointers). However, we can make a method
# called `extend_python` instead that accepts an arbitrary Python iterable.
cpdef extend_python(self, values):
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)
if value == 0:
# this may mean that the queue is empty,
# or that it happens to contain a 0 value
if cqueue.queue_is_empty(self._c_queue):
raise IndexError("Queue is empty")
return value
cpdef intptr_t 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)
def __bool__(self):
return not cqueue.queue_is_empty(self._c_queue)
......@@ -318,28 +318,27 @@ 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 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.
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*``
Here is a simple implementation for the ``append()`` method::
cdef append(self, int value):
cdef append(self, intptr_t 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, int value):
cdef append(self, intptr_t 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, int* values, size_t count):
cdef extend(self, intptr_t* values, size_t count):
"""Append all ints to the queue.
"""
cdef size_t i
......@@ -355,11 +354,11 @@ 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::
cdef int peek(self):
return <int>cqueue.queue_peek_head(self._c_queue)
cdef intptr_t peek(self):
return <intptr_t>cqueue.queue_peek_head(self._c_queue)
cdef int pop(self):
return <int>cqueue.queue_pop_head(self._c_queue)
cdef intptr_t pop(self):
return <intptr_t>cqueue.queue_pop_head(self._c_queue)
Handling errors
......@@ -375,8 +374,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 int peek(self) except? -1:
value = <int>cqueue.queue_peek_head(self._c_queue)
cdef intptr_t peek(self) except? -1:
value = <intptr_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
......@@ -425,10 +424,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 int pop(self) except? -1:
cdef intptr_t pop(self) except? -1:
if cqueue.queue_is_empty(self._c_queue):
raise IndexError("Queue is empty")
return <int>cqueue.queue_pop_head(self._c_queue)
return <intptr_t>cqueue.queue_pop_head(self._c_queue)
The return value for exception propagation is declared exactly as for
``peek()``.
......@@ -467,77 +466,9 @@ methods even when they are called from Cython. This adds a tiny overhead
compared to ``cdef`` methods.
The following listing shows the complete implementation that uses
``cpdef`` methods where possible::
cimport cqueue
cdef class Queue:
"""A queue class for C integer values.
>>> q = Queue()
>>> q.append(5)
>>> q.peek()
5
>>> q.pop()
5
"""
cdef cqueue.Queue* _c_queue
def __cinit__(self):
self._c_queue = cqueue.queue_new()
if self._c_queue is NULL:
raise MemoryError()
def __dealloc__(self):
if self._c_queue is not NULL:
cqueue.queue_free(self._c_queue)
cpdef append(self, int value):
if not cqueue.queue_push_tail(self._c_queue,
<void*>value):
raise MemoryError()
cdef extend(self, int* values, size_t count):
cdef size_t i
for i in xrange(count):
if not cqueue.queue_push_tail(
self._c_queue, <void*>values[i]):
raise MemoryError()
cpdef int peek(self) except? -1:
cdef int value = \
<int>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
if cqueue.queue_is_empty(self._c_queue):
raise IndexError("Queue is empty")
return value
cpdef int pop(self) except? -1:
if cqueue.queue_is_empty(self._c_queue):
raise IndexError("Queue is empty")
return <int>cqueue.queue_pop_head(self._c_queue)
def __bool__(self):
return not cqueue.queue_is_empty(self._c_queue)
The ``cpdef`` feature is obviously not available for the ``extend()``
method, as the method signature is incompatible with Python argument
types. However, if wanted, we can rename the C-ish ``extend()``
method to e.g. ``c_extend()``, and write a new ``extend()`` method
instead that accepts an arbitrary Python iterable::
cdef c_extend(self, int* values, size_t count):
cdef size_t i
for i in range(count):
if not cqueue.queue_push_tail(
self._c_queue, <void*>values[i]):
raise MemoryError()
cpdef extend(self, values):
for value in values:
self.append(value)
``cpdef`` methods where possible:
.. literalinclude:: ../../examples/tutorial/clibraries/queue3.pyx
Now we can test our Queue implementation using a python script,
for example here :file:`test_queue.py`:
......@@ -545,7 +476,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 ``int`` values is about five
using this Queue from Cython code with C ``intptr_t`` 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
......@@ -574,12 +505,12 @@ predicate. The API could look as follows::
* 0 for reject
* 1 for accept
*/
typedef int (*predicate_func)(void* user_context, QueueValue data);
typedef intptr_t (*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.
*/
int queue_pop_head_until(Queue *queue, predicate_func predicate,
intptr_t 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*`
......@@ -590,13 +521,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 int evaluate_predicate(void* context, cqueue.QueueValue value):
cdef intptr_t 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(<int>value))
return bool(func(<intptr_t>value))
except:
# catch any Python errors and return error indicator
return -1
......
cdef extern from "libcalg/queue.h":
ctypedef struct Queue:
pass
ctypedef void* QueueValue
Queue* queue_new()
void queue_free(Queue* queue)
int queue_push_head(Queue* queue, QueueValue data)
QueueValue queue_pop_head(Queue* queue)
QueueValue queue_peek_head(Queue* queue)
int queue_push_tail(Queue* queue, QueueValue data)
QueueValue queue_pop_tail(Queue* queue)
QueueValue queue_peek_tail(Queue* queue)
int queue_is_empty(Queue* queue)
cimport cqueue
cdef class Queue:
cdef cqueue.Queue* _c_queue
def __cinit__(self):
self._c_queue = cqueue.queue_new()
if self._c_queue is NULL:
raise MemoryError()
def __dealloc__(self):
if self._c_queue is not NULL:
cqueue.queue_free(self._c_queue)
cpdef int append(self, int value) except -1:
if not cqueue.queue_push_tail(self._c_queue, <void*>value):
raise MemoryError()
return 0
cdef int extend(self, int* values, Py_ssize_t count) except -1:
cdef Py_ssize_t i
for i in range(count):
if not cqueue.queue_push_tail(self._c_queue, <void*>values[i]):
raise MemoryError()
return 0
cpdef int peek(self) except? 0:
cdef int value = <int>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
if cqueue.queue_is_empty(self._c_queue):
raise IndexError("Queue is empty")
return value
cpdef int pop(self) except? 0:
cdef int value = <int>cqueue.queue_pop_head(self._c_queue)
if value == 0:
# this may mean that the queue is empty, or that it
# happens to contain a 0 value
if cqueue.queue_is_empty(self._c_queue):
raise IndexError("Queue is empty")
return value
def __bool__(self): # same as __nonzero__ in Python 2.x
return not cqueue.queue_is_empty(self._c_queue)
DEF repeat_count=10000
def test_cy():
cdef int i
cdef Queue q = Queue()
for i in range(repeat_count):
q.append(i)
for i in range(repeat_count):
q.peek()
while q:
q.pop()
def test_py():
cdef int i
q = Queue()
for i in range(repeat_count):
q.append(i)
for i in range(repeat_count):
q.peek()
while q:
q.pop()
from collections import deque
def test_deque():
cdef int i
q = deque()
for i in range(repeat_count):
q.appendleft(i)
for i in range(repeat_count):
q[-1]
while q:
q.pop()
repeat = range(repeat_count)
def test_py_exec():
q = Queue()
d = dict(q=q, repeat=repeat)
exec u"""\
for i in repeat:
q.append(9)
for i in repeat:
q.peek()
while q:
q.pop()
""" in d
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