『Python CoolBook』C擴展庫_其五_C語言層面Python庫之間調用API

點擊進入項目git

1、C層面模塊添加API

咱們仍然操做以下結構體,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的文件使用。

小結

  • 將函數指針封裝到結構體中
  • 將結構體生成爲Capsule,並將其做爲屬性綁定給模塊
  • 使用PyCapsule_Import根據模塊名稱檢索到Capsule,因爲該函數會直接返回C指針,直接使用一個空的結構體接受Capsule便可

2、C層面模塊調用API

/* 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文件中的指針,而後接收調用。

測試以下,

 

相關文章
相關標籤/搜索