• 『Python CoolBook』C扩展库_其五_C语言层面Python库之间调用API


    点击进入项目

    一、C层面模块添加API

    我们仍然操作如下结构体,

    #include <math.h>
    
    typedef struct Point {
        double x,y;
    } Point;
    

    本节目标是封装两个Point结构体的操作函数为sample库的C级API,可以被sample以外的C库调用,首先写出以下函数指针结构体实例,

    /* 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"。

    /* 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;
    }

    测试如下,

    不过由于Python并不能解析Capsule对象,所以这个API实际上是留给其他C源代码调用的。

    我们希望在调用这个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 必须与该字符串完全匹配。

    此时我们就已经封装好了pysample.c中的两个函数为PyPoint_AsPoint和PyPoint_FromPoint,可以接受任何导入了pysample.h的文件使用。

    小结

    • 将函数指针封装到结构体中
    • 将结构体生成为Capsule,并将其作为属性绑定给模块
    • 使用PyCapsule_Import根据模块名称检索到Capsule,由于该函数会直接返回C指针,直接使用一个空的结构体接受Capsule即可

    二、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
    ", 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文件中的指针,然后接收调用。

    测试如下,

     

  • 相关阅读:
    Power Strings P5019
    Floyd模板题 P1704
    【训练题】强连通分量缩点 P1679
    字符串hash模板题 P5018
    Dijkstra模板题 P1710
    【训练题】分队 P1672
    二分图模板题 P1631
    【训练题】无序字母对 P1675
    KMP模板题 P1537
    马路 树链剖分/线段树/最近公共祖先(LCA)
  • 原文地址:https://www.cnblogs.com/hellcat/p/9089723.html
Copyright © 2020-2023  润新知