├── .gitignore ├── README.md ├── ch1 ├── 1_helloworld │ ├── hello.pyx │ ├── run_me.sh │ ├── setup.py │ └── test.py ├── 2_math │ ├── cmath │ │ ├── cmath.c │ │ └── cmath.h │ ├── pymath │ │ ├── pymath.pyx │ │ ├── setup.py │ │ └── test.py │ └── run_me.sh └── 3_gcc │ ├── cmath.c │ ├── cmath.h │ ├── pymath.pyx │ ├── run_me.sh │ └── test.py ├── ch2 ├── 1_function │ ├── demo.pyx │ ├── run_me.sh │ ├── setup.py │ └── test.py ├── 2_wrap_function │ ├── cqueue │ │ ├── queue.c │ │ └── queue.h │ ├── queue.pyx │ ├── run_me.sh │ ├── setup.py │ └── test.py ├── 3_callback │ ├── c │ │ ├── foo.c │ │ └── foo.h │ ├── py │ │ ├── cfoo.pxd │ │ ├── foo.pyx │ │ ├── setup.py │ │ └── test.py │ └── run_me.sh └── 4_struct │ ├── c │ ├── person.c │ └── person.h │ ├── py │ ├── person.pxd │ ├── person.pyx │ ├── setup.py │ └── test.py │ └── run_me.sh └── ch3 └── 1_pxd ├── cqueue.pxd ├── cqueue ├── queue.c └── queue.h ├── queue.pyx ├── run_me.sh ├── setup.py └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cython_tutorial 2 | 各个章节下都有`run_me.sh`用来编译、运行对应的案例。 3 | 4 | ## ch1,入门 5 | 6 | 1. 一个hello world项目 7 | 2. Python调用C语言动态库 8 | 3. 和2是一样的,但是使用手工gcc的方式进行编译 9 | 10 | ## ch2,封装C函数、结构体 11 | 12 | 1. Cython已经为我们封装好了很多实用的函数,那么如何调用呢? 13 | 2. 假设已经用C实现了一个队列算法,如何编写wrapper实现将C函数封装给Python调用 14 | 15 | ## ch3,代码管理 16 | 17 | 1. 通过pxd文件回避Python符号和C符号同名的问题 18 | -------------------------------------------------------------------------------- /ch1/1_helloworld/hello.pyx: -------------------------------------------------------------------------------- 1 | def say_hello_to(name): 2 | print("Hello %s!" % name) 3 | -------------------------------------------------------------------------------- /ch1/1_helloworld/run_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | 5 | python setup.py build_ext --inplace 6 | 7 | python test.py 8 | -------------------------------------------------------------------------------- /ch1/1_helloworld/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from Cython.Build import cythonize 3 | 4 | setup(name='Hello world app', 5 | ext_modules=cythonize("hello.pyx")) 6 | -------------------------------------------------------------------------------- /ch1/1_helloworld/test.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hello 3 | 4 | hello.say_hello_to('张三') 5 | -------------------------------------------------------------------------------- /ch1/2_math/cmath/cmath.c: -------------------------------------------------------------------------------- 1 | #include "cmath.h" 2 | 3 | int add(int a, int b) 4 | { 5 | return a + b; 6 | } 7 | -------------------------------------------------------------------------------- /ch1/2_math/cmath/cmath.h: -------------------------------------------------------------------------------- 1 | int add(int a, int b); 2 | -------------------------------------------------------------------------------- /ch1/2_math/pymath/pymath.pyx: -------------------------------------------------------------------------------- 1 | cdef extern from "cmath.h": 2 | int add(int a, int b) 3 | 4 | def pyadd(int a, int b): 5 | return add(a, b) 6 | -------------------------------------------------------------------------------- /ch1/2_math/pymath/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | from Cython.Build import cythonize 3 | 4 | extension = Extension( 5 | "pymath", 6 | ["pymath.pyx"], 7 | libraries=["cmath"] 8 | ) 9 | 10 | setup( 11 | ext_modules=cythonize([extension]) 12 | ) 13 | -------------------------------------------------------------------------------- /ch1/2_math/pymath/test.py: -------------------------------------------------------------------------------- 1 | import pymath 2 | 3 | print(pymath.pyadd(1, 2)) 4 | -------------------------------------------------------------------------------- /ch1/2_math/run_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | 5 | cd cmath 6 | gcc -fPIC -shared -g *.c -o libcmath.so 7 | 8 | cd ../pymath 9 | CFLAGS="-I`pwd`/../cmath/" LDFLAGS="-L`pwd`/../cmath/" python setup.py build_ext --inplace 10 | 11 | LD_LIBRARY_PATH=`pwd`/../cmath/ python test.py 12 | -------------------------------------------------------------------------------- /ch1/3_gcc/cmath.c: -------------------------------------------------------------------------------- 1 | #include "cmath.h" 2 | 3 | int add(int a, int b) 4 | { 5 | return a + b; 6 | } 7 | -------------------------------------------------------------------------------- /ch1/3_gcc/cmath.h: -------------------------------------------------------------------------------- 1 | int add(int a, int b); 2 | -------------------------------------------------------------------------------- /ch1/3_gcc/pymath.pyx: -------------------------------------------------------------------------------- 1 | cdef extern from "cmath.h": 2 | int add(int a, int b) 3 | 4 | def pyadd(int a, int b): 5 | return add(a, b) 6 | -------------------------------------------------------------------------------- /ch1/3_gcc/run_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | 5 | gcc -fPIC -shared -g cmath.c -o libcmath.so 6 | 7 | # -2选项不是很重要,也可以省略,请不要太在意,感兴趣用`cython -help`看看啥意思 8 | cython -2 pymath.pyx 9 | 10 | # 由于这里引用的是python3.6的头文件,所以编译出来的so只能给python3使用 11 | gcc -fPIC -shared ./pymath.c -o pymath.so -I/usr/include/python3.6/ -L./ -lcmath 12 | LD_LIBRARY_PATH=`pwd` python3 test.py # 只能用python3执行,不能用python执行 13 | 14 | rm ./pymath.so 15 | # 由于这里引用的是python2.7的头文件,所以编译出来的so只能给python2使用 16 | gcc -fPIC -shared ./pymath.c -o pymath.so -I/usr/include/python2.7/ -L./ -lcmath 17 | LD_LIBRARY_PATH=`pwd` python test.py # 只能用python执行,不能用python3执行 18 | -------------------------------------------------------------------------------- /ch1/3_gcc/test.py: -------------------------------------------------------------------------------- 1 | import pymath 2 | 3 | print(pymath.pyadd(1, 2)) 4 | -------------------------------------------------------------------------------- /ch2/1_function/demo.pyx: -------------------------------------------------------------------------------- 1 | from libc.math cimport sin 2 | from libc.stdlib cimport atof 3 | 4 | def foo(char *s): 5 | x = atof(s) 6 | return sin(x) 7 | -------------------------------------------------------------------------------- /ch2/1_function/run_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | 5 | python setup.py build_ext --inplace 6 | 7 | python test.py 8 | -------------------------------------------------------------------------------- /ch2/1_function/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | from Cython.Build import cythonize 3 | 4 | extension = Extension( 5 | "demo", 6 | ["demo.pyx"], 7 | ) 8 | 9 | setup( 10 | ext_modules=cythonize([extension]) 11 | ) 12 | -------------------------------------------------------------------------------- /ch2/1_function/test.py: -------------------------------------------------------------------------------- 1 | import demo 2 | 3 | print(demo.foo("3.14159")) # 答案约等于0 4 | -------------------------------------------------------------------------------- /ch2/2_wrap_function/cqueue/queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2005-2008, Simon Howard 4 | 5 | Permission to use, copy, modify, and/or distribute this software 6 | for any purpose with or without fee is hereby granted, provided 7 | that the above copyright notice and this permission notice appear 8 | in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 14 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 16 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | */ 20 | 21 | #include 22 | 23 | #include "queue.h" 24 | 25 | /* malloc() / free() testing */ 26 | 27 | #ifdef ALLOC_TESTING 28 | #include "alloc-testing.h" 29 | #endif 30 | 31 | /* A double-ended queue */ 32 | 33 | typedef struct _QueueEntry QueueEntry; 34 | 35 | struct _QueueEntry { 36 | QueueValue data; 37 | QueueEntry *prev; 38 | QueueEntry *next; 39 | }; 40 | 41 | struct _Queue { 42 | QueueEntry *head; 43 | QueueEntry *tail; 44 | }; 45 | 46 | Queue *queue_new(void) 47 | { 48 | Queue *queue; 49 | 50 | queue = (Queue *) malloc(sizeof(Queue)); 51 | 52 | if (queue == NULL) { 53 | return NULL; 54 | } 55 | 56 | queue->head = NULL; 57 | queue->tail = NULL; 58 | 59 | return queue; 60 | } 61 | 62 | void queue_free(Queue *queue) 63 | { 64 | /* Empty the queue */ 65 | 66 | while (!queue_is_empty(queue)) { 67 | queue_pop_head(queue); 68 | } 69 | 70 | /* Free back the queue */ 71 | 72 | free(queue); 73 | } 74 | 75 | int queue_push_head(Queue *queue, QueueValue data) 76 | { 77 | QueueEntry *new_entry; 78 | 79 | /* Create the new entry and fill in the fields in the structure */ 80 | 81 | new_entry = malloc(sizeof(QueueEntry)); 82 | 83 | if (new_entry == NULL) { 84 | return 0; 85 | } 86 | 87 | new_entry->data = data; 88 | new_entry->prev = NULL; 89 | new_entry->next = queue->head; 90 | 91 | /* Insert into the queue */ 92 | 93 | if (queue->head == NULL) { 94 | 95 | /* If the queue was previously empty, both the head and 96 | * tail must be pointed at the new entry */ 97 | 98 | queue->head = new_entry; 99 | queue->tail = new_entry; 100 | 101 | } else { 102 | 103 | /* First entry in the list must have prev pointed back to this 104 | * new entry */ 105 | 106 | queue->head->prev = new_entry; 107 | 108 | /* Only the head must be pointed at the new entry */ 109 | 110 | queue->head = new_entry; 111 | } 112 | 113 | return 1; 114 | } 115 | 116 | QueueValue queue_pop_head(Queue *queue) 117 | { 118 | QueueEntry *entry; 119 | QueueValue result; 120 | 121 | /* Check the queue is not empty */ 122 | 123 | if (queue_is_empty(queue)) { 124 | return QUEUE_NULL; 125 | } 126 | 127 | /* Unlink the first entry from the head of the queue */ 128 | 129 | entry = queue->head; 130 | queue->head = entry->next; 131 | result = entry->data; 132 | 133 | if (queue->head == NULL) { 134 | 135 | /* If doing this has unlinked the last entry in the queue, set 136 | * tail to NULL as well. */ 137 | 138 | queue->tail = NULL; 139 | } else { 140 | 141 | /* The new first in the queue has no previous entry */ 142 | 143 | queue->head->prev = NULL; 144 | } 145 | 146 | /* Free back the queue entry structure */ 147 | 148 | free(entry); 149 | 150 | return result; 151 | } 152 | 153 | QueueValue queue_peek_head(Queue *queue) 154 | { 155 | if (queue_is_empty(queue)) { 156 | return QUEUE_NULL; 157 | } else { 158 | return queue->head->data; 159 | } 160 | } 161 | 162 | int queue_push_tail(Queue *queue, QueueValue data) 163 | { 164 | QueueEntry *new_entry; 165 | 166 | /* Create the new entry and fill in the fields in the structure */ 167 | 168 | new_entry = malloc(sizeof(QueueEntry)); 169 | 170 | if (new_entry == NULL) { 171 | return 0; 172 | } 173 | 174 | new_entry->data = data; 175 | new_entry->prev = queue->tail; 176 | new_entry->next = NULL; 177 | 178 | /* Insert into the queue tail */ 179 | 180 | if (queue->tail == NULL) { 181 | 182 | /* If the queue was previously empty, both the head and 183 | * tail must be pointed at the new entry */ 184 | 185 | queue->head = new_entry; 186 | queue->tail = new_entry; 187 | 188 | } else { 189 | 190 | /* The current entry at the tail must have next pointed to this 191 | * new entry */ 192 | 193 | queue->tail->next = new_entry; 194 | 195 | /* Only the tail must be pointed at the new entry */ 196 | 197 | queue->tail = new_entry; 198 | } 199 | 200 | return 1; 201 | } 202 | 203 | QueueValue queue_pop_tail(Queue *queue) 204 | { 205 | QueueEntry *entry; 206 | QueueValue result; 207 | 208 | /* Check the queue is not empty */ 209 | 210 | if (queue_is_empty(queue)) { 211 | return QUEUE_NULL; 212 | } 213 | 214 | /* Unlink the first entry from the tail of the queue */ 215 | 216 | entry = queue->tail; 217 | queue->tail = entry->prev; 218 | result = entry->data; 219 | 220 | if (queue->tail == NULL) { 221 | 222 | /* If doing this has unlinked the last entry in the queue, set 223 | * head to NULL as well. */ 224 | 225 | queue->head = NULL; 226 | 227 | } else { 228 | 229 | /* The new entry at the tail has no next entry. */ 230 | 231 | queue->tail->next = NULL; 232 | } 233 | 234 | /* Free back the queue entry structure */ 235 | 236 | free(entry); 237 | 238 | return result; 239 | } 240 | 241 | QueueValue queue_peek_tail(Queue *queue) 242 | { 243 | if (queue_is_empty(queue)) { 244 | return QUEUE_NULL; 245 | } else { 246 | return queue->tail->data; 247 | } 248 | } 249 | 250 | int queue_is_empty(Queue *queue) 251 | { 252 | return queue->head == NULL; 253 | } 254 | -------------------------------------------------------------------------------- /ch2/2_wrap_function/cqueue/queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2005-2008, Simon Howard 4 | 5 | Permission to use, copy, modify, and/or distribute this software 6 | for any purpose with or without fee is hereby granted, provided 7 | that the above copyright notice and this permission notice appear 8 | in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 14 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 16 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | */ 20 | 21 | /** 22 | * @file queue.h 23 | * 24 | * @brief Double-ended queue. 25 | * 26 | * A double ended queue stores a list of values in order. New values 27 | * can be added and removed from either end of the queue. 28 | * 29 | * To create a new queue, use @ref queue_new. To destroy a queue, use 30 | * @ref queue_free. 31 | * 32 | * To add values to a queue, use @ref queue_push_head and 33 | * @ref queue_push_tail. 34 | * 35 | * To read values from the ends of a queue, use @ref queue_pop_head 36 | * and @ref queue_pop_tail. To examine the ends without removing values 37 | * from the queue, use @ref queue_peek_head and @ref queue_peek_tail. 38 | * 39 | */ 40 | 41 | #ifndef ALGORITHM_QUEUE_H 42 | #define ALGORITHM_QUEUE_H 43 | 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | /** 49 | * A double-ended queue. 50 | */ 51 | 52 | typedef struct _Queue Queue; 53 | 54 | /** 55 | * A value stored in a @ref Queue. 56 | */ 57 | 58 | typedef void *QueueValue; 59 | 60 | /** 61 | * A null @ref QueueValue. 62 | */ 63 | 64 | #define QUEUE_NULL ((void *) 0) 65 | 66 | /** 67 | * Create a new double-ended queue. 68 | * 69 | * @return A new queue, or NULL if it was not possible to allocate 70 | * the memory. 71 | */ 72 | 73 | Queue *queue_new(void); 74 | 75 | /** 76 | * Destroy a queue. 77 | * 78 | * @param queue The queue to destroy. 79 | */ 80 | 81 | void queue_free(Queue *queue); 82 | 83 | /** 84 | * Add a value to the head of a queue. 85 | * 86 | * @param queue The queue. 87 | * @param data The value to add. 88 | * @return Non-zero if the value was added successfully, or zero 89 | * if it was not possible to allocate the memory for the 90 | * new entry. 91 | */ 92 | 93 | int queue_push_head(Queue *queue, QueueValue data); 94 | 95 | /** 96 | * Remove a value from the head of a queue. 97 | * 98 | * @param queue The queue. 99 | * @return Value that was at the head of the queue, or 100 | * @ref QUEUE_NULL if the queue is empty. 101 | */ 102 | 103 | QueueValue queue_pop_head(Queue *queue); 104 | 105 | /** 106 | * Read value from the head of a queue, without removing it from 107 | * the queue. 108 | * 109 | * @param queue The queue. 110 | * @return Value at the head of the queue, or @ref QUEUE_NULL if the 111 | * queue is empty. 112 | */ 113 | 114 | QueueValue queue_peek_head(Queue *queue); 115 | 116 | /** 117 | * Add a value to the tail of a queue. 118 | * 119 | * @param queue The queue. 120 | * @param data The value to add. 121 | * @return Non-zero if the value was added successfully, or zero 122 | * if it was not possible to allocate the memory for the 123 | * new entry. 124 | */ 125 | 126 | int queue_push_tail(Queue *queue, QueueValue data); 127 | 128 | /** 129 | * Remove a value from the tail of a queue. 130 | * 131 | * @param queue The queue. 132 | * @return Value that was at the head of the queue, or 133 | * @ref QUEUE_NULL if the queue is empty. 134 | */ 135 | 136 | QueueValue queue_pop_tail(Queue *queue); 137 | 138 | /** 139 | * Read a value from the tail of a queue, without removing it from 140 | * the queue. 141 | * 142 | * @param queue The queue. 143 | * @return Value at the tail of the queue, or QUEUE_NULL if the 144 | * queue is empty. 145 | */ 146 | 147 | QueueValue queue_peek_tail(Queue *queue); 148 | 149 | /** 150 | * Query if any values are currently in a queue. 151 | * 152 | * @param queue The queue. 153 | * @return Zero if the queue is not empty, non-zero if the queue 154 | * is empty. 155 | */ 156 | 157 | int queue_is_empty(Queue *queue); 158 | 159 | #ifdef __cplusplus 160 | } 161 | #endif 162 | 163 | #endif /* #ifndef ALGORITHM_QUEUE_H */ 164 | -------------------------------------------------------------------------------- /ch2/2_wrap_function/queue.pyx: -------------------------------------------------------------------------------- 1 | #file: queue.pyx 2 | 3 | from cpython.ref cimport PyObject 4 | 5 | cdef extern from "queue.h": 6 | ctypedef struct Queue: 7 | pass 8 | ctypedef void *QueueValue 9 | 10 | Queue *queue_new() 11 | void queue_free(Queue *queue) 12 | int queue_push_tail(Queue *queue, QueueValue data) 13 | QueueValue queue_pop_tail(Queue *queue) 14 | 15 | def foo(): 16 | # 虽然没有实际意义,但这段代码很自嗨,可以看到Cython中完全可以调用C函数 17 | cdef Queue *q 18 | q = queue_new() 19 | queue_free(q) 20 | 21 | cdef class PyQueue: 22 | cdef Queue *_c_queue 23 | 24 | def __cinit__(self): 25 | self._c_queue = queue_new() 26 | if self._c_queue == NULL: 27 | raise MemoryError() 28 | 29 | def __dealloc__(self): 30 | if self._c_queue is not NULL: 31 | queue_free(self._c_queue) 32 | 33 | def push_tail(self, v): 34 | if (queue_push_tail(self._c_queue, v) != 0): 35 | (v).ob_refcnt += 1 36 | else: 37 | raise MemoryError() 38 | 39 | def pop_tail(self): 40 | cdef QueueValue v 41 | v = queue_pop_tail(self._c_queue) 42 | if v != NULL: 43 | obj = v 44 | (obj).ob_refcnt -= 1 45 | return obj 46 | else: 47 | return None 48 | -------------------------------------------------------------------------------- /ch2/2_wrap_function/run_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | 5 | cd cqueue 6 | gcc -fPIC -shared -g *.c -o libcqueue.so 7 | 8 | cd .. 9 | CFLAGS="-I`pwd`/cqueue/" LDFLAGS="-L`pwd`/cqueue/" python setup.py build_ext --inplace 10 | 11 | LD_LIBRARY_PATH=`pwd`/cqueue/ python test.py 12 | -------------------------------------------------------------------------------- /ch2/2_wrap_function/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | from Cython.Build import cythonize 3 | 4 | extension = Extension( 5 | "queue", 6 | ["queue.pyx"], 7 | libraries=["cqueue"] 8 | ) 9 | 10 | setup( 11 | ext_modules=cythonize([extension]) 12 | ) 13 | -------------------------------------------------------------------------------- /ch2/2_wrap_function/test.py: -------------------------------------------------------------------------------- 1 | import queue 2 | 3 | q = queue.PyQueue() 4 | q.push_tail('a') 5 | q.push_tail(123) 6 | q.push_tail({'name': 'zhang', 'age': 18}) 7 | print(q.pop_tail()) 8 | print(q.pop_tail()) 9 | print(q.pop_tail()) 10 | -------------------------------------------------------------------------------- /ch2/3_callback/c/foo.c: -------------------------------------------------------------------------------- 1 | 2 | void traverse(int *arr, int len, void (*cb)(int)) { 3 | for (int i = 0; i < len; i++) { 4 | cb(arr[i]); 5 | } 6 | } 7 | 8 | void traverse2(int *arr, int len, void (*cb)(int, void*), void *priv) { 9 | for (int i = 0; i < len; i++) { 10 | cb(arr[i], priv); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ch2/3_callback/c/foo.h: -------------------------------------------------------------------------------- 1 | void traverse(int *arr, int len, void (*cb)(int)); 2 | void traverse2(int *arr, int len, void (*cb)(int, void*), void *priv); 3 | -------------------------------------------------------------------------------- /ch2/3_callback/py/cfoo.pxd: -------------------------------------------------------------------------------- 1 | cdef extern from 'foo.h': 2 | void traverse(int *arr, int len, void (*cb)(int)) 3 | void traverse2(int *arr, int len, void (*cb)(int, void*), void *priv) 4 | -------------------------------------------------------------------------------- /ch2/3_callback/py/foo.pyx: -------------------------------------------------------------------------------- 1 | from cfoo cimport traverse as c_traverse 2 | from cfoo cimport traverse2 as c_traverse2 3 | from libc.stdlib cimport malloc, free 4 | 5 | app_cb = None 6 | 7 | cdef void traverse_cb(int a): 8 | global app_cb 9 | app_cb(a) 10 | 11 | def traverse(arr, cb): 12 | global app_cb 13 | cdef int *c_arr = malloc(n_arr * sizeof(int)) 14 | cdef int n_arr = len(arr) 15 | for i in range(n_arr): 16 | c_arr[i] = arr[i] 17 | app_cb = cb 18 | c_traverse(c_arr, n_arr, traverse_cb) 19 | free(c_arr) 20 | 21 | cdef void traverse2_cb(int a, void *priv): 22 | wrap_priv = priv 23 | cb = wrap_priv['cb'] 24 | p = wrap_priv['p'] 25 | cb(a, p) 26 | 27 | def traverse2(arr, cb, priv): 28 | global app_cb 29 | cdef int *c_arr 30 | cdef int n_arr 31 | 32 | n_arr = len(arr) 33 | c_arr = malloc(n_arr * sizeof(int)) 34 | for i in range(n_arr): 35 | c_arr[i] = arr[i] 36 | wrap_priv = {'cb': cb, 'p': priv} 37 | c_traverse2(c_arr, n_arr, traverse2_cb, wrap_priv) 38 | free(c_arr) 39 | -------------------------------------------------------------------------------- /ch2/3_callback/py/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | from Cython.Build import cythonize 3 | 4 | extension = Extension( 5 | "foo", 6 | ["foo.pyx"], 7 | libraries=["foo"] 8 | ) 9 | 10 | setup( 11 | ext_modules=cythonize([extension]) 12 | ) 13 | -------------------------------------------------------------------------------- /ch2/3_callback/py/test.py: -------------------------------------------------------------------------------- 1 | import foo 2 | 3 | def cb(a): 4 | print(a*a) 5 | 6 | foo.traverse([1, 2, 3], cb) 7 | 8 | def cb2(a, priv): 9 | print(priv + str(a)) 10 | 11 | foo.traverse2([4, 5, 6], cb2, 'hello: ') 12 | -------------------------------------------------------------------------------- /ch2/3_callback/run_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | 5 | cd c 6 | gcc -fPIC -shared -g *.c -o libfoo.so 7 | 8 | cd ../py 9 | CFLAGS="-I`pwd`/../c/" LDFLAGS="-L`pwd`/../c/" python setup.py build_ext --inplace 10 | 11 | LD_LIBRARY_PATH=`pwd`/../c/ python test.py 12 | -------------------------------------------------------------------------------- /ch2/4_struct/c/person.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "person.h" 3 | 4 | void person_say_hello(person_t *p) { 5 | printf("hello, my name is %s, %d years old.\n", p->name, p->age); 6 | } 7 | -------------------------------------------------------------------------------- /ch2/4_struct/c/person.h: -------------------------------------------------------------------------------- 1 | #ifndef __PERSON_H__ 2 | #define __PERSON_H__ 3 | 4 | typedef struct person_s { 5 | int age; 6 | char *name; 7 | } person_t; 8 | 9 | void person_say_hello(person_t *p); 10 | 11 | #endif /* __PERSON_H__ */ 12 | -------------------------------------------------------------------------------- /ch2/4_struct/py/person.pxd: -------------------------------------------------------------------------------- 1 | from libc.stdlib cimport malloc, free 2 | 3 | cdef extern from "person.h": 4 | ctypedef struct person_t: 5 | int age 6 | char *name 7 | 8 | void person_say_hello(person_t *p) 9 | 10 | cdef class Person: 11 | cdef person_t *__ptr 12 | -------------------------------------------------------------------------------- /ch2/4_struct/py/person.pyx: -------------------------------------------------------------------------------- 1 | 2 | cdef class Person: 3 | def __cinit__(self): 4 | self.__ptr = malloc(sizeof(person_t)) 5 | 6 | def __dealloc__(self): 7 | free(self.__ptr) 8 | 9 | @property 10 | def age(self): 11 | return self.__ptr.age 12 | 13 | @age.setter 14 | def age(self, value): 15 | self.__ptr.age = value 16 | 17 | @property 18 | def name(self): 19 | return self.__ptr.name 20 | 21 | @name.setter 22 | def name(self, value): 23 | self.__ptr.name = value 24 | 25 | def say_hello(self): 26 | person_say_hello(self.__ptr) 27 | -------------------------------------------------------------------------------- /ch2/4_struct/py/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | from Cython.Build import cythonize 3 | 4 | extension = Extension( 5 | "person", 6 | ["person.pyx"], 7 | libraries=["person"] 8 | ) 9 | 10 | setup( 11 | ext_modules=cythonize([extension]) 12 | ) 13 | -------------------------------------------------------------------------------- /ch2/4_struct/py/test.py: -------------------------------------------------------------------------------- 1 | import person 2 | 3 | p = person.Person() 4 | p.name = 'zhangsan' 5 | p.age = 18 6 | p.say_hello() 7 | 8 | print(p.name) 9 | print(p.age) 10 | -------------------------------------------------------------------------------- /ch2/4_struct/run_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | 5 | cd c 6 | gcc -fPIC -shared -g *.c -o libperson.so 7 | 8 | cd ../py 9 | CFLAGS="-I`pwd`/../c/" LDFLAGS="-L`pwd`/../c/" python setup.py build_ext --inplace 10 | 11 | LD_LIBRARY_PATH=`pwd`/../c/ python test.py 12 | -------------------------------------------------------------------------------- /ch3/1_pxd/cqueue.pxd: -------------------------------------------------------------------------------- 1 | #file: queue.pxd 2 | 3 | cdef extern from "queue.h": 4 | ctypedef struct Queue: 5 | pass 6 | ctypedef void *QueueValue 7 | 8 | Queue *queue_new() 9 | void queue_free(Queue *queue) 10 | -------------------------------------------------------------------------------- /ch3/1_pxd/cqueue/queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2005-2008, Simon Howard 4 | 5 | Permission to use, copy, modify, and/or distribute this software 6 | for any purpose with or without fee is hereby granted, provided 7 | that the above copyright notice and this permission notice appear 8 | in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 14 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 16 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | */ 20 | 21 | #include 22 | 23 | #include "queue.h" 24 | 25 | /* malloc() / free() testing */ 26 | 27 | #ifdef ALLOC_TESTING 28 | #include "alloc-testing.h" 29 | #endif 30 | 31 | /* A double-ended queue */ 32 | 33 | typedef struct _QueueEntry QueueEntry; 34 | 35 | struct _QueueEntry { 36 | QueueValue data; 37 | QueueEntry *prev; 38 | QueueEntry *next; 39 | }; 40 | 41 | struct _Queue { 42 | QueueEntry *head; 43 | QueueEntry *tail; 44 | }; 45 | 46 | Queue *queue_new(void) 47 | { 48 | Queue *queue; 49 | 50 | queue = (Queue *) malloc(sizeof(Queue)); 51 | 52 | if (queue == NULL) { 53 | return NULL; 54 | } 55 | 56 | queue->head = NULL; 57 | queue->tail = NULL; 58 | 59 | return queue; 60 | } 61 | 62 | void queue_free(Queue *queue) 63 | { 64 | /* Empty the queue */ 65 | 66 | while (!queue_is_empty(queue)) { 67 | queue_pop_head(queue); 68 | } 69 | 70 | /* Free back the queue */ 71 | 72 | free(queue); 73 | } 74 | 75 | int queue_push_head(Queue *queue, QueueValue data) 76 | { 77 | QueueEntry *new_entry; 78 | 79 | /* Create the new entry and fill in the fields in the structure */ 80 | 81 | new_entry = malloc(sizeof(QueueEntry)); 82 | 83 | if (new_entry == NULL) { 84 | return 0; 85 | } 86 | 87 | new_entry->data = data; 88 | new_entry->prev = NULL; 89 | new_entry->next = queue->head; 90 | 91 | /* Insert into the queue */ 92 | 93 | if (queue->head == NULL) { 94 | 95 | /* If the queue was previously empty, both the head and 96 | * tail must be pointed at the new entry */ 97 | 98 | queue->head = new_entry; 99 | queue->tail = new_entry; 100 | 101 | } else { 102 | 103 | /* First entry in the list must have prev pointed back to this 104 | * new entry */ 105 | 106 | queue->head->prev = new_entry; 107 | 108 | /* Only the head must be pointed at the new entry */ 109 | 110 | queue->head = new_entry; 111 | } 112 | 113 | return 1; 114 | } 115 | 116 | QueueValue queue_pop_head(Queue *queue) 117 | { 118 | QueueEntry *entry; 119 | QueueValue result; 120 | 121 | /* Check the queue is not empty */ 122 | 123 | if (queue_is_empty(queue)) { 124 | return QUEUE_NULL; 125 | } 126 | 127 | /* Unlink the first entry from the head of the queue */ 128 | 129 | entry = queue->head; 130 | queue->head = entry->next; 131 | result = entry->data; 132 | 133 | if (queue->head == NULL) { 134 | 135 | /* If doing this has unlinked the last entry in the queue, set 136 | * tail to NULL as well. */ 137 | 138 | queue->tail = NULL; 139 | } else { 140 | 141 | /* The new first in the queue has no previous entry */ 142 | 143 | queue->head->prev = NULL; 144 | } 145 | 146 | /* Free back the queue entry structure */ 147 | 148 | free(entry); 149 | 150 | return result; 151 | } 152 | 153 | QueueValue queue_peek_head(Queue *queue) 154 | { 155 | if (queue_is_empty(queue)) { 156 | return QUEUE_NULL; 157 | } else { 158 | return queue->head->data; 159 | } 160 | } 161 | 162 | int queue_push_tail(Queue *queue, QueueValue data) 163 | { 164 | QueueEntry *new_entry; 165 | 166 | /* Create the new entry and fill in the fields in the structure */ 167 | 168 | new_entry = malloc(sizeof(QueueEntry)); 169 | 170 | if (new_entry == NULL) { 171 | return 0; 172 | } 173 | 174 | new_entry->data = data; 175 | new_entry->prev = queue->tail; 176 | new_entry->next = NULL; 177 | 178 | /* Insert into the queue tail */ 179 | 180 | if (queue->tail == NULL) { 181 | 182 | /* If the queue was previously empty, both the head and 183 | * tail must be pointed at the new entry */ 184 | 185 | queue->head = new_entry; 186 | queue->tail = new_entry; 187 | 188 | } else { 189 | 190 | /* The current entry at the tail must have next pointed to this 191 | * new entry */ 192 | 193 | queue->tail->next = new_entry; 194 | 195 | /* Only the tail must be pointed at the new entry */ 196 | 197 | queue->tail = new_entry; 198 | } 199 | 200 | return 1; 201 | } 202 | 203 | QueueValue queue_pop_tail(Queue *queue) 204 | { 205 | QueueEntry *entry; 206 | QueueValue result; 207 | 208 | /* Check the queue is not empty */ 209 | 210 | if (queue_is_empty(queue)) { 211 | return QUEUE_NULL; 212 | } 213 | 214 | /* Unlink the first entry from the tail of the queue */ 215 | 216 | entry = queue->tail; 217 | queue->tail = entry->prev; 218 | result = entry->data; 219 | 220 | if (queue->tail == NULL) { 221 | 222 | /* If doing this has unlinked the last entry in the queue, set 223 | * head to NULL as well. */ 224 | 225 | queue->head = NULL; 226 | 227 | } else { 228 | 229 | /* The new entry at the tail has no next entry. */ 230 | 231 | queue->tail->next = NULL; 232 | } 233 | 234 | /* Free back the queue entry structure */ 235 | 236 | free(entry); 237 | 238 | return result; 239 | } 240 | 241 | QueueValue queue_peek_tail(Queue *queue) 242 | { 243 | if (queue_is_empty(queue)) { 244 | return QUEUE_NULL; 245 | } else { 246 | return queue->tail->data; 247 | } 248 | } 249 | 250 | int queue_is_empty(Queue *queue) 251 | { 252 | return queue->head == NULL; 253 | } 254 | -------------------------------------------------------------------------------- /ch3/1_pxd/cqueue/queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2005-2008, Simon Howard 4 | 5 | Permission to use, copy, modify, and/or distribute this software 6 | for any purpose with or without fee is hereby granted, provided 7 | that the above copyright notice and this permission notice appear 8 | in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 14 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 16 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | */ 20 | 21 | /** 22 | * @file queue.h 23 | * 24 | * @brief Double-ended queue. 25 | * 26 | * A double ended queue stores a list of values in order. New values 27 | * can be added and removed from either end of the queue. 28 | * 29 | * To create a new queue, use @ref queue_new. To destroy a queue, use 30 | * @ref queue_free. 31 | * 32 | * To add values to a queue, use @ref queue_push_head and 33 | * @ref queue_push_tail. 34 | * 35 | * To read values from the ends of a queue, use @ref queue_pop_head 36 | * and @ref queue_pop_tail. To examine the ends without removing values 37 | * from the queue, use @ref queue_peek_head and @ref queue_peek_tail. 38 | * 39 | */ 40 | 41 | #ifndef ALGORITHM_QUEUE_H 42 | #define ALGORITHM_QUEUE_H 43 | 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | /** 49 | * A double-ended queue. 50 | */ 51 | 52 | typedef struct _Queue Queue; 53 | 54 | /** 55 | * A value stored in a @ref Queue. 56 | */ 57 | 58 | typedef void *QueueValue; 59 | 60 | /** 61 | * A null @ref QueueValue. 62 | */ 63 | 64 | #define QUEUE_NULL ((void *) 0) 65 | 66 | /** 67 | * Create a new double-ended queue. 68 | * 69 | * @return A new queue, or NULL if it was not possible to allocate 70 | * the memory. 71 | */ 72 | 73 | Queue *queue_new(void); 74 | 75 | /** 76 | * Destroy a queue. 77 | * 78 | * @param queue The queue to destroy. 79 | */ 80 | 81 | void queue_free(Queue *queue); 82 | 83 | /** 84 | * Add a value to the head of a queue. 85 | * 86 | * @param queue The queue. 87 | * @param data The value to add. 88 | * @return Non-zero if the value was added successfully, or zero 89 | * if it was not possible to allocate the memory for the 90 | * new entry. 91 | */ 92 | 93 | int queue_push_head(Queue *queue, QueueValue data); 94 | 95 | /** 96 | * Remove a value from the head of a queue. 97 | * 98 | * @param queue The queue. 99 | * @return Value that was at the head of the queue, or 100 | * @ref QUEUE_NULL if the queue is empty. 101 | */ 102 | 103 | QueueValue queue_pop_head(Queue *queue); 104 | 105 | /** 106 | * Read value from the head of a queue, without removing it from 107 | * the queue. 108 | * 109 | * @param queue The queue. 110 | * @return Value at the head of the queue, or @ref QUEUE_NULL if the 111 | * queue is empty. 112 | */ 113 | 114 | QueueValue queue_peek_head(Queue *queue); 115 | 116 | /** 117 | * Add a value to the tail of a queue. 118 | * 119 | * @param queue The queue. 120 | * @param data The value to add. 121 | * @return Non-zero if the value was added successfully, or zero 122 | * if it was not possible to allocate the memory for the 123 | * new entry. 124 | */ 125 | 126 | int queue_push_tail(Queue *queue, QueueValue data); 127 | 128 | /** 129 | * Remove a value from the tail of a queue. 130 | * 131 | * @param queue The queue. 132 | * @return Value that was at the head of the queue, or 133 | * @ref QUEUE_NULL if the queue is empty. 134 | */ 135 | 136 | QueueValue queue_pop_tail(Queue *queue); 137 | 138 | /** 139 | * Read a value from the tail of a queue, without removing it from 140 | * the queue. 141 | * 142 | * @param queue The queue. 143 | * @return Value at the tail of the queue, or QUEUE_NULL if the 144 | * queue is empty. 145 | */ 146 | 147 | QueueValue queue_peek_tail(Queue *queue); 148 | 149 | /** 150 | * Query if any values are currently in a queue. 151 | * 152 | * @param queue The queue. 153 | * @return Zero if the queue is not empty, non-zero if the queue 154 | * is empty. 155 | */ 156 | 157 | int queue_is_empty(Queue *queue); 158 | 159 | #ifdef __cplusplus 160 | } 161 | #endif 162 | 163 | #endif /* #ifndef ALGORITHM_QUEUE_H */ 164 | -------------------------------------------------------------------------------- /ch3/1_pxd/queue.pyx: -------------------------------------------------------------------------------- 1 | cimport cqueue 2 | 3 | # 类叫做Queue,不怕和C的Queue结构体名字冲突了 4 | cdef class Queue: 5 | cdef cqueue.Queue *_c_queue 6 | 7 | def __cinit__(self): 8 | self._c_queue = cqueue.queue_new() 9 | 10 | def __dealloc__(self): 11 | if self._c_queue is not NULL: 12 | cqueue.queue_free(self._c_queue) 13 | -------------------------------------------------------------------------------- /ch3/1_pxd/run_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | 5 | cd cqueue 6 | gcc -fPIC -shared -g *.c -o libcqueue.so 7 | 8 | cd .. 9 | CFLAGS="-I`pwd`/cqueue/" LDFLAGS="-L`pwd`/cqueue/" python setup.py build_ext --inplace 10 | 11 | LD_LIBRARY_PATH=`pwd`/cqueue/ python test.py 12 | -------------------------------------------------------------------------------- /ch3/1_pxd/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | from Cython.Build import cythonize 3 | 4 | extension = Extension( 5 | "queue", 6 | ["queue.pyx"], 7 | libraries=["cqueue"] 8 | ) 9 | 10 | setup( 11 | ext_modules=cythonize([extension]) 12 | ) 13 | -------------------------------------------------------------------------------- /ch3/1_pxd/test.py: -------------------------------------------------------------------------------- 1 | import queue 2 | 3 | q = queue.Queue() 4 | --------------------------------------------------------------------------------