|
5 | 5 | ----------
|
6 | 6 | 问题
|
7 | 7 | ----------
|
8 |
| -You have an extension module that needs to handle a pointer to a C data structure, but |
9 |
| -you don’t want to expose any internal details of the structure to Python. |
| 8 | +你有一个扩展模块需要处理C结构体中的指针, |
| 9 | +但是你又不想暴露结构体中任何内部细节给Python。 |
10 | 10 |
|
11 | 11 | |
|
12 | 12 |
|
13 | 13 | ----------
|
14 | 14 | 解决方案
|
15 | 15 | ----------
|
16 |
| -Opaque data structures are easily handled by wrapping them inside capsule objects. |
17 |
| -Consider this fragment of C code from our sample code: |
18 |
| - |
19 |
| -typedef struct Point { |
20 |
| - double x,y; |
21 |
| -} Point; |
22 |
| - |
23 |
| -extern double distance(Point *p1, Point *p2); |
24 |
| -
|
25 |
| -Here is an example of extension code that wraps the Point structure and distance() |
26 |
| -function using capsules: |
27 |
| - |
28 |
| -/* Destructor function for points */ |
29 |
| -static void del_Point(PyObject *obj) { |
30 |
| - free(PyCapsule_GetPointer(obj,"Point")); |
31 |
| -} |
32 |
| -
|
33 |
| -/* Utility functions */ |
34 |
| -static Point *PyPoint_AsPoint(PyObject *obj) { |
35 |
| - return (Point *) PyCapsule_GetPointer(obj, "Point"); |
36 |
| -} |
37 |
| -
|
38 |
| -static PyObject *PyPoint_FromPoint(Point *p, int must_free) { |
39 |
| - return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); |
40 |
| -} |
41 |
| -
|
42 |
| -/* Create a new Point object */ |
43 |
| -static PyObject *py_Point(PyObject *self, PyObject *args) { |
44 |
| -
|
45 |
| - Point *p; |
46 |
| - double x,y; |
47 |
| - if (!PyArg_ParseTuple(args,"dd",&x,&y)) { |
48 |
| - return NULL; |
49 |
| - } |
50 |
| - p = (Point *) malloc(sizeof(Point)); |
51 |
| - p->x = x; |
52 |
| - p->y = y; |
53 |
| - return PyPoint_FromPoint(p, 1); |
54 |
| -} |
55 |
| -
|
56 |
| -static PyObject *py_distance(PyObject *self, PyObject *args) { |
57 |
| - Point *p1, *p2; |
58 |
| - PyObject *py_p1, *py_p2; |
59 |
| - double result; |
60 |
| -
|
61 |
| - if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) { |
62 |
| - return NULL; |
63 |
| - } |
64 |
| - if (!(p1 = PyPoint_AsPoint(py_p1))) { |
65 |
| - return NULL; |
66 |
| - } |
67 |
| - if (!(p2 = PyPoint_AsPoint(py_p2))) { |
68 |
| - return NULL; |
69 |
| - } |
70 |
| - result = distance(p1,p2); |
71 |
| - return Py_BuildValue("d", result); |
72 |
| -} |
73 |
| - |
74 |
| -Using these functions from Python looks like this: |
75 |
| - |
76 |
| ->>> import sample |
77 |
| ->>> p1 = sample.Point(2,3) |
78 |
| ->>> p2 = sample.Point(4,5) |
79 |
| ->>> p1 |
80 |
| -<capsule object "Point" at 0x1004ea330> |
81 |
| ->>> p2 |
82 |
| -<capsule object "Point" at 0x1005d1db0> |
83 |
| ->>> sample.distance(p1,p2) |
84 |
| -2.8284271247461903 |
85 |
| ->>> |
| 16 | +隐形结构体可以很容易的通过将它们包装在胶囊对象中来处理。 |
| 17 | +考虑我们例子代码中的下列C代码片段: |
| 18 | + |
| 19 | +:: |
| 20 | + |
| 21 | + typedef struct Point { |
| 22 | + double x,y; |
| 23 | + } Point; |
| 24 | + |
| 25 | + extern double distance(Point *p1, Point *p2); |
| 26 | + |
| 27 | +下面是一个使用胶囊包装Point结构体和 ``distance()`` 函数的扩展代码实例: |
| 28 | + |
| 29 | +:: |
| 30 | + |
| 31 | + /* Destructor function for points */ |
| 32 | + static void del_Point(PyObject *obj) { |
| 33 | + free(PyCapsule_GetPointer(obj,"Point")); |
| 34 | + } |
| 35 | + |
| 36 | + /* Utility functions */ |
| 37 | + static Point *PyPoint_AsPoint(PyObject *obj) { |
| 38 | + return (Point *) PyCapsule_GetPointer(obj, "Point"); |
| 39 | + } |
| 40 | + |
| 41 | + static PyObject *PyPoint_FromPoint(Point *p, int must_free) { |
| 42 | + return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); |
| 43 | + } |
| 44 | + |
| 45 | + /* Create a new Point object */ |
| 46 | + static PyObject *py_Point(PyObject *self, PyObject *args) { |
| 47 | + |
| 48 | + Point *p; |
| 49 | + double x,y; |
| 50 | + if (!PyArg_ParseTuple(args,"dd",&x,&y)) { |
| 51 | + return NULL; |
| 52 | + } |
| 53 | + p = (Point *) malloc(sizeof(Point)); |
| 54 | + p->x = x; |
| 55 | + p->y = y; |
| 56 | + return PyPoint_FromPoint(p, 1); |
| 57 | + } |
| 58 | + |
| 59 | + static PyObject *py_distance(PyObject *self, PyObject *args) { |
| 60 | + Point *p1, *p2; |
| 61 | + PyObject *py_p1, *py_p2; |
| 62 | + double result; |
| 63 | + |
| 64 | + if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) { |
| 65 | + return NULL; |
| 66 | + } |
| 67 | + if (!(p1 = PyPoint_AsPoint(py_p1))) { |
| 68 | + return NULL; |
| 69 | + } |
| 70 | + if (!(p2 = PyPoint_AsPoint(py_p2))) { |
| 71 | + return NULL; |
| 72 | + } |
| 73 | + result = distance(p1,p2); |
| 74 | + return Py_BuildValue("d", result); |
| 75 | + } |
| 76 | + |
| 77 | +在Python中可以像下面这样来使用这些函数: |
| 78 | + |
| 79 | +:: |
| 80 | + |
| 81 | + >>> import sample |
| 82 | + >>> p1 = sample.Point(2,3) |
| 83 | + >>> p2 = sample.Point(4,5) |
| 84 | + >>> p1 |
| 85 | + <capsule object "Point" at 0x1004ea330> |
| 86 | + >>> p2 |
| 87 | + <capsule object "Point" at 0x1005d1db0> |
| 88 | + >>> sample.distance(p1,p2) |
| 89 | + 2.8284271247461903 |
| 90 | + >>> |
86 | 91 |
|
87 | 92 | |
|
88 | 93 |
|
89 | 94 | ----------
|
90 | 95 | 讨论
|
91 | 96 | ----------
|
92 |
| -Capsules are similar to a typed C pointer. Internally, they hold a generic pointer along |
93 |
| -with an identifying name and can be easily created using the PyCapsule_New() function. |
94 |
| -In addition, an optional destructor function can be attached to a capsule to release the |
95 |
| -underlying memory when the capsule object is garbage collected. |
96 |
| - |
97 |
| -To extract the pointer contained inside a capsule, use the PyCapsule_GetPointer() |
98 |
| -function and specify the name. If the supplied name doesn’t match that of the capsule |
99 |
| -or some other error occurs, an exception is raised and NULL is returned. |
100 |
| -In this recipe, a pair of utility functions—PyPoint_FromPoint() and PyPoint_As |
101 |
| -Point()—have been written to deal with the mechanics of creating and unwinding |
102 |
| -Point instances from capsule objects. In any extension functions, we’ll use these func‐ |
103 |
| -tions instead of working with capsules directly. This design choice makes it easier to |
104 |
| -deal with possible changes to the wrapping of Point objects in the future. For example, |
105 |
| -if you decided to use something other than a capsule later, you would only have to change |
106 |
| -these two functions. |
107 |
| -One tricky part about capsules concerns garbage collection and memory management. |
108 |
| -The PyPoint_FromPoint() function accepts a must_free argument that indicates |
109 |
| -whether the underlying Point * structure is to be collected when the capsule is de‐ |
110 |
| -stroyed. When working with certain kinds of C code, ownership issues can be difficult |
111 |
| -to handle (e.g., perhaps a Point structure is embedded within a larger data structure |
112 |
| -that is managed separately). Rather than making a unilateral decision to garbage collect, |
113 |
| -this extra argument gives control back to the programmer. It should be noted that the |
114 |
| -destructor associated with an existing capsule can also be changed using the PyCap |
115 |
| -sule_SetDestructor() function. |
116 |
| -Capsules are a sensible solution to interfacing with certain kinds of C code involving |
117 |
| -structures. For instance, sometimes you just don’t care about exposing the internals of |
118 |
| -a structure or turning it into a full-fledged extension type. With a capsule, you can put |
119 |
| -a lightweight wrapper around it and easily pass it around to other extension functions. |
| 97 | +胶囊和C指针类似。在内部,它们获取一个通用指针和一个名称,可以使用 ``PyCapsule_New()`` 函数很容易的被创建。 |
| 98 | +另外,一个可选的析构函数能被绑定到胶囊上,用来在胶囊对象被垃圾回收时释放底层的内存。 |
| 99 | + |
| 100 | +要提取胶囊中的指针,可使用 ``PyCapsule_GetPointer()`` 函数并指定名称。 |
| 101 | +如果提供的名称和胶囊不匹配或其他错误出现,那么就会抛出异常并返回NULL。 |
| 102 | + |
| 103 | +本节中,一对工具函数—— ``PyPoint_FromPoint()`` 和 ``PyPoint_AsPoint()`` |
| 104 | +被用来创建和从胶囊对象中提取Point实例。 |
| 105 | +在任何扩展函数中,我们会使用这些函数而不是直接使用胶囊对象。 |
| 106 | +这种设计使得我们可以很容易的应对将来对Point底下的包装的更改。 |
| 107 | +例如,如果你决定使用另外一个胶囊了,那么只需要更改这两个函数即可。 |
| 108 | + |
| 109 | +对于胶囊对象一个难点在于垃圾回收和内存管理。 |
| 110 | +``PyPoint_FromPoint()`` 函数接受一个 ``must_free`` 参数, |
| 111 | +用来指定当胶囊被销毁时底层Point * 结构体是否应该被回收。 |
| 112 | +在某些C代码中,归属问题通常很难被处理(比如一个Point结构体被嵌入到一个被单独管理的大结构体中)。 |
| 113 | +程序员可以使用 ``extra`` 参数来控制,而不是单方面的决定垃圾回收。 |
| 114 | +要注意的是和现有胶囊有关的析构器能使用 ``PyCapsule_SetDestructor()`` 函数来更改。 |
| 115 | + |
| 116 | +对于涉及到结构体的C代码而言,使用胶囊是一个比较合理的解决方案。 |
| 117 | +例如,有时候你并不关心暴露结构体的内部信息或者将其转换成一个完整的扩展类型。 |
| 118 | +通过使用胶囊,你可以在它上面放一个轻量级的包装器,然后将它传给其他的扩展函数。 |
| 119 | + |
0 commit comments