├── README.md
├── with_C_API
├── README.md
├── ptexample.c
├── pysample.c
├── pysample.h
├── sample.c
├── sample.h
├── setup.py
└── setup_sample.py
├── with_Cython
├── csample.pxd
├── sample.c
├── sample.cpython-36m-x86_64-linux-gnu.so
├── sample.h
├── sample.pyx
└── setup.py
└── with_ctypes
├── README.md
├── sample.c
├── sample.h
└── sample.py
/README.md:
--------------------------------------------------------------------------------
1 | # Extend Python with C
2 | 实际上ctypes、Python C API或者基于API的Cython,逻辑都是:
3 | `接收Python对象`->`转换为C对象`->`调用C函数`->`返回值转换为Python对象`->`返回`
4 | 的流程,较为值得关切的几点是:
5 | 指针传参问题
6 | 数组传递问题
7 | 结构体传递问题
8 | 高级一点情况会考虑如何绕过GIL提升速度
9 |
--------------------------------------------------------------------------------
/with_C_API/README.md:
--------------------------------------------------------------------------------
1 |
2 | #### 资料原文
3 | [python3cookbook第十五章_C语言扩展](http://python3-cookbook.readthedocs.io/zh_CN/latest/chapters/p15_c_extensions.html#)
4 | #### 程序介绍
5 | [『Python CoolBook』C扩展库_其二_demo演示](https://www.cnblogs.com/hellcat/p/9083441.html)
6 | [『Python CoolBook』C扩展库_其三_简单数组操作](https://www.cnblogs.com/hellcat/p/9088524.html)
7 | [『Python CoolBook』C扩展库_其四_结构体操作与Capsule](https://www.cnblogs.com/hellcat/p/9088824.html)
8 | [『Python CoolBook』C扩展库_其五_C语言层面Python库之间调用API](https://www.cnblogs.com/hellcat/p/9089723.html)
9 | [『Python CoolBook』C扩展库_其六_从C语言中调用Python代码](https://www.cnblogs.com/hellcat/p/9093602.html)
10 | [『Python CoolBook』高效数组操作](https://www.cnblogs.com/hellcat/p/9130241.html)
11 |
--------------------------------------------------------------------------------
/with_C_API/ptexample.c:
--------------------------------------------------------------------------------
1 | /* ptexample.c */
2 |
3 | /* Include the header associated with the other module */
4 | #include "pysample.h"
5 |
6 | /* An extension function that uses the exported API */
7 | static PyObject *print_point(PyObject *self, PyObject *args) {
8 | PyObject *obj;
9 | Point *p;
10 | if (!PyArg_ParseTuple(args,"O", &obj)) {
11 | return NULL;
12 | }
13 |
14 | /* Note: This is defined in a different module */
15 | p = PyPoint_AsPoint(obj);
16 | if (!p) {
17 | return NULL;
18 | }
19 | printf("%f %f\n", p->x, p->y);
20 | return Py_BuildValue("");
21 | }
22 |
23 | static PyMethodDef PtExampleMethods[] = {
24 | {"print_point", print_point, METH_VARARGS, "output a point"},
25 | { NULL, NULL, 0, NULL}
26 | };
27 |
28 | static struct PyModuleDef ptexamplemodule = {
29 | PyModuleDef_HEAD_INIT,
30 | "ptexample", /* name of module */
31 | "A module that imports an API", /* Doc string (may be NULL) */
32 | -1, /* Size of per-interpreter state or -1 */
33 | PtExampleMethods /* Method table */
34 | };
35 |
36 | /* Module initialization function */
37 | PyMODINIT_FUNC
38 | PyInit_ptexample(void) {
39 | PyObject *m;
40 |
41 | m = PyModule_Create(&ptexamplemodule);
42 | if (m == NULL)
43 | return NULL;
44 |
45 | /* Import sample, loading its API functions */
46 | if (!import_sample()) { //<---pysample.h:21
47 | return NULL;
48 | }
49 |
50 | return m;
51 | }
52 |
--------------------------------------------------------------------------------
/with_C_API/pysample.c:
--------------------------------------------------------------------------------
1 | /* pysample.c */
2 |
3 | #include "Python.h"
4 | #define PYSAMPLE_MODULE //<---pysample.h:16
5 | #include "pysample.h"
6 |
7 | /* int gcd(int, int) */
8 | static PyObject *py_gcd(PyObject *self, PyObject *args) {
9 | int x, y, result;
10 |
11 | if (!PyArg_ParseTuple(args,"ii", &x, &y)) {
12 | return NULL;
13 | }
14 | result = gcd(x,y);
15 | return Py_BuildValue("i", result);
16 | }
17 |
18 | /* int in_mandel(double, double, int) */
19 | static PyObject *py_in_mandel(PyObject *self, PyObject *args) {
20 | double x0, y0;
21 | int n;
22 | int result;
23 |
24 | if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) {
25 | return NULL;
26 | }
27 | result = in_mandel(x0,y0,n);
28 | return Py_BuildValue("i", result);
29 | }
30 |
31 | /* int divide(int, int, int *) */
32 | static PyObject *py_divide(PyObject *self, PyObject *args) {
33 | int a, b, quotient, remainder;
34 | if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
35 | return NULL;
36 | }
37 | quotient = divide(a,b, &remainder);
38 | return Py_BuildValue("(ii)", quotient, remainder);
39 | }
40 |
41 | /* Call double avg(double *, int) */
42 | static PyObject *py_avg(PyObject *self, PyObject *args) {
43 | PyObject *bufobj;
44 | Py_buffer view;
45 | double result;
46 | /* Get the passed Python object */
47 | // 在一个C对象指针中储存一个Python对象(没有任何转换)。
48 | // 因此,C程序接收传递的实际对象。对象的引用计数没有增加。
49 | // 存储的指针不是空的
50 | if (!PyArg_ParseTuple(args, "O", &bufobj)) {
51 | return NULL;
52 | }
53 |
54 | /* Attempt to extract buffer information from it */
55 |
56 | if (PyObject_GetBuffer(bufobj, &view,
57 | PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) {
58 | return NULL;
59 | }
60 |
61 | if (view.ndim != 1) {
62 | PyErr_SetString(PyExc_TypeError, "Expected a 1-dimensional array");
63 | PyBuffer_Release(&view);
64 | return NULL;
65 | }
66 |
67 | /* Check the type of items in the array */
68 | if (strcmp(view.format,"d") != 0) {
69 | PyErr_SetString(PyExc_TypeError, "Expected an array of doubles");
70 | PyBuffer_Release(&view);
71 | return NULL;
72 | }
73 |
74 | /* Pass the raw buffer and size to the C function */
75 | result = avg(view.buf, view.shape[0]);
76 |
77 | /* Indicate we're done working with the buffer */
78 | PyBuffer_Release(&view);
79 | return Py_BuildValue("d", result);
80 | }
81 |
82 |
83 | /* Destructor function for points */
84 | static void del_Point(PyObject *obj) {
85 | printf("Deleting point\n");
86 | free(PyCapsule_GetPointer(obj,"Point"));
87 | }
88 |
89 | static PyObject *PyPoint_FromPoint(Point *p, int must_free) {
90 | /* 胶囊和C指针类似。在内部,它们获取一个通用指针和一个名称,可以使用
91 | PyCapsule_New() 函数很容易的被创建。 另外,一个可选的析构函数能被
92 | 绑定到胶囊上,用来在胶囊对象被垃圾回收时释放底层的内存*/
93 | return PyCapsule_New(p, "Point", must_free ? del_Point : NULL);
94 | }
95 |
96 | /* Utility functions */
97 | static Point *PyPoint_AsPoint(PyObject *obj) {
98 | return (Point *) PyCapsule_GetPointer(obj, "Point");
99 | }
100 |
101 | static _PointAPIMethods _point_api = {
102 | PyPoint_AsPoint,
103 | PyPoint_FromPoint
104 | };
105 |
106 | /* Create a new Point object */
107 | static PyObject *py_Point(PyObject *self, PyObject *args) {
108 |
109 | Point *p;
110 | double x,y;
111 | if (!PyArg_ParseTuple(args,"dd",&x,&y)) {
112 | return NULL;
113 | }
114 | p = (Point *) malloc(sizeof(Point));
115 | p->x = x;
116 | p->y = y;
117 | return PyPoint_FromPoint(p, 1);
118 | }
119 |
120 | static PyObject *py_distance(PyObject *self, PyObject *args) {
121 | Point *p1, *p2;
122 | PyObject *py_p1, *py_p2;
123 | double result;
124 |
125 | if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) {
126 | return NULL;
127 | }
128 | if (!(p1 = PyPoint_AsPoint(py_p1))) {
129 | return NULL;
130 | }
131 | if (!(p2 = PyPoint_AsPoint(py_p2))) {
132 | return NULL;
133 | }
134 | result = distance(p1,p2);
135 | return Py_BuildValue("d", result);
136 | }
137 |
138 | // void clip(double *a, int n, double min, double max, double *out);
139 | static PyObject *py_clip(PyObject *self, PyObject *args){
140 | PyObject *a, *out;
141 | int min, max;
142 | if(!PyArg_ParseTuple(args, "OiiO", &a, &min, &max, &out)){
143 | return NULL;
144 | }
145 |
146 | // printf("%i, %i\n", min, max);
147 | Py_buffer view_a, view_out;
148 | if (PyObject_GetBuffer(a, &view_a,
149 | PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) {
150 | return NULL;
151 | }
152 | if (PyObject_GetBuffer(out, &view_out,
153 | PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) {
154 | return NULL;
155 | }
156 | clip(view_a.buf, view_a.shape[0], min, max, view_out.buf);
157 | PyBuffer_Release(&view_a);
158 | PyBuffer_Release(&view_out);
159 | return Py_BuildValue("");
160 | }
161 |
162 | #include "Python.h"
163 |
164 | /* Execute func(x,y) in the Python interpreter. The
165 | arguments and return result of the function must
166 | be Python floats */
167 |
168 | double call_func(PyObject *func, double x, double y) {
169 | PyObject *args;
170 | PyObject *kwargs;
171 | PyObject *result = 0;
172 | double retval;
173 |
174 | /* Make sure we own the GIL */
175 | PyGILState_STATE state = PyGILState_Ensure();
176 |
177 | /* Verify that func is a proper callable */
178 | /* 你必须先有一个表示你将要调用的Python可调用对象。 这可以是一个函数、
179 | 类、方法、内置方法或其他任意实现了 __call__() 操作的东西。 为了确
180 | 保是可调用的,可以像下面的代码这样利用 PyCallable_Check() 做检查 */
181 | if (!PyCallable_Check(func)) {
182 | fprintf(stderr,"call_func: expected a callable\n");
183 | goto fail;
184 | }
185 | /* Build arguments */
186 | /* 使用 Py_BuildValue()构建参数元组或字典 */
187 | args = Py_BuildValue("(dd)", x, y);
188 | kwargs = NULL;
189 |
190 | /* Call the function */
191 | /* 使用 PyObject_Call(),传一个可调用对象给它、一个参数元组
192 | 和一个可选的关键字字典。
193 | 如果没有关键字参数,传递NULL */
194 | result = PyObject_Call(func, args, kwargs);
195 | /* 需要确保使用了 Py_DECREF() 或者 Py_XDECREF() 清理参数。
196 | 第二个函数相对安全点,因为它允许传递NULL指针(直接忽略它),
197 | 这也是为什么我们使用它来清理可选的关键字参数。 */
198 | Py_DECREF(args);
199 | Py_XDECREF(kwargs);
200 |
201 | /* Check for Python exceptions (if any) */
202 | /* 调用万Python函数之后,用PyErr_Occurred() 函数检查是否
203 | 有异常发生 */
204 | if (PyErr_Occurred()) {
205 | PyErr_Print();
206 | goto fail;
207 | }
208 |
209 | /* Verify the result is a float object */
210 | if (!PyFloat_Check(result)) {
211 | fprintf(stderr,"call_func: callable didn't return a float\n");
212 | goto fail;
213 | }
214 |
215 | /* Create the return value */
216 | retval = PyFloat_AsDouble(result);
217 | Py_DECREF(result);
218 |
219 | /* Restore previous GIL state and return */
220 | PyGILState_Release(state);
221 | return retval;
222 |
223 | fail:
224 | Py_XDECREF(result);
225 | PyGILState_Release(state);
226 | abort(); // Change to something more appropriate
227 | }
228 |
229 | /* Extension function for testing the C-Python callback */
230 | static PyObject *py_call_func(PyObject *self, PyObject *args) {
231 | PyObject *func;
232 |
233 | double x, y, result;
234 | if (!PyArg_ParseTuple(args,"Odd", &func,&x,&y)) {
235 | return NULL;
236 | }
237 | result = call_func(func, x, y);
238 | return Py_BuildValue("d", result);
239 | }
240 |
241 | /* Module method table */
242 | static PyMethodDef SampleMethods[] = {
243 | {"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"},
244 | {"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"},
245 | {"divide", py_divide, METH_VARARGS, "Integer division"},
246 | {"avg", py_avg, METH_VARARGS, "Average values in an array"},
247 | {"Point", py_Point, METH_VARARGS},
248 | {"distance", py_distance, METH_VARARGS, "Function involving a C data structure"},
249 | {"clip", py_clip, METH_VARARGS, "clip array"},
250 | {"call_func", py_call_func, METH_VARARGS, "Extension function for testing the C-Python callback"},
251 | { NULL, NULL, 0, NULL}
252 | };
253 |
254 | /* Module structure */
255 | static struct PyModuleDef samplemodule = {
256 | PyModuleDef_HEAD_INIT,
257 |
258 | "sample", /* name of module */
259 | "A sample module", /* Doc string (may be NULL) */
260 | -1, /* Size of per-interpreter state or -1 */
261 | SampleMethods /* Method table */
262 | };
263 |
264 | /* Module initialization function */
265 | PyMODINIT_FUNC
266 | PyInit_sample(void) {
267 | PyObject *m;
268 | PyObject *py_point_api;
269 |
270 | m = PyModule_Create(&samplemodule);
271 | if (m == NULL)
272 | return NULL;
273 |
274 | /* Add the Point C API functions */
275 | py_point_api = PyCapsule_New((void *) &_point_api, "sample._point_api", NULL); //<---pysample.h:23
276 | if (py_point_api) {
277 | PyModule_AddObject(m, "_point_api", py_point_api);
278 | }
279 | return m;
280 | }
--------------------------------------------------------------------------------
/with_C_API/pysample.h:
--------------------------------------------------------------------------------
1 | /* pysample.h */
2 | #include "Python.h"
3 | #include "sample.h"
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | /* Public API Table */
9 | /* 这里最重要的部分是函数指针表 _PointAPIMethods.
10 | 它会在导出模块时被初始化,然后导入模块时被查找到。 */
11 | typedef struct {
12 | Point *(*aspoint)(PyObject *);
13 | PyObject *(*frompoint)(Point *, int);
14 | } _PointAPIMethods;
15 |
16 | #ifndef PYSAMPLE_MODULE //<---pysample.c:4
17 | /* Method table in external module */
18 | static _PointAPIMethods *_point_api = 0;
19 |
20 | /* Import the API table from sample, import_sample() 被用来指向胶囊导入并初始化这个指针 */
21 | static int import_sample(void) { //<---ptexample.c:46
22 | // 需提供属性名(比如sample._point_api),会一次性找到胶囊对象并提取出指针来。
23 | _point_api = (_PointAPIMethods *) PyCapsule_Import("sample._point_api",0); //<---pysample.c:250
24 | return (_point_api != NULL) ? 1 : 0;
25 | }
26 |
27 | /* Macros to implement the programming interface */
28 | #define PyPoint_AsPoint(obj) (_point_api->aspoint)(obj)
29 | #define PyPoint_FromPoint(obj) (_point_api->frompoint)(obj)
30 | #endif
31 |
32 | #ifdef __cplusplus
33 | }
34 | #endif
35 |
--------------------------------------------------------------------------------
/with_C_API/sample.c:
--------------------------------------------------------------------------------
1 | /* sample.c */
2 | #include "sample.h"
3 |
4 | /* Compute the greatest common divisor */
5 | int gcd(int x, int y) {
6 | int g = y;
7 | while (x > 0) {
8 | g = x;
9 | x = y % x;
10 | y = g;
11 | }
12 | return g;
13 | }
14 |
15 | /* Test if (x0,y0) is in the Mandelbrot set or not */
16 | int in_mandel(double x0, double y0, int n) {
17 | double x=0,y=0,xtemp;
18 | while (n > 0) {
19 | xtemp = x*x - y*y + x0;
20 | y = 2*x*y + y0;
21 | x = xtemp;
22 | n -= 1;
23 | if (x*x + y*y > 4) return 0;
24 | }
25 | return 1;
26 | }
27 |
28 | /* Divide two numbers */
29 | int divide(int a, int b, int *remainder) {
30 | int quot = a / b;
31 | *remainder = a % b;
32 | return quot;
33 | }
34 |
35 | /* Average values in an array */
36 | double avg(double *a, int n) {
37 | int i;
38 | double total = 0.0;
39 | for (i = 0; i < n; i++) {
40 | total += a[i];
41 | }
42 | return total / n;
43 | }
44 |
45 | /* Function involving a C data structure */
46 | double distance(Point *p1, Point *p2) {
47 | return hypot(p1->x - p2->x, p1->y - p2->y);
48 | }
49 |
50 | /* n:longth of array */
51 | void clip(double *a, int n, double min, double max, double *out) {
52 | double x;
53 | for (; n >= 0; n--, a++, out++) {
54 | x = *a;
55 |
56 | *out = x > max ? max : (x < min ? min : x);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/with_C_API/sample.h:
--------------------------------------------------------------------------------
1 | /* sample.h */
2 |
3 | #include
4 |
5 | extern int gcd(int, int);
6 | extern int in_mandel(double x0, double y0, int n);
7 | extern int divide(int a, int b, int *remainder);
8 | extern double avg(double *a, int n);
9 |
10 | typedef struct Point {
11 | double x,y;
12 | } Point;
13 |
14 | extern double distance(Point *p1, Point *p2);
15 |
--------------------------------------------------------------------------------
/with_C_API/setup.py:
--------------------------------------------------------------------------------
1 | # setup.py
2 | from distutils.core import setup, Extension
3 |
4 | setup(name='ptexample',
5 | ext_modules=[
6 | Extension('ptexample',
7 | ['ptexample.c'])]
8 | )
9 |
--------------------------------------------------------------------------------
/with_C_API/setup_sample.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup, Extension
2 |
3 | setup(name='sample',
4 | ext_modules=[
5 | Extension('sample',
6 | ['sample.c','pysample.c'])]
7 | )
8 |
--------------------------------------------------------------------------------
/with_Cython/csample.pxd:
--------------------------------------------------------------------------------
1 | # csample.pxd
2 | #
3 | # Declarations of "external" C functions and structures
4 |
5 | cdef extern from "sample.h":
6 | int gcd(int, int)
7 | bint in_mandel(double, double, int)
8 | int divide(int, int, int *)
9 | double avg(double *, int) nogil
10 |
11 | ctypedef struct Point:
12 | double x
13 | double y
14 |
15 | double distance(Point *, Point *)
--------------------------------------------------------------------------------
/with_Cython/sample.cpython-36m-x86_64-linux-gnu.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hellcatzm/Extend_Python/392742e05939c3741350203311c97eab74190846/with_Cython/sample.cpython-36m-x86_64-linux-gnu.so
--------------------------------------------------------------------------------
/with_Cython/sample.h:
--------------------------------------------------------------------------------
1 | #ifndef __SAMPLE_H__
2 | #define __SAMPLE_H__
3 | #include
4 |
5 | #ifdef __cplusplus
6 | extern "C" {
7 | #endif
8 |
9 | int gcd(int x, int y);
10 | int in_mandel(double x0, double y0, int n);
11 | int divide(int a, int b, int *remainder);
12 | double avg(double *a, int n);
13 |
14 | /* A C data structure */
15 | typedef struct Point {
16 | double x,y;
17 | } Point;
18 |
19 | double distance(Point *p1, Point *p2);
20 |
21 | #ifdef __cplusplus
22 | }
23 | #endif
24 | #endif
25 |
--------------------------------------------------------------------------------
/with_Cython/sample.pyx:
--------------------------------------------------------------------------------
1 | # sample.pyx
2 |
3 | # Import the low-level C declarations
4 | cimport csample
5 |
6 | # Import some functionality from Python and the C stdlib
7 | from cpython.pycapsule cimport *
8 |
9 | from libc.stdlib cimport malloc, free
10 |
11 | # Wrappers
12 | # def gcd(unsigned int x, unsigned int y):
13 | # return csample.gcd(x, y)
14 | def gcd(int x, int y):
15 | if x <= 0:
16 | raise ValueError("x must be > 0")
17 | if y <= 0:
18 | raise ValueError("y must be > 0")
19 | return csample.gcd(x,y)
20 |
21 | def in_mandel(x, y, unsigned int n):
22 | return csample.in_mandel(x, y, n)
23 |
24 | def divide(x, y):
25 | cdef int rem
26 | quot = csample.divide(x, y, &rem)
27 | return quot, rem
28 |
29 | def avg(double[:] a):
30 | cdef:
31 | int sz
32 | double result
33 |
34 | sz = a.size
35 | with nogil:
36 | result = csample.avg( &a[0], sz)
37 | return result
38 |
39 | # Destructor for cleaning up Point objects
40 | cdef del_Point(object obj):
41 | pt = PyCapsule_GetPointer(obj,"Point")
42 | free( pt)
43 |
44 | # Create a Point object and return as a capsule
45 | # def Point(double x,double y):
46 | # cdef csample.Point *p
47 | # p = malloc(sizeof(csample.Point))
48 | # if p == NULL:
49 | # raise MemoryError("No memory to make a Point")
50 | # p.x = x
51 | # p.y = y
52 | # return PyCapsule_New(p,"Point",del_Point)
53 |
54 | # def distance(p1, p2):
55 | # pt1 = PyCapsule_GetPointer(p1,"Point")
56 | # pt2 = PyCapsule_GetPointer(p2,"Point")
57 | # return csample.distance(pt1,pt2)
58 | cdef class Point:
59 | cdef csample.Point *_c_point
60 | def __cinit__(self, double x, double y):
61 | self._c_point = malloc(sizeof(csample.Point))
62 | self._c_point.x = x
63 | self._c_point.y = y
64 |
65 | def __dealloc__(self):
66 | free(self._c_point)
67 |
68 | property x:
69 | def __get__(self):
70 | return self._c_point.x
71 | def __set__(self, value):
72 | self._c_point.x = value
73 |
74 | property y:
75 | def __get__(self):
76 | return self._c_point.y
77 | def __set__(self, value):
78 | self._c_point.y = value
79 |
80 | def distance(Point p1, Point p2):
81 | return csample.distance(p1._c_point, p2._c_point)
--------------------------------------------------------------------------------
/with_Cython/setup.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup
2 | from distutils.extension import Extension
3 | from Cython.Distutils import build_ext
4 |
5 | ext_modules = [
6 | Extension('sample',
7 |
8 | ['sample.pyx'],
9 | libraries=['sample'],
10 | library_dirs=['.'])]
11 | setup(
12 | name = 'Sample extension module',
13 | cmdclass = {'build_ext': build_ext},
14 | ext_modules = ext_modules
15 | )
--------------------------------------------------------------------------------
/with_ctypes/README.md:
--------------------------------------------------------------------------------
1 | #### 资料原文
2 | [使用ctypes访问C代码](http://python3-cookbook.readthedocs.io/zh_CN/latest/c15/p01_access_ccode_using_ctypes.html)
3 | #### 介绍博文
4 | [『Python CoolBook』使用ctypes访问C代码_上_用法讲解](https://www.cnblogs.com/hellcat/p/9058330.html)
5 | [『Python CoolBook』使用ctypes访问C代码_下_demo进阶](https://www.cnblogs.com/hellcat/p/9078321.html)
6 |
--------------------------------------------------------------------------------
/with_ctypes/sample.c:
--------------------------------------------------------------------------------
1 | /* sample.c */
2 | #include "sample.h"
3 |
4 | /* Compute the greatest common divisor */
5 | int gcd(int x, int y) {
6 | int g = y;
7 | while (x > 0) {
8 | g = x;
9 | x = y % x;
10 | y = g;
11 | }
12 | return g;
13 | }
14 |
15 | /* Test if (x0,y0) is in the Mandelbrot set or not */
16 | int in_mandel(double x0, double y0, int n) {
17 | double x=0,y=0,xtemp;
18 | while (n > 0) {
19 | xtemp = x*x - y*y + x0;
20 | y = 2*x*y + y0;
21 | x = xtemp;
22 | n -= 1;
23 | if (x*x + y*y > 4) return 0;
24 | }
25 | return 1;
26 | }
27 |
28 | /* Divide two numbers */
29 | int divide(int a, int b, int *remainder) {
30 | int quot = a / b;
31 | *remainder = a % b;
32 | return quot;
33 | }
34 |
35 | /* Average values in an array */
36 | double avg(double *a, int n) {
37 | int i;
38 | double total = 0.0;
39 | for (i = 0; i < n; i++) {
40 | total += a[i];
41 | }
42 | return total / n;
43 | }
44 |
45 | /* Function involving a C data structure */
46 | double distance(Point *p1, Point *p2) {
47 | return hypot(p1->x - p2->x, p1->y - p2->y);
48 | }
49 |
--------------------------------------------------------------------------------
/with_ctypes/sample.h:
--------------------------------------------------------------------------------
1 | #ifndef __SAMPLE_H__
2 | #define __SAMPLE_H__
3 | #include
4 |
5 | #ifdef __cplusplus
6 | extern "C" {
7 | #endif
8 |
9 | int gcd(int x, int y);
10 | int in_mandel(double x0, double y0, int n);
11 | int divide(int a, int b, int *remainder);
12 | double avg(double *a, int n);
13 |
14 | /* A C data structure */
15 | typedef struct Point {
16 | double x,y;
17 | } Point;
18 |
19 | double distance(Point *p1, Point *p2);
20 |
21 | #ifdef __cplusplus
22 | }
23 | #endif
24 | #endif
25 |
--------------------------------------------------------------------------------
/with_ctypes/sample.py:
--------------------------------------------------------------------------------
1 | # sample.py
2 | import ctypes
3 | import os
4 |
5 | # Try to locate the .so file in the same directory as this file
6 | _file = 'libsample.so'
7 | _path = os.path.join(*(os.path.split(__file__)[:-1] + (_file,)))
8 | _mod = ctypes.cdll.LoadLibrary(_path)
9 |
10 | # int gcd(int, int)
11 | gcd = _mod.gcd
12 | gcd.argtypes = (ctypes.c_int, ctypes.c_int)
13 | gcd.restype = ctypes.c_int
14 |
15 | # int in_mandel(double, double, int)
16 | in_mandel = _mod.in_mandel
17 | in_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int)
18 | in_mandel.restype = ctypes.c_int
19 |
20 | # int divide(int, int, int *)
21 | _divide = _mod.divide
22 | _divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
23 | _divide.restype = ctypes.c_int
24 |
25 | def divide(x, y):
26 | rem = ctypes.c_int()
27 | quot = _divide(x, y, rem)
28 |
29 | return quot,rem.value
30 |
31 | # void avg(double *, int n)
32 | # Define a special type for the 'double *' argument
33 | class DoubleArrayType:
34 | def from_param(self, param):
35 | typename = type(param).__name__
36 | if hasattr(self, 'from_' + typename):
37 | return getattr(self, 'from_' + typename)(param)
38 | elif isinstance(param, ctypes.Array):
39 | return param
40 | else:
41 | raise TypeError("Can't convert %s" % typename)
42 |
43 | # Cast from array.array objects
44 | def from_array(self, param):
45 | if param.typecode != 'd':
46 | raise TypeError('must be an array of doubles')
47 | ptr, _ = param.buffer_info()
48 | return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double))
49 |
50 | # Cast from lists/tuples
51 | def from_list(self, param):
52 | val = ((ctypes.c_double)*len(param))(*param)
53 | return val
54 |
55 | from_tuple = from_list
56 |
57 | # Cast from a numpy array
58 | def from_ndarray(self, param):
59 | return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
60 |
61 | DoubleArray = DoubleArrayType()
62 | _avg = _mod.avg
63 | _avg.argtypes = (DoubleArray, ctypes.c_int)
64 | _avg.restype = ctypes.c_double
65 |
66 | def avg(values):
67 | return _avg(values, len(values))
68 |
69 | # struct Point { }
70 | class Point(ctypes.Structure):
71 | _fields_ = [('x', ctypes.c_double),
72 | ('y', ctypes.c_double)]
73 |
74 | # double distance(Point *, Point *)
75 | distance = _mod.distance
76 | distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))
77 | distance.restype = ctypes.c_double
78 |
--------------------------------------------------------------------------------