开发者社区> 问答> 正文

从扩展模块中定义和导出C的API

你有一个C扩展模块,在内部定义了很多有用的函数,你想将它们导出为一个公共的C API供其他地方使用。 你想在其他扩展模块中使用这些函数,但是不知道怎样将它们链接起来, 并且通过C编译器/链接器来做看上去特别复杂(或者不可能做到)。

展开
收起
哦哦喔 2020-04-17 18:11:59 2491 0
2 条回答
写回答
取消 提交回答
  • 有点尴尬唉 你要寻找的东西已经被吃掉啦!

    /* Destructor function for points */ static void del_Point(PyObject *obj) {

    free(PyCapsule_GetPointer(obj,"Point")); }

    /* Utility functions */ static Point *PyPoint_AsPoint(PyObject *obj) { return (Point *) PyCapsule_GetPointer(obj, "Point"); }

    static PyObject *PyPoint_FromPoint(Point *p, int must_free) { return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); }

    2020-04-17 23:58:41
    赞同 展开评论 打赏
  • 本节主要问题是如何处理15.4小节中提到的Point对象。仔细回一下,在C代码中包含了如下这些工具函数:
    
    /* Destructor function for points */
    static void del_Point(PyObject *obj) {
    
      free(PyCapsule_GetPointer(obj,"Point"));
    }
    
    /* Utility functions */
    static Point *PyPoint_AsPoint(PyObject *obj) {
      return (Point *) PyCapsule_GetPointer(obj, "Point");
    }
    
    static PyObject *PyPoint_FromPoint(Point *p, int must_free) {
      return PyCapsule_New(p, "Point", must_free ? del_Point : NULL);
    }
    现在的问题是怎样将 PyPoint_AsPoint() 和 Point_FromPoint() 函数作为API导出, 这样其他扩展模块能使用并链接它们,比如如果你有其他扩展也想使用包装的Point对象。
    
    要解决这个问题,首先要为 sample 扩展写个新的头文件名叫 pysample.h ,如下:
    
    /* pysample.h */
    #include "Python.h"
    #include "sample.h"
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /* Public API Table */
    typedef struct {
      Point *(*aspoint)(PyObject *);
      PyObject *(*frompoint)(Point *, int);
    } _PointAPIMethods;
    
    #ifndef PYSAMPLE_MODULE
    /* Method table in external module */
    static _PointAPIMethods *_point_api = 0;
    
    /* Import the API table from sample */
    static int import_sample(void) {
      _point_api = (_PointAPIMethods *) PyCapsule_Import("sample._point_api",0);
      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)
    #endif
    
    #ifdef __cplusplus
    }
    #endif
    这里最重要的部分是函数指针表 _PointAPIMethods . 它会在导出模块时被初始化,然后导入模块时被查找到。 修改原始的扩展模块来填充表格并将它像下面这样导出:
    
    /* pysample.c */
    
    #include "Python.h"
    #define PYSAMPLE_MODULE
    #include "pysample.h"
    
    ...
    /* Destructor function for points */
    static void del_Point(PyObject *obj) {
      printf("Deleting point\n");
      free(PyCapsule_GetPointer(obj,"Point"));
    }
    
    /* Utility functions */
    static Point *PyPoint_AsPoint(PyObject *obj) {
      return (Point *) PyCapsule_GetPointer(obj, "Point");
    }
    
    static PyObject *PyPoint_FromPoint(Point *p, int free) {
      return PyCapsule_New(p, "Point", free ? del_Point : NULL);
    }
    
    static _PointAPIMethods _point_api = {
      PyPoint_AsPoint,
      PyPoint_FromPoint
    };
    ...
    
    /* 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);
      if (py_point_api) {
        PyModule_AddObject(m, "_point_api", py_point_api);
      }
      return m;
    }
    最后,下面是一个新的扩展模块例子,用来加载并使用这些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()) {
        return NULL;
      }
    
      return m;
    }
    编译这个新模块时,你甚至不需要去考虑怎样将函数库或代码跟其他模块链接起来。 例如,你可以像下面这样创建一个简单的 setup.py 文件:
    
    # setup.py
    from distutils.core import setup, Extension
    
    setup(name='ptexample',
          ext_modules=[
            Extension('ptexample',
                      ['ptexample.c'],
                      include_dirs = [],  # May need pysample.h directory
                      )
            ]
    )
    如果一切正常,你会发现你的新扩展函数能和定义在其他模块中的C API函数一起运行的很好。
    
    >>> import sample
    >>> p1 = sample.Point(2,3)
    >>> p1
    <capsule object "Point *" at 0x1004ea330>
    >>> import ptexample
    >>> ptexample.print_point(p1)
    2.000000 3.000000
    >>>
    
    2020-04-17 18:20:13
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
Spring Boot2.0实战Redis分布式缓存 立即下载
CUDA MATH API 立即下载
API PLAYBOOK 立即下载