點擊進入項目git
咱們仍然操做以下結構體,github
#include <math.h> typedef struct Point { double x,y; } Point;
本節目標是封裝兩個Point結構體的操做函數爲sample庫的C級API,能夠被sample之外的C庫調用,首先寫出如下函數指針結構體實例,api
/* pysample.c */ static PyObject *PyPoint_FromPoint(Point *p, int must_free) { /* 膠囊和C指針相似。在內部,它們獲取一個通用指針和一個名稱,可使用 PyCapsule_New() 函數很容易的被建立。 另外,一個可選的析構函數能被 綁定到膠囊上,用來在膠囊對象被垃圾回收時釋放底層的內存*/ return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); } /* Utility functions */ static Point *PyPoint_AsPoint(PyObject *obj) { return (Point *) PyCapsule_GetPointer(obj, "Point"); } static _PointAPIMethods _point_api = { PyPoint_AsPoint, PyPoint_FromPoint };
結構體定義以下,位於一個新的頭函數中,函數
/* pysample.h */ /* Public API Table */ /* 這裏最重要的部分是函數指針表 _PointAPIMethods. 它會在導出模塊時被初始化,而後導入模塊時被查找到。 */ typedef struct { Point *(*aspoint)(PyObject *); PyObject *(*frompoint)(Point *, int); } _PointAPIMethods;
修改初始化函數,將函數指針結構體註冊爲Capsule,並將之使用PyModule_AddObject,添加給模塊對象,做爲模塊屬性,測試
PyModule_AddObject(PyObject *module, const char *name, PyObject *value),其中module就是Py_InitModule()返回的對象,含義就是將py_point_api這個類加入m這個模塊中,並簡記爲"_point_api"。ui
/* pysample.c */ /* Module initialization function */ PyMODINIT_FUNC PyInit_sample(void) { PyObject *m; PyObject *py_point_api; m = PyModule_Create(&samplemodule); if (m == NULL) return NULL; /* Add the Point C API functions */ py_point_api = PyCapsule_New((void *) &_point_api, "sample._point_api", NULL); //<---pysample.h:23,name爲全名 if (py_point_api) { PyModule_AddObject(m, "_point_api", py_point_api); //name略去模塊名 } return m; }
測試以下,spa
不過因爲Python並不能解析Capsule對象,因此這個API其實是留給其餘C源代碼調用的。3d
咱們但願在調用這個Capsule對象時,並不直接導入這個C源文件,只是使用頭文件,因此咱們在pysample.h中再進行一次封裝,指針
/* pysample.h */ /* Method table in external module */ static _PointAPIMethods *_point_api = 0; /* Import the API table from sample, import_sample() 被用來指向膠囊導入並初始化這個指針 */ static int import_sample(void) { //<---ptexample.c:46 // 需提供屬性名(好比sample._point_api),會一次性找到膠囊對象並提取出指針來。 _point_api = (_PointAPIMethods *) PyCapsule_Import("sample._point_api",0); //<---pysample.c:171 return (_point_api != NULL) ? 1 : 0; } /* Macros to implement the programming interface */ #define PyPoint_AsPoint(obj) (_point_api->aspoint)(obj) #define PyPoint_FromPoint(obj) (_point_api->frompoint)(obj)
PyCapsule_Import:從模塊中的capsule屬性導入指向C對象的指針。 name 參數應指定屬性的全名,如 module.attribute
中所示。存儲在膠囊中的 name 必須與該字符串徹底匹配。code
此時咱們就已經封裝好了pysample.c中的兩個函數爲PyPoint_AsPoint和PyPoint_FromPoint,能夠接受任何導入了pysample.h的文件使用。
/* ptexample.c */ /* Include the header associated with the other module */ #include "pysample.h" /* An extension function that uses the exported API */ static PyObject *print_point(PyObject *self, PyObject *args) { PyObject *obj; Point *p; if (!PyArg_ParseTuple(args,"O", &obj)) { return NULL; } /* Note: This is defined in a different module */ p = PyPoint_AsPoint(obj); if (!p) { return NULL; } printf("%f %f\n", p->x, p->y); return Py_BuildValue(""); } static PyMethodDef PtExampleMethods[] = { {"print_point", print_point, METH_VARARGS, "output a point"}, { NULL, NULL, 0, NULL} }; static struct PyModuleDef ptexamplemodule = { PyModuleDef_HEAD_INIT, "ptexample", /* name of module */ "A module that imports an API", /* Doc string (may be NULL) */ -1, /* Size of per-interpreter state or -1 */ PtExampleMethods /* Method table */ }; /* Module initialization function */ PyMODINIT_FUNC PyInit_ptexample(void) { PyObject *m; m = PyModule_Create(&ptexamplemodule); if (m == NULL) return NULL; /* Import sample, loading its API functions */ if (!import_sample()) { //<---pysample.h:21 return NULL; } return m; }
這裏面先初始化前面.h文件中的指針,而後接收調用。
測試以下,