Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
cython
Commits
88034bdc
Commit
88034bdc
authored
Jun 20, 2018
by
scoder
Committed by
GitHub
Jun 20, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2362 from gabrieldemarmiesse/enhance_wrapping_c_libraries
Enhance "wrapping C libraries".
parents
5c04c1a8
50a8405a
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
71 additions
and
188 deletions
+71
-188
docs/examples/tutorial/clibraries/queue3.pyx
docs/examples/tutorial/clibraries/queue3.pyx
+62
-0
docs/src/tutorial/clibraries.rst
docs/src/tutorial/clibraries.rst
+9
-75
docs/src/tutorial/queue_example/cqueue.pxd
docs/src/tutorial/queue_example/cqueue.pxd
+0
-17
docs/src/tutorial/queue_example/queue.pyx
docs/src/tutorial/queue_example/queue.pyx
+0
-96
No files found.
docs/examples/tutorial/clibraries/queue3.pyx
0 → 100644
View file @
88034bdc
# 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
)
docs/src/tutorial/clibraries.rst
View file @
88034bdc
...
...
@@ -353,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 int peek(self):
return <
in
t>cqueue.queue_peek_head(self._c_queue)
return <
Py_ssize_
t>cqueue.queue_peek_head(self._c_queue)
cdef int pop(self):
return <
in
t>cqueue.queue_pop_head(self._c_queue)
return <
Py_ssize_
t>cqueue.queue_pop_head(self._c_queue)
Handling errors
...
...
@@ -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::
cdef int peek(self) except? -1:
value = <in
t>cqueue.queue_peek_head(self._c_queue)
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
...
...
@@ -428,7 +430,7 @@ removal. Instead, we must test it on entry::
cdef int pop(self) except? -1:
if cqueue.queue_is_empty(self._c_queue):
raise IndexError("Queue is empty")
return <
in
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()``.
...
...
@@ -467,77 +469,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`:
...
...
docs/src/tutorial/queue_example/cqueue.pxd
deleted
100644 → 0
View file @
5c04c1a8
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
)
docs/src/tutorial/queue_example/queue.pyx
deleted
100644 → 0
View file @
5c04c1a8
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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment