• 『Python CoolBook』C扩展库_其四_结构体操作与Capsule


    点击进入项目

    一、Python生成C语言结构体

    C语言中的结构体传给Python时会被封装为胶囊(Capsule),

    我们想要一个如下结构体进行运算,则需要Python传入x、y两个浮点数,

    typedef struct Point {
        double x,y;
    } Point;
    

    然后对这两个浮点数解析后生成C中Point的结构体,如下,

    /* Create a new Point object */
    static PyObject *py_Point(PyObject *self, PyObject *args) {
    
      Point *p;
      double x,y;
      if (!PyArg_ParseTuple(args,"dd",&x,&y)) {
        return NULL;
      }
      p = (Point *) malloc(sizeof(Point));
      p->x = x;
      p->y = y;
      return PyPoint_FromPoint(p, 1);
    }
    

     上面最后一句将使用C中的结构体构建Python胶囊对象并返回给Python,

    /* Destructor function for points */
    static void del_Point(PyObject *obj) {
      free(PyCapsule_GetPointer(obj,"Point"));
    }
    
    static PyObject *PyPoint_FromPoint(Point *p, int must_free) {
      /* 胶囊和C指针类似。在内部,它们获取一个通用指针和一个名称,可以使用 
      PyCapsule_New() 函数很容易的被创建。 另外,一个可选的析构函数能被
    绑定到胶囊上,用来在胶囊对象被垃圾回收时释放底层的内存*/
      return PyCapsule_New(p, "Point", must_free ? del_Point : NULL);
    }
    

    PyCapsule_New():从结构体创建胶囊

    PyCapsule_GetPointer():提取胶囊中的指针,使用 PyCapsule_GetPointer() 函数并指定名称。 如果提供的名称和胶囊不匹配或其他错误出现,那么就会抛出异常并返回NULL。实际上就是从胶囊转换回结构体

    效果如下,

    >>> import sample
    >>> p1 = sample.Point(2,3)
    >>> p2 = sample.Point(4,5)
    >>> p1
    <capsule object "Point" at 0x1004ea330>
    >>> p2
    <capsule object "Point" at 0x1005d1db0>

    总结

    本节中,一对工具函数—— PyPoint_FromPoint()PyPoint_AsPoint() 被用来创建和从胶囊对象中提取Point实例。 在任何扩展函数中,我们会使用这些函数而不是直接使用胶囊对象。 这种设计使得我们可以很容易的应对将来对Point底下的包装的更改。 例如,如果你决定使用另外一个胶囊了,那么只需要更改这两个函数即可。

    对于胶囊对象一个难点在于垃圾回收和内存管理。 PyPoint_FromPoint() 函数接受一个 must_free 参数, 用来指定当胶囊被销毁时底层Point * 结构体是否应该被回收。 在某些C代码中,归属问题通常很难被处理(比如一个Point结构体被嵌入到一个被单独管理的大结构体中)。 程序员可以使用 extra 参数来控制,而不是单方面的决定垃圾回收。 要注意的是和现有胶囊有关的析构器能使用 PyCapsule_SetDestructor() 函数来更改。

    二、Python中的C结构体传入C语言进行运算

    /* Utility functions */
    static Point *PyPoint_AsPoint(PyObject *obj) {
      return (Point *) PyCapsule_GetPointer(obj, "Point");
    }
    
    static PyObject *py_distance(PyObject *self, PyObject *args) {
      Point *p1, *p2;
      PyObject *py_p1, *py_p2;
      double result;
    
      if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) {
        return NULL;
      }
      if (!(p1 = PyPoint_AsPoint(py_p1))) {
        return NULL;
      }
      if (!(p2 = PyPoint_AsPoint(py_p2))) {
        return NULL;
      }
      result = distance(p1,p2);
      return Py_BuildValue("d", result);
    }
    

     将两个结构体转为C指针存储后,分别使用PyCapsule_GetPointer()提取指针信息转换为结构体,计算后返回。

    >>> sample.distance(p1,p2)
    2.8284271247461903
  • 相关阅读:
    mybatis-Generator 代码自动生成报错 Result Maps collection already contains value for BaseResultMap
    Linux 常用的命令
    Linux 查找文件
    eclipse tasks
    Myeclipse 文件注释和解注释
    proxool连接池 异常
    EL 表达式 函数 操作 字符串
    Myeclipse 选中高亮
    Spring 定时作业
    linux配置java环境变量(详细)
  • 原文地址:https://www.cnblogs.com/hellcat/p/9088824.html
Copyright © 2020-2023  润新知