Commit 88034bdc authored by scoder's avatar scoder Committed by GitHub

Merge pull request #2362 from gabrieldemarmiesse/enhance_wrapping_c_libraries

Enhance "wrapping C libraries".
parents 5c04c1a8 50a8405a
# queue.pyx
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()
# 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 rename
# the C-ish `extend()` method to e.g. `extend_ints()`, and write
# a new `extend()` method instead that accepts an arbitrary Python iterable.
cpdef extend(self, values):
for value in values:
self.append(value)
cdef extend_ints(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 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
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 <Py_ssize_t> cqueue.queue_pop_head(self._c_queue)
def __bool__(self):
return not cqueue.queue_is_empty(self._c_queue)
...@@ -353,13 +353,15 @@ example. ...@@ -353,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 int peek(self): cdef int peek(self):
return <int>cqueue.queue_peek_head(self._c_queue) return <Py_ssize_t>cqueue.queue_peek_head(self._c_queue)
cdef int pop(self): cdef int pop(self):
return <int>cqueue.queue_pop_head(self._c_queue) return <Py_ssize_t>cqueue.queue_pop_head(self._c_queue)
Handling errors Handling errors
...@@ -376,7 +378,7 @@ simply return ``0``. To deal with this, we need to special case this ...@@ -376,7 +378,7 @@ 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 int peek(self) except? -1: cdef int peek(self) except? -1:
value = <int>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
...@@ -428,7 +430,7 @@ removal. Instead, we must test it on entry:: ...@@ -428,7 +430,7 @@ removal. Instead, we must test it on entry::
cdef int 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 <int>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()``.
...@@ -467,77 +469,9 @@ methods even when they are called from Cython. This adds a tiny overhead ...@@ -467,77 +469,9 @@ methods even when they are called from Cython. This adds a tiny overhead
compared to ``cdef`` methods. compared to ``cdef`` methods.
The following listing shows the complete implementation that uses The following listing shows the complete implementation that uses
``cpdef`` methods where possible:: ``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)
.. literalinclude:: ../../examples/tutorial/clibraries/queue3.pyx
Now we can test our Queue implementation using a python script, Now we can test our Queue implementation using a python script,
for example here :file:`test_queue.py`: for example here :file:`test_queue.py`:
......
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