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
d055de85
Commit
d055de85
authored
Jun 16, 2018
by
gabrieldemarmiesse
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Used intptr_t instead of int to avoid warnings (and crashes in C++). Removed old examples files.
parent
3d291a58
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
83 additions
and
203 deletions
+83
-203
docs/examples/tutorial/clibraries/queue3.pyx
docs/examples/tutorial/clibraries/queue3.pyx
+62
-0
docs/src/tutorial/clibraries.rst
docs/src/tutorial/clibraries.rst
+21
-90
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 @
d055de85
# 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
)
docs/src/tutorial/clibraries.rst
View file @
d055de85
...
@@ -318,28 +318,27 @@ to give them a straight C interface.
...
@@ -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
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 can avoid
which usually fit into the size of a pointer type, we will use ``intptr_t``
additional memory allocations through a trick: we cast our ``int`` values
as it is garanteed to be at least as big as an ``int`` and the same size
to ``void*`` and vice versa, and store the value directly as the
as a ``void*``
pointer value.
Here is a simple implementation for the ``append()`` method::
Here is a simple implementation for the ``append()`` method::
cdef append(self, int value):
cdef append(self, int
ptr_t
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, int value):
cdef append(self, int
ptr_t
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, int* values, size_t count):
cdef extend(self, int
ptr_t
* values, size_t count):
"""Append all ints to the queue.
"""Append all ints to the queue.
"""
"""
cdef size_t i
cdef size_t i
...
@@ -355,11 +354,11 @@ So far, we can only add data to the queue. The next step is to write
...
@@ -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()``,
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::
cdef int peek(self):
cdef int
ptr_t
peek(self):
return <int>cqueue.queue_peek_head(self._c_queue)
return <int
ptr_t
>cqueue.queue_peek_head(self._c_queue)
cdef int pop(self):
cdef int
ptr_t
pop(self):
return <int>cqueue.queue_pop_head(self._c_queue)
return <int
ptr_t
>cqueue.queue_pop_head(self._c_queue)
Handling errors
Handling errors
...
@@ -375,8 +374,8 @@ first case to raise an exception, whereas the second case should
...
@@ -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
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
ptr_t
peek(self) except? -1:
value = <int>cqueue.queue_peek_head(self._c_queue)
value = <int
ptr_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
...
@@ -425,10 +424,10 @@ also needs adaptation. Since it removes a value from the queue,
...
@@ -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
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 int pop(self) except? -1:
cdef int
ptr_t
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 <int
ptr_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 +466,9 @@ methods even when they are called from Cython. This adds a tiny overhead
...
@@ -467,77 +466,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`:
...
@@ -545,7 +476,7 @@ 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
.. 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
``
int
``
values
is
about
five
using
this
Queue
from
Cython
code
with
C
``
int
ptr_t
``
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
...
@@ -574,12 +505,12 @@ predicate. The API could look as follows::
...
@@ -574,12 +505,12 @@ predicate. The API could look as follows::
*
0
for
reject
*
0
for
reject
*
1
for
accept
*
1
for
accept
*/
*/
typedef
int
(*
predicate_func
)(
void
*
user_context
,
QueueValue
data
);
typedef
int
ptr_t
(*
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
.
*/
*/
int
queue_pop_head_until
(
Queue
*
queue
,
predicate_func
predicate
,
int
ptr_t
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
*`
...
@@ -590,13 +521,13 @@ predicate function.
...
@@ -590,13 +521,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
int
evaluate_predicate
(
void
*
context
,
cqueue
.
QueueValue
value
):
cdef
int
ptr_t
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
(<
int
>
value
))
return
bool
(
func
(<
int
ptr_t
>
value
))
except
:
except
:
#
catch
any
Python
errors
and
return
error
indicator
#
catch
any
Python
errors
and
return
error
indicator
return
-
1
return
-
1
...
...
docs/src/tutorial/queue_example/cqueue.pxd
deleted
100644 → 0
View file @
3d291a58
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 @
3d291a58
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