开发者社区> 问答> 正文

用SWIG包装C代码

你想让你写的C代码作为一个C扩展模块来访问,想通过使用 Swig包装生成器 来完成。

展开
收起
哦哦喔 2020-04-17 18:13:30 2293 0
1 条回答
写回答
取消 提交回答
  • Swig通过解析C头文件并自动创建扩展代码来操作。 要使用它,你先要有一个C头文件。例如,我们示例的头文件如下:
    
    /* sample.h */
    
    #include <math.h>
    extern int gcd(int, int);
    extern int in_mandel(double x0, double y0, int n);
    extern int divide(int a, int b, int *remainder);
    extern double avg(double *a, int n);
    
    typedef struct Point {
        double x,y;
    } Point;
    
    extern double distance(Point *p1, Point *p2);
    一旦你有了这个头文件,下一步就是编写一个Swig”接口”文件。 按照约定,这些文件以”.i”后缀并且类似下面这样:
    
    // sample.i - Swig interface
    %module sample
    %{
    #include "sample.h"
    %}
    
    /* Customizations */
    %extend Point {
        /* Constructor for Point objects */
        Point(double x, double y) {
            Point *p = (Point *) malloc(sizeof(Point));
            p->x = x;
            p->y = y;
            return p;
       };
    };
    
    /* Map int *remainder as an output argument */
    %include typemaps.i
    %apply int *OUTPUT { int * remainder };
    
    /* Map the argument pattern (double *a, int n) to arrays */
    %typemap(in) (double *a, int n)(Py_buffer view) {
      view.obj = NULL;
      if (PyObject_GetBuffer($input, &view, PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) {
        SWIG_fail;
      }
      if (strcmp(view.format,"d") != 0) {
        PyErr_SetString(PyExc_TypeError, "Expected an array of doubles");
        SWIG_fail;
      }
      $1 = (double *) view.buf;
      $2 = view.len / sizeof(double);
    }
    
    %typemap(freearg) (double *a, int n) {
      if (view$argnum.obj) {
        PyBuffer_Release(&view$argnum);
      }
    }
    
    /* C declarations to be included in the extension module */
    
    extern int gcd(int, int);
    extern int in_mandel(double x0, double y0, int n);
    extern int divide(int a, int b, int *remainder);
    extern double avg(double *a, int n);
    
    typedef struct Point {
        double x,y;
    } Point;
    
    extern double distance(Point *p1, Point *p2);
    一旦你写好了接口文件,就可以在命令行工具中调用Swig了:
    
    bash % swig -python -py3 sample.i
    bash %
    swig的输出就是两个文件,sample_wrap.c和sample.py。 后面的文件就是用户需要导入的。 而sample_wrap.c文件是需要被编译到名叫 _sample 的支持模块的C代码。 这个可以通过跟普通扩展模块一样的技术来完成。 例如,你创建了一个如下所示的 setup.py 文件:
    
    # setup.py
    from distutils.core import setup, Extension
    
    setup(name='sample',
          py_modules=['sample.py'],
          ext_modules=[
            Extension('_sample',
                      ['sample_wrap.c'],
                      include_dirs = [],
                      define_macros = [],
    
                      undef_macros = [],
                      library_dirs = [],
                      libraries = ['sample']
                      )
            ]
    )
    要编译和测试,在setup.py上执行python3,如下:
    
    bash % python3 setup.py build_ext --inplace
    running build_ext
    building '_sample' extension
    gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
    -I/usr/local/include/python3.3m -c sample_wrap.c
     -o build/temp.macosx-10.6-x86_64-3.3/sample_wrap.o
    sample_wrap.c: In function ‘SWIG_InitializeModule’:
    sample_wrap.c:3589: warning: statement with no effect
    gcc -bundle -undefined dynamic_lookup build/temp.macosx-10.6-x86_64-3.3/sample.o
     build/temp.macosx-10.6-x86_64-3.3/sample_wrap.o -o _sample.so -lsample
    bash %
    如果一切正常的话,你会发现你就可以很方便的使用生成的C扩展模块了。例如:
    
    >>> import sample
    >>> sample.gcd(42,8)
    2
    >>> sample.divide(42,8)
    [5, 2]
    >>> p1 = sample.Point(2,3)
    >>> p2 = sample.Point(4,5)
    >>> sample.distance(p1,p2)
    2.8284271247461903
    >>> p1.x
    2.0
    >>> p1.y
    3.0
    >>> import array
    >>> a = array.array('d',[1,2,3])
    >>> sample.avg(a)
    2.0
    >>>
    
    2020-04-17 18:13:39
    赞同 展开评论 打赏
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载